c++进阶学习(未完待续)

C++

数组与指针

//数组在初始化时数组大小只能写常量
int arr[10] = { 0 };                 //arr有10位,并且全为0
int arr[n] = {0};				    //不合法
//但可以这样
int* arr = new int[n]			   //开辟一个n个int空间的地址,让arr作为指针来接
int* arr = {0,1,2,3};				//不合法,只能接一个0
char* arr = {"hello"};				//不合法,"hello"这时一个常量,不能用指针指向
const char* arr = {"hello"}           //合法,规定指针不改变常量的值
								  //"hello"是一个常量,执行这句是"hello"被分成'h','e','l','l','e'放在例如0x00,0x01,0x02,0x03,0x04这五个地址上,arr作为一个指针指向0x00('h'),编译器会理解arr = "hello"
char arr[] = {"hello"}               //合法,是一个字符数组 
//即使是c风格的字符串(字符数组),在末尾也会加上\0,(int *arr or int arr[])

当数组作为形参时:
  一.引用传递
	1. type fun(int *&arr....)		arr作为左值引用,当函数被调用时值传递为
								  int *&arr = array  (左右类型都是int*,左值是右值的别名)
	2. (int (&arr)[10],.....)       &arr要阔起来因为优先级不够,[]中必须填写一个常量
  二.指针传递
	3.(int arr[], ....)            一维数组传递,虽然数组不允许拷贝和复制,但是在形参中数组退化成为一个指针 
	4. (int * arr .....)		   指针传递,arr指向数组首地址
	
	
	

类型声明规则

右左原则:首先找到变量名,先往右看,当右边没有的话或者出现“)”的时候往左看,当左边没有的话或者出现“(”,循环这个过程,直至读完变量或者函数的声明:

int a;                         //a是一个int类型的变量
int *a;                        //a是一个指针,指向int型
int a();						//a是一个函数,返回值是int
int (*a)();					//a是指向一个返回int型的函数的函数指针
int a[5];					//a是一个数组,有5个空间,每一个空间大小(类型)是int
int *a[5];					//a是一个数组,有5个空间,每一个空间是一个指向int类型的指针(a是一个存储5个指针的数组)
int (*a)[5]; 				//a是一个指针,有5个空间,每一个空间大小是int(a一个是指向一个5个int类型的数组的指针)
int (*a[])();				//a是一个数组,数组装着指针,每个指针指向一个返回值为int型的函数
int (*(*a)[])();			//a是一个指针,指向一个数组,数组里装的都是指针,每个指针指向一个返回类型为int的函数(a是一个指向int型函数指针组成的数组的指针)
int const a;				//a是一个int类型是的常量
const int a;				//int const a 等价与 const int a
int const *a;				//a是一个指向一个int常量的指针,意味着a不是常量,可以给a赋值让a指向其他地方,*a(解引用)才是常量
int *const a; 				//a是一个常量指针,指向一个int类型,意味着指针a不可改变,在初始化后就一直指向这个地址,但是指向的int类型数据(*a)可以改变,


重载运算符

两种写法:

  1. 返回值 operator运算符(左参数,右参数){

    ​ 实现体

    }

  2. 返回值 operator运算符(运算符右边参数的引用){

    ​ 实现体

    }

第二种写法一般写在类里面,因为可以用this来指代左边的参数

显式类型转换

命名的强制类型转换:

name-cast(表达式);
double a;
const char* p;
static_cast(a);				//不支持不想关类型转换,比如一个类转换成一个int等,其他都支持即使是把子类强转为父类,(但是不安全,可能会越界)
const_cast(p);				//只能去掉const和加上const
dynamic_cast(new Son);		//只支持安全的类型转换,即把子类转换成父类,其他的都不支持
reinterpret_cast;			//最不安全的,不支持基本类型转换,其他的都支持

尽量用const代替宏定义

#define NUM 10
const int NUM = 10;

用#define NUM 10 的时候在预处理的时候NUM已经被替换成10了,NUM就没有被编译器看到过,当我们得出一些编译错误信息的时候,可能会带来一些困惑,如果这个NUM不是写在你的头文件里你你们不知道这个10代表什么意思,解决方法是用常量替代上面的宏

  1. const有类型,可进行编译器类型安全检查,#define无类型,不可进行类型检查

  2. const有作用域,而#define不重视作用域,宏不能作为命名空间,结构体,类的成员

    namespace A{
    	#define NUM  100     //无效,宏不能作为结构体,命名空间,类多成员
        const int NUM1 = 100;
    }
    cout<

左值与右值

左值:可以放在等号左边的,能够取地址,具有名字

int a = 0;						//变量名,a是左值
void func(int &a)				//左值引用的函数调用,a是左值
++a;--a;					   //前置自增或自减,因为++a是先算a+1,赋值给a,这个a是可以取地址的,所有是左值
++a = 10;					   //所有此式可以通过编译
(a = 9) = 100;					//赋值运算或者复合赋值运算也可以当作左值
A *a = &p;
*a = xxx;						//解引用也是左值

右值:只能放在右边的值,不能取地址,不具备名字

a = 9						//字面值,右值
a = Max(i,j);				//返回非引用函数调用,右值
a++;a--;			//后置自增或自减,右值,因为是先算a+1,然后把a+1这个值返回出去,而这个值无地址无名是个右值
a = b - c;			//算术表达式,右值
a = b && c;			//逻辑表达式,右值

当右值被作为拷贝函数参数时,将被作为将亡值,如果类中有移动拷贝构造时优先调用移动拷贝构造,否则调用拷贝构造

#include

using namespace std;

class A {
public:
    A() {
        cout << "AStruct " << this << endl;
    }
    ~A() {
        cout << "~A" << endl;
    }
    A(const A&) {										//拷贝构造
        cout << "cpStruct" << this << endl;
    };
    //A(A&&) {											//移动拷贝构造,参数列表中写的A&&是右值引用
    //    cout << "moveStruct" << this << endl;
    //}
};

A test() {
    A temp;
    return temp;
}

int main() {
    A a = test();			//函数调用返回值是一个右值,此时A类中移动拷贝构造被注释,此时调用的是拷贝构造
    return 0;
}

运行结果如下

AStruct 000000A466EFFBA4
cpStruct000000A466EFFCE4
~A
~A

如果把注释消掉,运行结果如下:

AStruct 000000CA73F1F804
moveStruct000000CA73F1F944
~A
~A

此时调用的是移动构造

用右值来调用拷贝构造时,右值被称为将亡值,目的是触发移动构造或移动赋值构造,并进行资源转移,之后调用析构函数(资源转移后调用析构,所以被称为将亡值)

左值引用和右值引用

int a = 100;
int& b = a;
const int& c = 10;
int&& d = 10;					//右值引用,指向右值,但是d是一个左值
d++;
cout << d << endl;				
int&& e = move(a);				//右值引用,运用move把左值a变成一个右值,但是e是一个左值
e++;
cout << e << endl;

运行结果如下:
11
101

常量也可以通过右值引用进行修改

声明出来的左值引用或者右值引用都是左值

左值引用时对左值的引用,右值引用(c++11新特性)是对右值的引用

​ 也有特例,左值引用也可以引用右值:const 左值引用 可以引用右值,但是这个局限,不能修改这个值(但是我们用引用的目的是通过引用修改变量,所以这个方法没什么用,所以c++11引入了右值引用来解决这个问题,让右值也可以被修改),

​ 右值引用也可以这样引用左值:用std::move(lvalue)函数,这个函数可以把左值转化成右值,如果这个左值的类型实现了移动构造或者移动赋值构造,则这个右值即使一个将亡值

左值引用避免了对象的拷贝,比如再函数传参用引用起别名来代替值传递

右值引用实现了移动语义和实现完美转发

移动语义:

#include

using namespace std;

class A {
public:
    int* p;
    A() {
        p = new int(10);
        cout << "A():p=" << p << endl;
    }
    ~A() {
        delete p;
        p = nullptr;
        cout << "~A()" << endl;
    }
    A(const A& a) {								//深拷贝,重新分配资源
        //p = a.p;								//当不适用拷贝构造直接进行赋值的话
        //a.p = nullptr;						//对a的p指针置空会报错,因为a是一个常量引用,不可改变,所以a.p会存在出现野指针的问题,会导致内存泄漏
        p = new int(10);
        memcpy(p, a.p, sizeof(int));
        cout << "cpStruct:p=" << p << endl;
    };
    /*A(A&& a) {
        this->p = a.p;
        a.p = nullptr;
        cout << "moveSturct:p=" << endl;
    }*/
};


int main() {
    A a;
    A b(a);
    return 0;
}

运行结果如下:

A():p=000001F7EA9A71F0
cpStruct:p=000001F7EA9A6AF0
~A()
~A()

这个时候a作为左值调用的是拷贝构造,在堆里new了一个与原对象一样的对象,是深拷贝,所以两个对象地址不一样

如果改成这样

#include

using namespace std;

class A {
public:
    int* p;
    A() {
        p = new int(10);
        cout << "A():p=" << p << endl;
    }
    ~A() {
        delete p;
        p = nullptr;
        cout << "~A()" << endl;
    }
    A(const A& a) {
        p = new int(10);
        memcpy(p, a.p, sizeof(int));
        cout << "cpStruct:p=" << p << endl;
    };
    A(A&& a) {
        this->p = a.p;
        a.p = nullptr;
        cout << "moveSturct:p=" << p << endl;
    }
};


int main() {
    A a;
    A b(move(a));				//让a变成一个右值,实现右值引用,在类里会调用移动构造而不是拷贝构造
    cout << a.p << endl;
    return 0;
}

结果如下:

A():p=0000024A277E6CB0
moveSturct:p=0000024A277E6CB0
0000000000000000
~A()
~A()

此时b与之前的a完全一样,而a在调用完移动构造后销毁.

当用move把a变成右值是,a变成将亡值,会触发移动构造,即在消亡之前把a赋给b,完成后自己灭亡

相比与拷贝构造,移动构造性能更强,类似与两个冰箱,a冰箱有一只大象,冰箱也想要有一只大象,拷贝构造的做法是,在外界再找一个一摸一样的大象放在冰箱里,而移动构造的做法是,把a中大象移动到b冰箱去

移动语义的用法:

  1. 对象赋值时避免资源重新分配

  2. stl容器应用:

    vector vec;
    vec.push_back(A());
    

    A()作为一个右值,在push_back(A a)函数中形参进行值传递时 A a = A();触发了移动构造,直接把A()的值让vector容器接管,相比之前效率更高

完美转发:不仅能准确的转发参数的值,还能保证被转发的参数左右值属性不变

void func(int &n){
	cout<<"lvalue="< 
     
  • 悬挂指针:多个指针指向一个堆里的一个地址,其中一个指针把这个地址delete掉了,也置空了,但其他的指针不知道,其他指针就是悬挂指针

  • 踩内存:当上面两种情况下,野指针或者悬挂指针指向的地址被其他进程重新使用,这些指针可以对意料之外的指向的地址操作,这叫做踩内存

  • 没有释放资源产生内存泄漏

    1. new与delete次数不匹配

    2. 次数匹配,但在运用多态时,用父类构建子类对象时,父类没有虚析构,析构函数就静态绑定了父类析构,子类生命周期结束时,只会调用父类析构,子类的属性就得不到释放造成了内存泄漏

      #include
      
      using namespace std;
      
      class A {
          int i;
      public:
          ~A() {
              cout << "父类析构调用" << endl;
          }
      };
      class B :public A {
          int j;
      public:
          ~B() {
              cout << "子类析构调用" << endl;
          }
      };
      
      int main() {
          A* b = new B();
          delete b;
          return 0;
      }
      
      

      运行结果如下:

      父类析构调用

      应改成虚析构,在父类析构前加入virtual,让他在运行时绑定子类析构,因为子类析构结束后会调用父类析构

      #include
      
      using namespace std;
      
      class A {
          int i;
      public:
          virtual ~A() {
              cout << "父类析构调用" << endl;
          }
      };
      class B :public A {
          int j;
      public:
          ~B() {
              cout << "子类析构调用" << endl;
          }
      };
      
      int main() {
          A* b = new B();
          delete b;
          return 0;
      }
      
      

      子类析构调用
      父类析构调用

  • 重复释放资源,引发coredump

  • 解决方案:智能指针:

    STL

    string库内部的声明如下

    //构造函数
    string();								//创建一个空的字符串
    string(const string& str);				//拷贝构造,用一个string对象初始化
    string(const char* s);					//用一个字符串初始化
    string(int n,char c);					//初始化字符串为n个字符c
    
    //基本赋值操作
    string& operator=(const char* s);		//重载=号,结果返回这个这个对象的引用,所有可以链式操作,就像cout一样在这里是(s = "1234") = "asd";不报错,因为完成等号后的值返回的是一个引用,是个左值
    string& operator=(const string& s);
    string& operator=(char c);
    
    string& assign(const char* s);			//字符串赋值赋值
    string& assign(const string &s);
    string& assign(const char* s ,int n);    //把s字符串前n个赋给此对象
    string& assign(int n ,int c);			//用n个字符c赋值给此对象
    string& assing(const string&s,int start,int n); //将s的从start开始的n个字符赋值给对象
    
    //字符串的存取操作
    char& operator[](int n); 				//通过[]来取字符,这个可能会数组越界
    char& at(int n);						//通过at方法获取字符,内涵异常处理不会越界
    
    //字符串的拼接操作
    string& operator+=(const string& str);		//重载+=,实现拼接
    string& operator+=(const char* s);		
    string& operator+=(char c);
    
    string& append(const char*s);				//将s追加到该字符串尾部
    string& append(const char*s,int n);			//把s的前n个字符追加到字符串尾部
    string& append(const string*s);
    string& append(const string*s,int pos,int n);//把s从pos开始的n个字符追加到字符串上
    string& append(int n ,char c);				//追加n个字符c
    
    //字符串的查找替换
    int find(const string& str,int pos = 0) const;		//查找第一次出现str的位置,pos为参数默认为0,,结尾加cosnt代表这是个只读函数,不会改变数据成员,可以提高程序健壮性
    int find(const char* s,int pos = 0) const;
    int find(const char* s,int pos = 0, int n) const;	//查找前n个字符第一次出现s的位置
    int find(char c,int pos = 0) const;					//查找字符c第一次出现的为
    int rfind(const string& str,int pos = npos) const;		//逆序找
    int rfind(const char* s,int pos = npos) const;
    int rfind(const char* s,int pos =npos, int n) const;	
    int rfind(char c,int pos = npos) const;	
    
    string& replace(int pos,int n,const string& str);		//在字符串从pos开始后n位变成str
    string& replace(int pos,int n,const char* str);
    
    //大小比较
    bool operator>(const string&s);						//> < == != 都可用
    ......
    int compare(const string &s)const;					//按字符编码排序,大于返回1小于返回-1,=返回0
    int compare(const char* s)const;
    
    //提取子串
    string substr(int pos = 0, int n = nops) const;	//返回前n个字符组成的字符串
        
    //插入和删除
    string& insert(int pos, const char* s);    		//插入字符串
    string& insert(int pos, const string& s); 		
    string& insert(int pos, int n, char c);  		//在指定位置插入n个字符c
    string& erase(int pos, int n = npos);			//删除从pos开始的n个字符
    

    vector

    ​ 在stl中除了string之外的所有容器都是类模板

    ​ vector是动态数组,每当size()的返回值(即容器的大小)即将超过容器容量时,capactiy()返回值(容量)会自增一倍(0,1,2,4,8,16,32…),当这个容器后面的空间没有需要动态增加的大小时,它的做法是找一个更大的内存空间,然后把原数据拷贝到新空间,并释放原空间

    vector v;
    vector::iterator it;  				//it是存放int类型vector容器的迭代器
    int i;
    v.push_back(i);						
    v.pop_back(i);
    v.front();
    v.back();
    v.begin();             //得到容器的起始迭代器指向首元素
    v.end();				//得到容器的尾迭代器指向尾元素的下一个位置,
    it++;					//迭代器重载了++ --表明迭代器到下一个迭代器的位置
    *it						//代表it迭代器指向的对象
    

    函数API

    //构造函数
    vector v;
    vector(v.begin(),v.end());//可以这样int arr[] = {1,2,3};vector;v(arr,arr+sizeof(arr)/sizeof(int));				 
    vector(n,elem);								//拷贝n个elem给自身			
    vector(const vector &vec);
    
    //赋值
    assign(beg,end);							//将[beg,end)区间的数据赋值自身
    assign(n,elem);
    vector& operator=(const vector &vec);			//重载=
    swap(vec);									//将vec与本容器内容互换
    size();								//元素个数
    empty();			//是否为空
    resize(int num);			//重新指定长度为num,容器变短则删除超出的,变长则填默认值
    resize(int num ,int elem);	//如上,elem是默认值
    capacity();					//容器的容量
    reserver(int len); 	//直接预留len个长度,预留位置不初始化
    
    //存取操作
    at(int idx);		
    operator[];  
    front();			//返回第一个数据元素
    back();			
    
    //插入和删除
    insert(const iterator pos,int count,ele); // pos代表插入位置的迭代器,n为个数,ele是哪个数
    push_back(ele);
    pop_back();
    erase(const iterator start,const iterator end);//删除迭代器从start到end'之间的元素
    erase(const iterator pos); 				//删除迭代器指向的元素
    clear();		//删除容器中所有
    
    

    deque

    ​ 双端动态数组,其也实现了迭代器,但是复杂程度和vector不是一个量级的,尽可能的使用vector,升值进行排序可以先把数据赋值到vector中在vector中排完序后再赋回来还更快

    deque的实现原理:

    ​ 有一个连续的地址被称为中控器,每个地址储存一个指针,指针指向缓冲区一小段连续的空间,deque的数据存在缓冲区中其中一小段连续空间中的一个空间,当这一小段空间填满了,中控器会再这个指针的上放或者下方再创建一个指针,指向缓冲区中另一个连续的小段地址,如果从头端压入,则数据放在这段连续地址从右往前数第一个空地址,如果从后方压入,则放在连续地址的从前往后数的第一个空地址

    deque的API与vector大同小异,多了push_front(ele)和pop_front(ele);

    stack

    ​ 栈 :先进后出的容器,没有迭代器,不支持遍历

    pop();					//压出
    push(ele);				//压入元素
    top();					//栈顶元素
    empty();				//判断堆栈是否为空
    size();					//栈大小
    

    queue

    ​ 队列容器:先进先出,出数据一方叫对头,入数据较队尾,没有迭代器,不支持遍历行为

    API与stack大同小异

    list

    ​ 链表:储存非连续非顺序的一种数据结构,是一个双向链表,拥有双向迭代器,不用管具体细节,能用就像

    API与deque大同小异,因为sort排序定义在algorithm头文件里,只支持随机访问容器不支持链表容器,所有list提供了内部sort成员函数

    sort(l.begin(),l.end());
    

    set

    ​ 集合:只有键值,所有元素的键值会自动排序,且不允许有两个相同的键值,有迭代器,但是是只读迭代器,不能中途改变,因为改变键值可能会导致set从有序变为无序,set和multiset底层都是用红黑树完成的是平衡二叉树

    multiset与set完全相同,除了运行键值重复

    API

    swap(st);			//交换两个集合容器
    insert(ele);			//插入ele
    erase(pos);			//删除pos迭代器所指的元素,返回下一个元素的迭代器
    erase(beg,end);		//删除[beg,end)的所有元素
    erase(ele);				//删除值为ele的元素
    find(key);			//查找key的个数
    
    //更改set的排序规则
    set s1;		//排序规则是在尖括号里面是一个类型,所有只能通过仿函数定义规则(仿函数重载())
    //如下
    class MyCompare{
    public:
    	bool operator()(int v1,int v2){			//重载(),当调用MyCompare()的时候,执行这个操作
            return v1>v2;					//改成从小到大
        }    
    }
    
    set s1;			//当实例化的时候,类MyCompare实例化构造函数触发仿函数,实现排序规则的改变
    
    //再更改set的排序规则的对象是自定义结果类型的时候,可以设置这个仿函数的类设置为友元类定义在这个自定义结构里
    //比如
    class Person{
    	friend class MyCompare;
        .....
    }
    
    set::const_iterator ret = s1.find(ele);      //find返回一个迭代器,因为set所有迭代器都是只读,所以用const_iterator来接,如果没找到这个值,返回s1.end();
    if (ret != s1.end()){
    	cout<<*ret<::const_iterator,set::const_iterator> p;
    p = s1.equal_range(keyele);
    if (p .first != s1.end()){
        cout<<"下限为"<<*(p.first)<

    pair

    ​ 队组:将一对值组合成一个值,可以有不同的数据类型,通过first,second访问

    ​ make_pair(type1,type2)返回一个队组

    map

    算法

    //suanfa.h
    //constexpr auto N = 10;
    
    //auto randArr(int max) -> int(*)[N];
    
    int* randArr(int size, int max);
    
    void print(int* (*pf)(int size, int max), int size, int max);
    
    void print(int* arr, int size);
    
    void swap(int& a, int& b);
    
    void Bubble_sort(int* arr, int size);
    
    void Select_sort(int* arr, int size);
    
    void Insert_sort(int* arr, int size);
    
    void Merge_sort(int* arr, int L ,int R);
    
    void Merge(int* arr, int L,int M, int R);
    
    void Quick_sort(int* arr, int L, int R);
    
    int Quick(int* arr, int L, int R);
    
    int Fenzhi_get_max(int* arr, int L, int R);
    
    void Hanoi(int nums, char a, char b, char c);
    
    //main.cpp
    #include
    #include"suanfa.h"
    
    using namespace std;
    
    int main() {
    	
    	int size,max;
    	cin >> size >> max;
    	int * arr = randArr(size, max);
    	time_t t1 = time(0);
    	Merge_sort(arr, 0, size - 1);
    	time_t t2 = time(0);
    	cout << t2 - t1 << endl;
    	
    	//print(arr, size);
    	//cout << "-----------" << endl;
    	//cout << Fenzhi_get_max(arr,0,size - 1);
    
    	//int n;
    	//cin >> n;
    	//Hanoi(n, 'a', 'b', 'c');
    
    	return 0;
    }
    
    //suanfa.cpp
    #include
    #include"suanfa.h"
    #include
    using namespace std;
    
    //constexpr auto N = 10;
    
    //auto randArr(int max) -> int(*)[N]
    //{
    //	srand(time(0));
    //	int(*arr)[N] = (int(*)[N]) new int[N]();
    //	for (int i = 0; i < N; i++) {
    //		(*arr)[i] = rand() % max;
    //	}
    //	return arr;
    //}
    
    int* randArr(int size, int max) {
    	srand(time(0));
    	int* arr = new int[size]();
    	for (int i = 0; i < size; i++) {
    		arr[i] = rand() % max;
    	}
    	return arr;
    }
    
    void print(int* (*pf)(int size, int max), int size, int max) {
    	for (int i = 0; i < size; i++) {
    		cout << pf(size, max)[i] << endl;
    	}
    }
    
    void print(int* arr, int size)
    {
    	for (int i = 0; i < size; i++) {
    		cout << arr[i] << endl;
    	}
    }
    
    void swap(int& a, int& b)
    {
    	int temp = a;
    	a = b;
    	b = temp;
    }
    
    void Bubble_sort(int* arr, int size)
    {
    	for (int i = 0; i < size - 1; i++) {
    		for (int j = 0 ; j < size - i - 1; j++) {
    			if (arr[j] > arr[j + 1]) {
    				swap(arr[j], arr[j + 1]);
    			}
    		}
    	}
    }
    
    void Select_sort(int* arr, int size)
    {
    	
    	for (int i = 0; i < size - 1; i++) {
    		int min = i;
    		for (int j = i + 1; j < size ; j++) {
    			if (arr[j] < arr[min]) {
    				min = j;
    			}
    		}
    		swap(arr[i], arr[min]);
    	}
    }
    
    void Insert_sort(int* arr, int size)
    {
    	int ok = 1;
    	for (int i = 0; i < size - 1; i++) {
    		for (int j = i + 1; j > 0 && ok; j--) {
    			if (arr[j] < arr[j - 1]) {
    				swap(arr[j], arr[j - 1]);
    			}
    			else {
    				ok == 0;
    			}
    		}
    	}
    }
    
    void Merge_sort(int* arr, int L, int R)
    {
    	if (L == R) return;
    	int M = L + (R - L) / 2;
    	Merge_sort(arr, L, M);
    	Merge_sort(arr, M + 1, R);
    	Merge(arr, L, M, R);
    }
    
    void Merge(int* arr, int L,int M, int R)
    {
    	int* temp = new int[R - L + 1];
    	int i = 0;
    	int p1 = L;
    	int p2 = M + 1;
    	while (p1 <= M && p2 <= R) {
    		temp[i++] = arr[p1] < arr[p2] ? arr[p1++] : arr[p2++];
    	}
    	while (p1 <= M) {
    		temp[i++] = arr[p1++];
    	}
    	while (p2 <= R) {
    		temp[i++] = arr[p2++];
    	}
    	for (int c = 0; c < R - L + 1; c++) {
    		arr[c + L] = temp[c];
    	}
    	delete[] temp;
    }
    
    void Quick_sort(int* arr, int L, int R)
    {
    	if (R - L <= 0) return;
    	else {
    		int M = Quick(arr, L, R);
    		Quick_sort(arr, L, M - 1);
    		Quick_sort(arr, M + 1, R);
    	}
    }
    
    int Quick(int* arr, int L, int R)
    {
    	int point = R;
    	int p1 = L , p2 = R - 1;
    	while (1) {
    		while (arr[p1] < arr[point] && p1 < point) {
    			p1++;
    		}
    		while (arr[p2] > arr[point] ) {
    			p2--;
    		}
    		if (p1 >= p2) {
    			break;
    		}
    		else {
    			swap(arr[p1], arr[p2]);
    			p1++;
    			p2--;
    		}
    	}
    	swap(arr[p1], arr[point]);
    	return p1;
    }
    
    
    int Fenzhi_get_max(int* arr,int L , int R)
    {
    	if (R < L) return -1;
    	if (R == L) return arr[L];
    	if (R - L == 1) return arr[L] > arr[R] ? arr[L] : arr[R];
    	int M = L + (R - L) / 2;
    	int last_L = Fenzhi_get_max(arr, L, M);
    	int last_R = Fenzhi_get_max(arr, M + 1, R);
    	return last_L > last_R ? last_L : last_R;
    }
    
    void Hanoi(int nums, char a, char b, char c)
    {
    	static int i = 1;
    	if (nums == 1) {
    		cout << "第" << i << "次" << a << "->" << b << endl;
    		i++;
    	}
    	else {
    		//把nums-1个圆盘从起始盘搬到辅助盘上去
    		Hanoi(nums - 1, a, c, b);
    		//把最大的圆盘从起始盘搬到目标盘去
    		cout << "第" << i << "次" << a << "->" << c << endl;
    		i++;
    		//把nums-1个圆盘从辅助盘搬到目标盘上去
    		Hanoi(nums - 1, c, b, a);
    	}
    }
    
    
    
    
    
    
    
    

    你可能感兴趣的:(c++,学习,数据结构)