C++学习(计蒜客)

2016-08-22  本文已影响303人  KevinCool

学习C++

1. 斐波纳切数列

斐波那契数列是一种非常有意思的数列,由 00 和 11 开始,之后的斐波那契系数就由之前的两数相加。用数学公式定义斐波那契数列则可以看成如下形式:
​Fn表示斐波那契数列的第n项,你能知道斐波那契数列中的任何一项吗?

#include <iostream>
using namespace std;
int Fibonacci(int pos);
int main(){
    int pos;
    cin >> pos;
    int value;
    value = Fibonacci(pos);
    cout << value << endl;
    return 0;
}
int Fibonacci(int pos){
    if (pos == 0){
        return 0;
    }else if (pos == 1){
        return 1;
    }
    else{
        return Fibonacci(pos - 1) + Fibonacci(pos - 2);
    }
}```

###2. 矩阵翻转
晓萌最近在做一个翻转图片的应用,你可能也知道,图片其实是由一个个的点组成的。于是,晓萌想先做一个可以翻转矩阵的程序,来解决他问题的核心部分。
输入第一行包括由空格分开的整数M、N、T(0 < M < 200,0 < N < 200,T=0或1),其中M和N分别表示待处理矩阵的行数与列数,T为0时表示左右翻转,为1时表示上下翻转。
之后的M行,每行包括由空格分隔的N个整数,依次为输入矩阵的每一行的数据。
输出包括M行N列,每个数字之间用一个空格分隔,每一行行末均有一个空格,表示的是按照要求翻转后的矩阵。
 

include <iostream>

using namespace std;
int main(){
int row,col,lrud;
cin >> row >> col >> lrud;
int arr[row][col];
//i不能到5
for (int i=0; i<row; i++){
for(int j=0; j<col; j++){
cin >> arr[i][j];
}
}
//左右翻转
if (lrud == 0){
for (int i1=0; i1<row; i1++){
for (int j1=col-1; j1>=0; j1--){
cout << arr[i1][j1] << " ";
}
cout << endl;
}
}else if(lrud == 1){ //上下翻转
for (int i1=row-1; i1>=0; i1--){
for (int j1=0; j1<col; j1++){
cout << arr[i1][j1] << " ";
}
cout << endl;
}
}
}

###3. 位操作

include<iostream>

using namespace std;
int main(){
int a;
cin>>a;
//hex指输出16进制数,oct是八进制数,dec是十进制,bin二进制
cout << hex << a << " " << ~a << endl;
return 0;
}```

4. cstdio库

#include <cstdio>引入C风格的输入和输出,scanf和printf比cout和cin要高效。

5. 指针

一个指针是一个地址,是一个常量,而一个指针变量却可以被赋予不同的指针值,是变量。定义指针的目的是通过指针去访问内存单元。
指针变量的赋值只能被赋予地址数据,错误赋值将无法通过编译或者在运行中出错。变量的地址是系统分配,使用&进行取地址,*是指针运算符,间接访问运算符,通过指针变量存储的地址来访问变量。

#include<iostream>
using namespace std;
int main(){
    int a = 0, b = 0, *p, *p1, *p2;
    cin >> a >> b;
    p1 = &a;
    p2 = &b;
    if (a<b){
        p = p1;
        p1 = p2;
        p2 = p;
    }
    cout << "a=" << a << ",b=" << b << endl;
    cout << "max=" << *p1 << ",min=" << *p2 << endl;
    return 0;
}```
函数的形式参数是一个指针类型的变量,*表示指针变量的类型声明,square函数中的指针指向了主函数中的变量,square函数中的指针值的运算将改变主函数中变量num的值。

include<iostream>

using namespace std;
void square(int *n){
*n = *n * *n;
}
int main(){
int num = 2;
cout<<"The original number is "<<num<<endl;
square(&num);
cout<<"The new value of number is "<<num<<endl;
return 0;
}```

6. 引用

引用就是某一变量的一个别名,对引用的操作与对变量直接操作完全一样,定义引用的表示方法与定义指针类似,只是用&代替了*,例如int a,&ra=a,ra是目标引用名,ra=1;等价于a=1,并且&只是在声明的时候起标识作用,不同于取地址。
声明一个引用,不是新定义了一个变量,它本身不是一种数据类型。
不能建立引用的数组,无法建立一个由引用组成的集合,但是可以建立数组的引用。
下面是引用做函数参数的例子,同样和指针一样可以修改main中的变量值。

#include<iostream>
using namespace std;
void square(int &n){
    n = n * n;
}
int main(){
    int num = 2;
    cout<<"The original number is "<<num<<endl;
    square(num);
    cout<<"The new value of number is "<<num<<endl;
    return 0;
}```
###7. 内存分区
计算机中的内存在用于编程的时候,被人为的进行了分区,分为栈区stack,堆区heap,全局区(静态区Static),文字常量区,程序代码区。
函数的参数值,局部变量值都被存在了栈区,这部分的内存是系统帮助来进行管理的。
堆区的内存,是由程序员来进行分配和释放的,使用堆内存的原因是栈上内存较少,不够用,系统管理内存的方式比较死板,不方便用。堆上的内存,程序员手动分配后,如果不释放就有可能出现内存泄漏。
全局变量和静态变量的存储是在一块的,初始化的全局变量和静态变量在一块区域,为初始化的全局变量和未初始化的静态变量在相邻的另一块区域。程序结束后由系统释放。
文字常量区用于储存常量字符串的,程序结束后由系统释放。
程序代码区用于存放函数体的二进制代码。
`int *arr = new int[n];` new用于申请堆上空间的运算符,申请空间的最开始位置首地址,用前面声明的指针进行指向,从而可以像用数组一样用指针名使用这块空间。

include<iostream>

using namespace std;
int main(){
int i,n;
cin>>n;
int *arr= new int[n];

//以下代码对一个数组第一位进行了赋值
arr[0]=1;

//以下代码对一个长度为n的数组第二位开始依次做计算,并输出最后一个值结果
for(i=1;i<n;i++){
    arr[i]=arr[i-1]+i;
}
cout<<arr[n-1];
//请在下一行释放内存空间
delete []arr;
    arr = NULL;
return 0;

}```
new来动态申请分配堆内存
申请单个对象

int *p;
p = new int;
//或者申请的堆内存中直接包含值
int *p;
p = new int(100);```
动态申请数组

int *p;
p = new int[n];```
圆括号是用来赋初值的,方括号是用来说明申请堆空间大小的。
内存释放
delete加指针名的形式进行释放,如果指针指向的空间是数组,那么需要在delete后加上[]进行标识。然后将arr指针数组指向NULL,使之成为空指针。

8. 链表

#include <iostream>
using namespace std;

struct List
{
    int num;
    List *next;
};

List *head;

void Insert(List *&head)
{
    //在下面编写插入代码
    List *node = NULL;
    node = new List;
    cin >> node->num;
    node -> next = NULL;
    List *q, *p;
    //如果插入的值本身小于head的值,插入到最前面
    if(node->num <= head->num){
        node->next = head;
        head = node; 
        return ;
    }
    p = head; //记录head指针
    q = head->next; //记录指向head后面的
    while(q!=NULL){
        if(node->num > q->num){
            //大于的话,判断下一个
            p = q;
            q = q->next;
        }else{
            break;
        }
    }
    p->next = node;
    node->next = q;
}
void deleteNode(List *&head)
{
    //在下面编写删除代码
    int num;
    cin >> num;
    List *p = NULL, *q = NULL;
    p = head;
    if(p->num == num){
        head = p->next;
        delete p;
        return;
    }
    //如果删除的节点不是头节点
    q = p->next;
    while(q != NULL){
        if(q->num == num){
            p->next = q->next;
            delete q;
            return;
        }
        if(q->num > num){
            return;
        }
        p = q;
        q = q->next;
    }
    return ;
}
List *Create()
{
    List *p = NULL;
    List *q = NULL;
    head = NULL;
    for ( int i = 0; i < 3; i++ ) {
        p = new List;
        p->num = i * 2;
        if ( head == NULL ) {
            head = p;
        }
        else {
            q->next = p;
        }
        q = p;
    }

    if ( head != NULL ) {
        q->next = NULL;
    }

    return head;
}

void displayList(List *head)
{
    while ( head != NULL ) {
        cout << head->num;
        head = head->next;
        if ( head != NULL ) {
            cout << "->";
        }
    }
    cout << endl;
}

int main() {
    Create();
    Insert(head);
    displayList(head);
    return 0;
}```
###9. 单链表部分逆置
给定一个固定的单链表,输入两个数begin和end。将下标为begin到end之间的内容逆置。

给定的单链表为:0->2->4->6->8->10->12->14->16->18

测试数据确保begin和end不会超出单链表的长度范围,并且end>=begin

include <iostream>

using namespace std;

struct List
{
int num;
List *next;
};

List *head;

void reverse(int begin, int end, List *&head)
{
//在这个函数中编写你的代码
if(end <= begin || begin < 0 || end > 9){
return;
}
if(begin == 0){
List *p = head;
List *qian = p;
List *kai = p;
List *hou = NULL;
p = p->next;
for(int i=1;i<=end;i++){
hou = p;
p = p->next;
//开始转换
hou->next = qian;
qian = hou;
if(i == end){
kai->next = p;
head = hou;
}
}
}else{
//记录begin前面的指针,begin的指针,end处的,end后面的
//算法思想就是将链表指针逆过来
List *p = head;
//q 开始的前一个节点指针
List *q = NULL;
//qian 是两个节点进行逆序时第一个节点指针
//kai 是开始位置节点指针
//hou是逆序时候第二个节点指针
List *qian=NULL, *hou=NULL, *kai=NULL;
for(int i=0;i<=end;i++){
if(i == begin - 1){
q = p;
qian = p;
p = p -> next;
}else if (i >= begin){
if (i == begin){
kai = p;
}
hou = p;
p = p->next;
//这里进行两个节点之间的逆序
hou -> next = qian;
qian = hou;
if(i == end){
if(q == NULL){
head = hou;
}else{
//这里处理结束时候与链表其它位置交接的地方
q->next = hou;
kai->next = p;
}
}
}else{
p = p->next;
}
}
}
}

List *Create()
{
List *p = NULL;
List *q = NULL;
head = NULL;
for ( int i = 0; i < 10; i++ ) {
p = new List;
p->num = i * 2;
if ( head == NULL ) {
head = p;
}
else {
q->next = p;
}
q = p;
}

if ( head != NULL ) {
    q->next = NULL;
}

return head;

}

void displayList(List *head)
{
while ( head != NULL ) {
cout << head->num;
head = head->next;
if ( head != NULL ) {
cout << "->";
}
}
cout << endl;
}

int main() {
Create();
int begin, end;
cin >> begin >> end;
reverse(begin, end, head);
displayList(head);
return 0;
}```

10. 单链表是否有环?

如果单链表里有重复的节点,则说明单链表中存在环
有一个链表,我们需要判断链表中是否存在环。有环则输出true,否则输出false。

输入有多行,每行为由空格分隔的两个整数m和n,m是当前结点的数据,n代表当前结点的指针域指向第n个结点。

n存在四种情形:
①为-1,代表该结点的指针域指向NULL,输入结束;
②指向该结点之前的结点,如第3个结点的指针域指向n = 2的结点;
③指向自己,如第3个结点的指针域指向n = 3的结点;
④指向其直接后继结点,如第3个结点的指针域指向n = 4的结点,不能指向n = 5的结点。

当输入为:
1 2
2 3
3 -1
时,代表:第1个结点的数据为1,指向第2个结点;第2个结点的数据为2,指向第3个结点;第3个结点的数据为3,指向NULL,输入结束。
下面是代码实现一个初步的算法,基本思想还是建立链表后,进行遍历,遍历过的节点置flag为1,如果以后遍历到flag==1的节点,那么就说明有环。

#include <iostream>
#include <vector>
using namespace std;

struct List{
    int num;
    int flag;
    List *next;
};

//建立头指针
List *head = NULL;

int main(){
    //建立输入动态数组
    vector<int> data;
    vector<int> pos;
    vector<List *> point;
    int m, n;
    while(cin >> m >> n){
        data.push_back(m);
        pos.push_back(n);
    }
    for(int i=0;i<pos.size();i++){
        List *node = NULL;
        node = new List;
        node->num = data[i];
        node->flag = 0;
        point.push_back(node);
    }
    head = point[0];
    //建立链表
    for(int j=0;j<pos.size();j++){
        if(pos[j] != -1){
            point[j]->next = point[pos[j]-1];
        }else{
            point[j]->next = NULL;
        }
    }
    //遍历链表
    int loop = 0;
    List *p;
    head->flag = 1;
    cout << head->num << "->";
    p = head->next;
    while(p){
      if(p->flag != 1){
        cout << p->num << "->";
        p->flag = 1;
      }else{
        loop = 1;
        break;
      }
      p = p->next;
    }
    cout << endl;
    if(loop == 1){
      cout << "true" << endl;
    }else{
      cout << "false" << endl;
    }
}```
###11. 面向对象Object Oriented Programming
首先是抽象特性,封装,继承,多态
C++对于类内的成员提供了三种访问权限,public,protected,private表示公有,保护和私有。
  - public
表示其后定义的成员对程序所有部分可见,包括数据成员,成员函数以及类型。作为公有成员存在的大多是一些特定方式读取,修改数据成员的成员函数,类的接口。
  - private
修饰成员时表示该成员仅在该类内可见,类外无法对这个成员进行访问。包括大部分变量,一些不需要类的使用者关注的用于具体运算过程的函数。
  - protected
继承派生

  通过对成员访问的控制,信息被封存在了一个可控的地方,可以设定一些安全的途径,获得和修改一些信息,这就是面向对象的一个特性,封装

include<iostream>

using namespace std;
class Student{
private:
int id;
int age;
public:
int getID(){return id;}
int getAge(){return age;}
//先进行声明,函数比较复杂,定义在类的外面
void setID(int newID);
void setAge(int newAge);
};
void Student::setID(int newID){
id = newID;
}
void Student::setAge(int newAge){
age = newAge<100?newAge:99;
}
int main(){
Student tom;
tom.setID(1);
tom.setAge(16);
cout<<tom.getID()<<" "<<tom.getAge();
return 0;
}```

上一篇下一篇

猜你喜欢

热点阅读