01-0003 C++诸多排序算法,前来报到~

前言:个人觉得在这些函数里面注释足够详细,不需要太多的解释,适合有基础的人看,主要是用来回忆算法细节,快速捡起该算法。其实是应为我实在写不动了,最近既要找工作,又要写毕设,却是有点忙不过来。而且源码1000多行,写注释写到吐的我也真的是醉了,甚至想扔出一只只嘤嘤怪,然后开始一局嘤嘤怪忍者,emm~
.
它如同参考文档,在未来的开发中起着指导性作用,但是不可直接拿来使用;在实际应用中可能比这个要复杂的多,需要进行更多的优化与改进。
.
如果想要更加详细的掌握每一种排序算法,可以在点击最下端的相关链接,都是我参考过的,也可以自行百度,拿着别人的算法与本算法进行比对,带着这里的注释,去看懂别处的代码。
.
如果想要直接运行可以点击最下端的下载链接,上面有VS2019的工程文件,里面写的比较详细,整体观比较强,因目前还没有上传GitHub,CSDN现在不可以设置免费下载,所以见谅。
.
不过还是可以get到源文件的,可以✉给我写信✉,或者是将下面的函数自己整理到对应的文件中,自己运行一下也是可以的。

.
在下面的代码段中,对常见的部分莫过于数据的拷贝,memcpy函数的使用,why?

1.因为本文实现了一个类,类中有多个排序方法,但是却只有一组数据;
2.为了方便测试,只能每次排序的时候都拷贝一份,而后返回有序数组;
3.同样为了方便测试,所有排序最后都可以通过一个无参函数进行调用。

Note:一定要把上面的文字看完,否则容易产生误解。

P1:初始化部分 [initial part]

为方便使用排序算法,设置了一个可以根据外部数据进行初始化的构造函数。
为方便测试排序算法,设置了一颗可以自动生成待排数组的构造函数。

1.构造函数 [Constructed function]

/***************************************************
**名称:Cpp_Sorts
**参数:data:给定数组,num:数组长度
**返回:无
**功能:根据指定的数组和长度对类对象进行初始化
**性能:
**注意:一定要初始化map
****************************************************/
Cpp_Sorts::Cpp_Sorts(int const* data, const int num) {
    this->data = new int[num];
    this->num = num;
    memcpy(this->data, data, sizeof(int) * num);//将传入的数据,拷贝到this->data中
    ini_func();//记得初始化【字符串-函数名】map
}

/***************************************************
**名称:Cpp_Sorts
**参数:left:随机下限,right:随机上限,num:生成随机数的个数
**返回:无
**功能:根据上下限,生成num个随机数,并初始化map
**性能:
**注意:初始化map
****************************************************/
Cpp_Sorts::Cpp_Sorts(int const left, int const right, int const num) {
    random_arr(left, right, num);
    ini_func();//记得初始化【字符串-函数名】map
}

2.随机生成 [random create]

/***************************************************
**名称:random_arr
**参数:begin:随机下限,end:随机上限,num:随机数个数
**返回:无
**功能:根据上下限生成num个随机数,并且给该类成员赋值
**性能:
**注意:data要申请空间
****************************************************/
void Cpp_Sorts::random_arr(int begin, int end, int num) {
    this->num = num;
    data = new int[this->num];
    srand((unsigned int)time(NULL));//初始化种子
    for (int i = 0; i < this->num; i++) {
        data[i] = rand() % (end - begin + 1) + begin;//成成[begin,end]的随机数
    }
}

3.通过字符串调用成员函数 [call method by string]

/***************************************************
**名称:ini_fun
**参数:无
**返回:无
**功能:初始化【字符串-函数名】的map
**性能:
**注意:注意下面的书写格式,不能变
****************************************************/
void Cpp_Sorts::ini_func() {
    fun_map["select"] = &Cpp_Sorts::s_select;
    fun_map["insert"] = &Cpp_Sorts::s_insert;
    fun_map["quick"] = &Cpp_Sorts::s_quick;
    fun_map["heap"] = &Cpp_Sorts::s_heap;
    fun_map["std_sort"] = &Cpp_Sorts::s_sort;
    fun_map["std_stable_sort"] = &Cpp_Sorts::s_stable_sort;
    fun_map["bubble"] = &Cpp_Sorts::s_bubble;
    fun_map["cocktail"] = &Cpp_Sorts::s_cocktail;
    fun_map["shell"] = &Cpp_Sorts::s_shell;
    fun_map["botonic"] = &Cpp_Sorts::s_bitonic_rec;
    fun_map["merge"] = &Cpp_Sorts::s_merge;
    fun_map["LSD"] = &Cpp_Sorts::s_LSD;
    fun_map["MSD"] = &Cpp_Sorts::s_MSD;
    fun_map["gnome"] = &Cpp_Sorts::s_gnome;
    fun_map["bogo"] = &Cpp_Sorts::s_bogo;
}

/***************************************************
**名称:use_function
**参数:fun_name:函数名字
**返回:无
**功能:根据函数名字,调用类的成员函数,并计算函数运行的时间
**性能:
**注意:调用之前一定要初始化
****************************************************/
void Cpp_Sorts::use_function(string fun_name) {
    cout << "Using sort: " << fun_name << endl;
    auto start = steady_clock::now();//时钟开始
    int* arr = (this->*fun_map[fun_name])();
    auto end = steady_clock::now();//时钟结束
    print_arr(arr);//打印数组
    auto dur = duration_cast<microseconds>(end - start);//计算间隔
    cout << "Time consumption: " << dur.count() << "μs" << endl;
}

P2:排序部分 [sort part]

1.选择排序 [select sort]

描述:反复从序列中选出极值元素放置在序列一段[已经选择过的象征性剔除]

  1. 首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置。
  2. 再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。
  3. 重复第二步,直到所有元素均排序完毕。
/***************************************************
**名称:s_select
**参数:无
**返回:排好顺序的数组的指针
**功能:统一接口,调用之后自动排序
**性能:
**注意:
****************************************************/
int* Cpp_Sorts::s_select() {
    return s_select(this->data, this->num);
}
/***************************************************
**名称:s_select
**参数:data:原始数组,num:数组长度
**返回:指向有序数组的指针
**功能:拷贝一个data数组,并对数组元素进行排序
**性能:最好O(n^2),平均O(n^2),最坏O(n^2)
**注意:分两段,两重循环
****************************************************/
int* Cpp_Sorts::s_select(int const* data, const int num) {
    //拷贝副本
    int* arr = new int[num];
    memcpy(arr, data, sizeof(int) * num);

    //选择排序
    //选择一个最小的元素与第i个元素进行交换
    //小元素最后都放在前面,有序段越来越长
    //第一轮循环用于遍历所有元素
    //这个代码还是很好的懂的,你细品
    for (int min, i = 0; i < num - 1; i++) {
        min = i;
        //第二轮循环用来找最小元素的下标
        for (int j = i + 1; j < num; j++) {
            if (arr[min] > arr[j]) {
                min = j;
            }
        }
        //找到最小元素,进行交换
        if (min != i) {
            arr[min] ^= arr[i] ^= arr[min] ^= arr[i];
        }
    }
    return arr;
}

2.插入排序 [insert part]

描述:顺序选择序列中的元素插入到有序数据段中[象征性的分割原始序列]

  1. 将排序序列第一个元素看做一个有序序列,把第二个元素到最后一个元素当成是未排序序列。
  2. 从头到尾依次扫描未排序序列,将扫描到的每个元素插入有序序列的适当位置。(如果有相等则插入到相等元素的后面。[规则可以自己定义])
/***************************************************
**名称:s_insert
**参数:无
**返回:有序数组的指针
**功能:
**性能:
**注意:
****************************************************/
int* Cpp_Sorts::s_insert() {
    return s_insert(this->data, this->num);
}
/***************************************************
**名称:s_insert
**参数:data:原始数组,num:数组长度
**返回:有序数组的指针
**功能:拷贝原始数组,并对新数组排序返回
**性能:最好O(n),平均O(n^2),最坏O(n^2)
**注意:分两段,两重循环
****************************************************/
int* Cpp_Sorts::s_insert(int const* data, const int num) {
    //拷贝副本
    int* arr = new int[num];
    memcpy(arr, data, sizeof(int) * num);

    //插入排序
    //插入排序前段有序,后端无序
    //每次从后段里面取出一个,从后向前对前段元素进行比较,到合适位置进行插入
    //第一轮循环用于遍历尾部未知元素
    for (int i = 1; i < num; i++) {
        int temp = arr[i];
        int j = i - 1;
        //第二轮循环用于挪动前段有序元素,给待插入元素腾位置
        //如果有序段全都挪动了位置,说明这个元素最小
        //如果找到一个元素比当前元素小,就把当前元素紧随其后放置
        //意即,找到一个不比temp大的元素,将temp放在该元素的后面
        while ((j >= 0) && (arr[j] > temp)) {
            arr[j + 1] = arr[j];//有序段元素相逐渐后撤
            j--;
        }
        //如果有序段发生了变化
        if (j != i - 1) {
            arr[j + 1] = temp;
        }
    }
    return arr;
}

3.快速排序 [quick sort]

描述:置元素于选定的基准两侧,逐步递归

  1. 从数列中挑出一个元素,称为 “基准”(pivot);
  2. 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。
  3. 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序;
/***************************************************
**名称:s_quick
**参数:无
**返回:指向有序数数组的指针
**功能:统一接口
**性能:
**注意:
****************************************************/
int* Cpp_Sorts::s_quick() {
    return s_quick(this->data, this->num);
}
/***************************************************
**名称:s_quick
**参数:data:原始数组,num:数组长度
**返回:指向有序数组的指针
**功能:调用重载的s_quick函数
**性能:
**注意:high索引值
****************************************************/
int* Cpp_Sorts::s_quick(int const* data, const int num) {
    //拷贝副本
    //由于快排要改变当前数组,所以在递归前进行拷贝
    int* arr = new int[num];
    memcpy(arr, data, sizeof(int) * num);
    return s_quick(arr, 0, num - 1);//确保高端元素的索引不超出范围
}
/***************************************************
**名称:s_quick
**参数:arr:待排序数组,low:左端,high:右端
**返回:返回有序数组的指针
**功能:对数组进行排序,并返回指向该数组的指针
**性能:最好:O(nlogn),平均O(nlogn),最坏:O(n^2)
**注意:下标别弄混
****************************************************/
int* Cpp_Sorts::s_quick(int* arr, int const low, int const high) {
    if (high <= low) return arr;//当汇聚到中间的时候退出
    int i = low, j = high + 1, key = arr[low];//默认第一个元素是key,下面是--j,所以这里写的是j=high+1
    while (true) {
        while (arr[++i] < key && (i != high));  //从左向右找比key大的值
        while (arr[--j] > key && (j != low));   //从右向左找比key小的值
        if (i >= j) break;
        arr[i] ^= arr[j] ^= arr[i] ^= arr[j];   //交换i,j对应的值,交换之后j位置的值是比low小的
    }
    if (low < j)//确保使用异或进行数值交换的是两个不一样的值
        arr[low] ^= arr[j] ^= arr[low] ^= arr[j];//中枢值与j对应值交换,去报中数值在中间
    s_quick(arr, low, j - 1);
    s_quick(arr, j + 1, high);
}

4.堆排序 [heap sort]

描述:建立二叉堆并反复摘取最大元

  1. 创建一个堆 H[0……n-1];
  2. 把堆首(最大值)和堆尾互换;
  3. 把堆的尺寸缩小 1,并调用 shift_down(0),目的是把新的数组顶端数据调整到相应位置;
  4. 重复步骤 2,直到堆的尺寸为 1
/***************************************************
**名称:s_heap
**参数:无
**返回:指向有序数组的指针
**功能:
**性能:
**注意:
****************************************************/
int* Cpp_Sorts::s_heap() {
    return s_heap(this->data, this->num);
}
/***************************************************
**名称:s_heap
**参数:data:原始数组,num:数组长度
**返回:指向有序数组的指针
**功能:堆排序
**性能:最好=平均=最坏=O(nlogn)
**注意:
****************************************************/
int* Cpp_Sorts::s_heap(int const* data, int const num) {
    //拷贝副本
    int* arr = new int[num];
    memcpy(arr, data, sizeof(int) * num);

    //排序
    //神奇的是,调整的过程与建立大根堆的过程是一致的
    //建立大根堆
    for (int i = num / 2 - 1; i >= 0; i--) {
        //注意这里是从最后一个根节点开始的,可以自己代入试一试,确实是最后一个节点
        s_max_heapify(arr, i, num - 1);//实际上的建立大根堆过程,最后一个节点的计算公式准确无误
    }
    //每次浮上一个最小元素,沉下一个最大元素
    for (int i = num - 1; i > 0; i--) {
        arr[0] ^= arr[i] ^= arr[0] ^= arr[i];//交换首尾两个元素的位置
        s_max_heapify(arr, 0, i - 1);//末尾少去一个元素之后,再进行调整
    }
    return arr;
}
/***************************************************
**名称:s_max_heapify
**参数:arr:待排序数组,start:数组首位,end:数组末位
**返回:指向具有堆序性的数组的指针
**功能:见于函数内部
**性能:
**注意:
****************************************************/
int* Cpp_Sorts::s_max_heapify(int* arr, int const start, int const end) {
    //两个功能:
    //1、对于任意三个元素(one dad,two sons)进行大根堆调整
    //2、当大根堆建立完毕之后,让小元素下沉,大元素上升。
    //【由于大根堆已经建立完毕,所以能浮在上层的一定是大元素,而且不会有比他更大的,而小元素则会继续下沉,直到一个合适的位置】
    int dad = start;
    int son = dad * 2 + 1;
    while (son <= end) {
        if (son + 1 <= end && arr[son] < arr[son + 1])//从子节点中选择一个比较大的,并且标记下标
            son++;
        if (arr[dad] > arr[son])//如果父节点更大,就直接返回
            return arr;
        else {//否则就应该将父子节点交换,让大的在上面
            arr[dad] ^= arr[son] ^= arr[dad] ^= arr[son];
            dad = son;//然后更加深入让小元素沉到海底
            son = dad * 2 + 1;
        }
    }
}

5.标准库:sort [STL:sort]

描述:标准库函数,sort()排序不是稳定排序,sort是主要用到了快速排序(平均时间复杂度为O(nlogn)),还结合了插入排序(时间复杂度为O(n²))和堆排序(时间复杂度为O(nlogn))

/***************************************************
**名称:s_sort
**参数:无
**返回:指向有序数组的指针
**功能:
**性能:
**注意:
****************************************************/
int* Cpp_Sorts::s_sort() {
    return s_sort(this->data, this->num);
}
/***************************************************
**名称:s_sort
**参数:data:原始数组,num:元素个数
**返回:指向有序数组的指针
**功能:拷贝,排序,返回指针
**性能:平均O(nlogn)
**注意:这里使用的是标准库函数
****************************************************/
int* Cpp_Sorts::s_sort(int const* data, int const num) {
    //创建副本
    int* arr = new int[num];
    memcpy(arr, data, sizeof(int) * num);

    //排序
    /*int*beg=std::begin(arr);//begin()可以直接对于数组元素使用,但是不能动态数组使用
    int* end = end(arr);*/
    //对数组使用迭代器的方式,尾部并不是最后一个元素,而是最后一个元素后的一个空元素
    std::sort(&arr[0], &arr[num]);
    return arr;
}

6.标准库:stable_sort [STL:stable_sort]

描述:标准库函数,保证最坏的排序效率。

/***************************************************
**名称:s_stable_sort
**参数:无
**返回:指向有序数组的指针
**功能:
**性能:
**注意:
****************************************************/
int* Cpp_Sorts::s_stable_sort() {
    return s_sort(this->data, this->num);
}
/***************************************************
**名称:s_stable_sort
**参数:data:原始数组,num:元素个数
**返回:指向有序数组的指针
**功能:拷贝,排序,返回指针
**性能:最坏:O(n(logn)^2),保证最坏情况
**注意:标准库函数,稳定排序
****************************************************/
int* Cpp_Sorts::s_stable_sort(int const* data, int const num) {
    //创建副本
    int* arr = new int[num];
    memcpy(arr, data, sizeof(int) * num);

    //排序
    /*int*beg=std::begin(arr);//begin()可以直接对于数组元素使用,但是不能动态数组使用
    int* end = end(arr);*/
    //对数组使用迭代器的方式,尾部并不是最后一个元素,而是最后一个元素后的一个空元素
    std::stable_sort(&arr[0], &arr[num]);
    return arr;
}

7.冒泡排序 [bubble sort]

描述:两两比较选择大者依次排列在最后[设定判定标志]

  1. 比较相邻的元素。如果第一个比第二个大,就交换他们两个。
  2. 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。
  3. 针对所有的元素重复以上的步骤,除了最后一个。
  4. 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
/***************************************************
**名称:s_bubble
**参数:无
**返回:指向有序数组的指针
**功能:
**性能:
**注意:
****************************************************/
int* Cpp_Sorts::s_bubble() {
    return s_bubble(this->data, this->num);
}
/***************************************************
**名称:s_bubble
**参数:data:原始数组,num:数组长度
**返回:指向有序数组的指针
**功能:拷贝,排序,返回指针
**性能:最好:O(n)[改进之后],平均:O(n^2),最坏:O(n^2)
**注意:可以添加标记位,标记是否需要排序。[一般都是需要的]
**注意:数据少的时候性能还是不错的
****************************************************/
int* Cpp_Sorts::s_bubble(int const* data, int const num) {
    int* arr = new int[num];
    memcpy(arr, data, sizeof(int) * num);

    //sort
    //冒泡排序需要两次循环
    //第一重循环用来缩减前段待排序列
    //第二循环用来将大元素交换到最后
    //version_1,习惯使用版本一,方便书写。
    for (int i = 0; i < num - 1; i++)
        for (int j = 0; j < num - 1 - i; j++)
            if (arr[j] > arr[j + 1])
                arr[j] ^= arr[j + 1] ^= arr[j] ^= arr[j + 1];
    //version_2
    /*bool exchange;//设置标记位,是否发生了交换
    for (int i = 0; i < num - 1; i++) {
        exchange = false;
        for (int j = 0; j < num - 1 - i; j++) {
            if (arr[j] > arr[j + 1]) {
                arr[j] ^= arr[j + 1] ^= arr[j] ^= arr[j + 1];
                exchange = true;
            }
        }
        if (!exchange) break;//如果没有发生交换,就直接退出
    }*/
    return arr;
}

8.鸡尾酒排序 [cocktail sort]

描述:相邻元素成对比较,确定大小两值置于两端

  1. 比较相邻的元素。如果第一个比第二个大,就交换他们两个。
  2. 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。
  3. 针对所有的元素重复以上的步骤,除了最后一个。
  4. 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
/***************************************************
**名称:s_cocktail
**参数:无
**返回:指向有序数组的指针
**功能:
**性能:
**注意:
****************************************************/
int* Cpp_Sorts::s_cocktail() {
    return s_cocktail(this->data, this->num);
}
/***************************************************
**名称:s_cocktail
**参数:data:原始数组,num:数组长度
**返回:指向有序数组的指针
**功能:拷贝,排序,返回
**性能:最好:O(n),平均:O(n^2),最坏:O(n^2)
**注意:
****************************************************/
int* Cpp_Sorts::s_cocktail(int const* data, int const num) {
    //实质上是一个双向的冒泡排序
    int* arr = new int[num];
    memcpy(arr, data, sizeof(int) * num);

    //sort
    //1.先从左到右,两两进行对比,将大的元素浮动到最右侧,记录下右边界
    //2.再从右到左,两两进行对比,将小的元素浮动到最左侧,记录下左边界
    //3.直到某次不发生交换,就返回
    int left = 0, bounder = 0, right = num - 1;//标记边界的bound是必须的,不能直接使用right or left
    bool swepped = true;//如果没有发生过交换,可以直接返回
    while (swepped) {
        swepped = false;
        for (int i = left; i < right; i++) {
            if (arr[i] > arr[i + 1]) {
                arr[i] ^= arr[i + 1] ^= arr[i] ^= arr[i + 1];
                swepped = true;
                bounder = i;//每次标记右边界,bounder现在刚好是小元素的位置
            }
        }
        right = bounder;//更新右边界,这里都是刚好的,不用进行加减等麻烦的操作

        for (int i = right; i > left; i--) {
            if (arr[i] < arr[i - 1]) {
                arr[i] ^= arr[i - 1] ^= arr[i] ^= arr[i - 1];
                swepped = true;
                bounder = i;//每次标记左边界,bounder现在刚好是大元素的位置
            }
        }
        left = bounder;//更新左边界
    }
    return arr;
}

9.地精排序 [gnome sort]

描述:设置标识,前进冒泡一次后折返,循环到达元素末尾时终止

  1. 只有一层循环,默认情况下前进冒泡
  2. 一旦遇到冒泡的情况发生就往回冒,直到把这个数字放好为止
/***************************************************
**名称:s_gnome
**参数:无
**返回:指向有序数组的指针
**功能:
**性能:
**注意:
****************************************************/
int* Cpp_Sorts::s_gnome() {
    return s_gnome(this->data, this->num);
}
/***************************************************
**名称:s_gnome
**参数:data:原始数组,num:数组长度
**返回:指向有序数组的指针
**功能:拷贝,排序,返回指针
**性能:最好:O(n),平均:O(n^2),最坏O(n^2)
**注意:
****************************************************/
int* Cpp_Sorts::s_gnome(int const* data, int const num) {
    int* arr = new int[num];
    memcpy(arr, data, sizeof(int) * num);

    //gnome
    //1.顺序向后,两两对比,直到遇见更小的元素
    //2.从该元素开始往回冒泡,直到将其放到正确的位置
    //3.再从该位置向后走,如遇类似情形就重复1/2操作
    int i = 0;//没有必要在语法上进行缩减,而应还从算法上进行精化
    while (i < num) {
        if (i == 0 || arr[i - 1] <= arr[i]) {
            i++;//用于退出的量,只要是向右走,就要进行加·1
        }
        else {
            arr[i - 1] ^= arr[i] ^= arr[i - 1] ^= arr[i];
            i--;//向左走的就要减1
        }
    }
    return arr;
}

10.希尔排序 [shell sort]

描述:视作二维矩阵逐步压缩行高

  1. 选择一个增量序列 t1,t2,……,tk,前者大于后者,tk = 1;[可以反过来定义,算法需要修改]
  2. 按增量序列个数 k,对序列进行 k 趟排序;
  3. 每趟排序,根据对应的增量 ti,将待排序列分割成若干长度为 m 的子序列,分别对各子表进行直接插入排序。
  4. 仅增量因子为 1 时,整个序列作为一个表来处理,表长度即为整个序列的长度。
/***************************************************
**名称:s_shell
**参数:无
**返回:指向有序元素的指针
**功能:随机使用两种不同的希尔排序
**性能:
**注意:
****************************************************/
int* Cpp_Sorts::s_shell() {
    srand(time(0));
    if (rand() % 2 == 0)
        return s_shell_1(this->data, this->num);
    else
        return s_shell(this->data, this->num);
}
/***************************************************
**名称:s_shell
**参数:data:原始数组,num:数组长度
**返回:指向有序数组的指针
**功能:进行希尔排序
**性能:平均:O(n^(7/6)),最坏:O(n^(4/3)),[斐波那契数列]:O(n^(3/2))
**注意:有一部分人会自己规定gap序列Wie斐波那契数列,似乎是效率比较好
****************************************************/
int* Cpp_Sorts::s_shell(int const* data, int const num) {
    int* arr = new int[num];
    memcpy(arr, data, sizeof(int) * num);

    //shell_sort
    //每一次gap都变为原来的1/2
    //loop-1:控制gap变小
    for (int gap = num / 2; gap > 0; gap /= 2) {
        //loop-2:控制插入排序的第一轮循环
        for (int i = gap; i < num; i++) {
            int j = i;//控制组
            //也算是插入排序,基于交换的
            //也有点像是冒泡排序,不过这个是向前的
            //可以理解为向前冒泡,或者是不完整的插入排序
            //loop-3:控制插入排序
            while (j - gap >= 0 && arr[j] < arr[j - gap]) {
                arr[j] ^= arr[j - gap] ^= arr[j] ^= arr[j - gap];
                j -= gap;
            }
        }
    }
    return arr;
}
/***************************************************
**名称:s_shell
**参数:data:原始数组,num:数组长度
**返回:指向有序数组的指针
**功能:进行希尔排序
**性能:平均:O(n^(7/6)),最坏:O(n^(4/3)),[斐波那契数列]:O(n^(3/2))
**注意:有一部分人会自己规定gap序列Wie斐波那契数列,似乎是效率比较好
****************************************************/
int* Cpp_Sorts::s_shell_1(int const* data, int const num) {
    int* arr = new int[num];
    memcpy(arr, data, sizeof(int) * num);

    //shell sort: version-1
    //大致流程与上面都是一样的
    int gap = 1;
    while (gap < num / 3)
        gap = gap * 3 + 1;
    while (gap >= 1) {//上一种方法这里是for循环,用的是while循环,gap>0等效于gap>=1
        for (int i = gap; i < num; i++) {
            //上面用的是while循环进行的插入排序
            //这里使用的是for循环进行插入排序
            //多一种写法,看着比较牛逼,其实是一个道理
            for (int j = i; j >= gap && arr[j] < arr[j - gap]; j -= gap) {
                arr[j] ^= arr[j - gap] ^= arr[j] ^= arr[j - gap];
            }
        }
        gap /= 3;
    }
    return arr;
}

11.双调排序【非递归】 [bitonic sort]

描述:[可以并行,效率会大大的提高。只能处理2^n个数的数据]

/***************************************************
**名称:s_bitonic
**参数:无
**返回:指向有序数组的指针
**功能:
**性能:
**注意:
****************************************************/
int* Cpp_Sorts::s_bitonic() {
    return s_bitonic(this->data, this->num);
}
/***************************************************
**名称:s_bitonic
**参数:data:原始数组,num:数组长度
**返回:指向有序数组的指针
**功能:进行非递归的双调排序
**性能:平均:O(n(logn)^2)[串行]  
**注意:双调排序可以串行处理,在多机器的情况下效率是很高的
****************************************************/
int* Cpp_Sorts::s_bitonic(int const* data, int const num) {
    int* arr = new int[num];
    memcpy(arr, data, sizeof(int) * num);

    bool up = true;
    //将普通的无序序列转化为双调序列
    for (int step = 2; step < num; step *= 2) {
        for (int i = 0; i < num; i += 2 * step) {
            //事实上这两处的顺序并不重要,只要满足双调序列的定义即可
            //双调数列的构建过程:
            //2(1),4(2,1),8(4,2,1),16(8,4,2,1)
            //括号里面每次都是需要逐渐深入的
            //代码需要时常观摩
            s_bitonic(arr + i, step, up);//前半段递增
            s_bitonic(arr + step + i, step, !up);//后半段递减
        }
    }
    return s_bitonic(arr, num, up);//对双调序列进行排序,而后返回
}
/***************************************************
**名称:s_bitonic
**参数:arr:数组,num:元素个数,up:是否递增
**返回:满足双调序列定义的数组,或有序数组
**功能:1.构建双调序列 2.对于双调序列进行排序
**性能:
**注意:双调序列概念
****************************************************/
int* Cpp_Sorts::s_bitonic(int* arr, int const num, bool up) {
    //bitonic_sort
    //默认升序排列
    //可以对照参考链接仔细理解这里的
    //构建双调序列的时候,这里的num是逐渐扩倍的
    for (int step = num / 2; step > 0; step /= 2) {//步长缩减
        for (int i = 0; i < num; i += 2 * step) {//第i组元素
            for (int j = 0; j < step; j++) {//第i组元素与第i+1组元素的比较
                if (up) {//是否是增序
                    if (arr[i + j] > arr[i + j + step])//前者大,则与后者换位
                        arr[i + j] ^= arr[i + step + j] ^= arr[i + j] ^= arr[i + step + j];
                }
                else {//如果是降序
                    if (arr[i + j] < arr[i + j + step])//前者小,则与后者换位
                        arr[i + j] ^= arr[i + step + j] ^= arr[i + j] ^= arr[i + step + j];
                }
            }
        }
    }
    return arr;
}

12.双调排序【递归】 [recursive bitonic sort]

/***************************************************
**名称:s_bitonic_rec
**参数:无
**返回:指向有序数组的指针
**功能:统一接口
**性能:
**注意:
****************************************************/
int* Cpp_Sorts::s_bitonic_rec() {
    return s_bitonic_rec(this->data, this->num);
}
/***************************************************
**名称:s_bitonic_rec
**参数:data:原始数组,num:元素个数
**返回:指向有序数组的指针
**功能:次级接口
**性能:
**注意:先进行数据拷贝
****************************************************/
int* Cpp_Sorts::s_bitonic_rec(int const* data, int const num) {
    int* arr = new int[num];
    memcpy(arr, data, sizeof(int) * num);

    bool up = true;//升序
    return s_bitonic_rec(arr, num, up);//调用双调排序
}
/***************************************************
**名称:s_bitonic_get_len
**参数:len:长度
**返回:参与双调排序的数据长度
**功能:找一个2的次幂,让它刚好比len小
**性能:
**注意:任何数字都可以拆成多2的幂级表达式的和(etc:15=2^3+2^2+2^1+2^0)
****************************************************/
int Cpp_Sorts::s_bitonic_get_len(int const len) {
    int k = 1;
    while (k < len) k = k << 1;//满足条件,扩大两倍
    return k >> 1;//上面循环最后一次运行,多扩大了一次,这里应该除下来
}
/***************************************************
**名称:s_bitonic_rec
**参数:arr:数组,len:元素个数,up:是否增序
**返回:指向有序数组的指针
**功能:递归双调排序
**性能:平均:O(n(logn)^2)[串行]  
**注意:前半段降序,后半段升序
****************************************************/
int* Cpp_Sorts::s_bitonic_rec(int* arr, int const len, bool up) {
    if (len > 1) {
        int m = len / 2;
        //以下的强制顺序与递归的流程是有关系的
        //将整个流程走一遍,如果最后要求的是增序,在递归情况下为满足双调序列的定义,必须先降后增
        //如果最后要求的是降序排列,这里同样也需要先降后增
        //改变了顺序,程序就会输出错误的结果
        //之前非递归版本的不要求这样做,原因未知
        s_bitonic_rec(arr, m, !up);//前半段降序,递归要求一定要这么做,why?
        s_bitonic_rec(arr + m, len - m, up);//后半段升序
        //回收并不是所有的递归都结束之后才运行的
        //从最后一次不符合条件的递归开始,这个归并机制就已经开始起作用了
        //最深层的递归,逐渐向上调用这个归并机制
        s_bitonic_rec_merge(arr, len, up);//归并机制
    }
    return arr;
}
/***************************************************
**名称:s_bitonic_rec_merge
**参数:arr:数组,len:元素个数,up:是否增序
**返回:无
**功能:对于任意双调序列进行归并
**性能:
**注意:
****************************************************/
void Cpp_Sorts::s_bitonic_rec_merge(int* arr, int const len, bool up) {
    //任意双调排序的归并
    //此函数即能用来生成双调函数,也能用来归并,非常的经典
    if (len > 1) {
        int m = s_bitonic_get_len(len);//前半段的长度
        for (int i = 0; i < len - m; i++) {//源码中写的额是++i,其实并不影响,for循环中第一次使用的是初始化的0
            if (arr[i] > arr[i + m] == up)//如果是升序,会对交换两者,如果是降序,后者大,也需要对两者进行交换
                arr[i] ^= arr[i + m] ^= arr[i] ^= arr[i + m];//上面的判断条件很将就
        }
        s_bitonic_rec_merge(arr, m, up);//对于前半段递增归并,或者是递减,与下面的保持一致
        s_bitonic_rec_merge(arr + m, len - m, up);//对于后半段也递增,或者是递减
    }
}

13.猴子排序 [bogo sort]

描述:随缘吧!

/***************************************************
**名称:s_bogo
**参数:无
**返回:指向有序元素的指针
**功能:统一入口
**性能:
**注意:
****************************************************/
int* Cpp_Sorts::s_bogo() {
    return s_bogo(this->data, this->num);
}
/***************************************************
**名称:s_get_dif_m_shu
**参数:index:递增数组,num:元素个数
**返回:指向打乱顺序的数组的指针
**功能:打乱递增数组的元素顺序
**性能:
**注意:
****************************************************/
int* Cpp_Sorts::s_get_dif_m_shu(int* index, int const num) {
    std::random_device rd;
    std::mt19937 g(rd());
    shuffle(&index[0], &index[num], g);
    return index;
}
/***************************************************
**名称:s_bogo_is_order
**参数:arr:数组,index:下标数组,num:元素个数
**返回:是否有序bool类型值
**功能:根据index数组,判断arr数组中的元素是否有序
**性能:
**注意:
****************************************************/
bool Cpp_Sorts::s_bogo_is_order(int const* arr, int const* index, int const num) {
    //比如,arr[3,5,1,2],index[2,3,0,1]
    //将index中的元素作为arr的索引,可以有arr[2]<arr[3]<arr[0]<arr[1]
    //此时,根据index数组判断arr数组的元素是有序的
    for (int i = 1; i < num; i++) {
        if (arr[index[i]] < arr[index[i - 1]])
            return false;
    }
    return true;
}
/***************************************************
**名称:s_bogo——print
**参数:arr:数组,num:元素个数
**返回:无
**功能:打印
**性能:
**注意:就是想简单的输出一下,调试的时候使用的
****************************************************/
void Cpp_Sorts::s_bogo_print(int const* arr, int num) {
    for (int i = 0; i < num; i++)
        cout << arr[i] << ' ';
    cout << endl;
}
/***************************************************
**名称:s_bogo
**参数:data:数组,num:元素个数
**返回:指向有序数组的指针
**功能:模拟猴子按键的过程,每次不同的键位,看这些键位是否有序
**性能:极差
**注意:元素个数>10的时候不建议使用
****************************************************/
int* Cpp_Sorts::s_bogo(int const* data, int const num) {
    int* arr = new int[num];
    memcpy(arr, data, sizeof(int) * num);

    int* index = new int[num];//不知道这一部分空间应该在哪里删除掉
    iota(&index[0], &index[num], 0);//对数组进行递增的初始化,从0开始,每次+1
    index = s_get_dif_m_shu(index, num);//打乱下标数组中的元素
    while (!s_bogo_is_order(arr, index, num)) {//判断是否有序
        index = s_get_dif_m_shu(index, num);//若无序,就继续打乱
        //s_bogo_print(index, num);//打印猴子按键的过程
    }
    arr = s_bogo(data, index, num);
    return arr;
}
/***************************************************
**名称:s_bogo
**参数:arr:数组,index:下标数组,num:元素个数
**返回:指向有序数组的指针
**功能:根据index中的下标依次取arr中的元素,并回放index数组中
**性能:
**注意:这里取巧,让index数组充当了有序数组容器,并将其返回
****************************************************/
int* Cpp_Sorts::s_bogo(int const* arr, int* index, int const num) {
    for (int i = 0; i < num; i++) {
        index[i] = arr[index[i]];
    }
    return index;
}

14.归并排序 [merge sort]

这里需要一个非递归的版本,下次写好了之后添加上。

描述:2048[递归和迭代两个版本]

  1. 申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列;
  2. 设定两个指针,最初位置分别为两个已经排序序列的起始位置;
  3. 比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置;
  4. 重复步骤 3 直到某一指针达到序列尾;
  5. 将另一序列剩下的所有元素直接复制到合并序列尾。
/***************************************************
**名称:s_merge_m
**参数:arr:数组,left:左下标,right:右下标,mid:中间下标
**返回:无
**功能:对于[left,right]之间的元素分两部分进行归并
**性能:
**注意:
****************************************************/
void Cpp_Sorts::s_merge_m(int* arr, int const left, int const right, int const mid) {
    int* temp = new int[right - left + 1];
    int i = 0, p1 = left, p2 = mid + 1;
    //取小的先放
    while (p1 <= mid && p2 <= right) {
        temp[i++] = arr[p1] < arr[p2] ? arr[p1++] : arr[p2++];
    }
    //第一部分没放完的放置在后面
    while (p1 <= mid) {
        temp[i++] = arr[p1++];
    }
    //第二部分没放完的继续向后放
    while (p2 <= right) {
        temp[i++] = arr[p2++];
    }
    //将归并好的部分,拷贝给原来的数组
    memcpy(arr + left, temp, sizeof(int) * (right - left + 1));
    delete[] temp;//清除申请的内存
}
/***************************************************
**名称:s_merge_rec
**参数:arr:数组,left:左下标,right:右下标
**返回:无
**功能:递归进行归并排序
**性能:最好:O(nlogn),平均:O(nlogn),最坏:O(nlogn)
**注意:递归的两部分不要重复
****************************************************/
void Cpp_Sorts::s_merge_rec(int* arr, int const left, int const right) {
    if (left == right) return;
    int mid = (left + right) / 2;
    //思想是很简单的,但实际上这是一个递归的过程
    s_merge_rec(arr, left, mid);//对于左边递归
    s_merge_rec(arr, mid + 1, right);//对于右边进行递归,一定要+1确保不会重复
    s_merge_m(arr, left, right, mid);//如果两边都好了,就开始进行合并
}
/***************************************************
**名称:s_merge
**参数:data:原始数据,num:元素个数
**返回:指向有序数组的指针
**功能:调用归并排序
**性能:
**注意:
****************************************************/
int* Cpp_Sorts::s_merge(int const* data, int const num) {
    int* arr = new int[num];
    memcpy(arr, data, sizeof(int) * num);
    s_merge_rec(arr, 0, num - 1);
    return arr;
}
/***************************************************
**名称:s_merge
**参数:无
**返回:指向有序数组的指针
**功能:统一入口
**性能:
**注意:
****************************************************/
int* Cpp_Sorts::s_merge() {
    return s_merge(this->data, this->num);
}

15.LSD排序 [LSD sort]

描述:最低位优先批次进行排序,最后读取序列

  1. 首先根据个位数的数值,在走访数值时将它们分配至编号0到9的桶子中:
  2. 接着再进行一次分配,这次是根据十位数来分配:
  3. 接下来将这些桶子中的数值重新串接起来,成为以下的数列:
  4. [位数变多则多进行几步]
/***************************************************
**名称:s_LSD
**参数:无
**返回:指向有序数组的指针
**功能:
**性能:
**注意:
****************************************************/
int* Cpp_Sorts::s_LSD() {
    return s_LSD(this->data, this->num);
}
/***************************************************
**名称:s_LSD
**参数:data:数组,num:元素个数
**返回:指向有序数组的指针
**功能:使用LSD对数组进行排序
**性能:最好:O(d(n+rd)),平均:O(d(n+r)),最坏:O(d(n+r))
**注意:
****************************************************/
int* Cpp_Sorts::s_LSD(int const* data, int const num) {
    int* arr = new int[num];
    memcpy(arr, data, sizeof(int) * num);
    int digit = s_LSD_get_d(arr, num);  //获取最高位的位数
    queue<int>* p_queue[10];            //指向队列的数组,指针数组,有10个queue<int>的指针,相当于算法中的十个桶
    for (int i = 0; i < 10; i++) {      //同时需要进行初始化
        p_queue[i] = new queue<int>;
    }

    int* temp = new int[num];           //出桶的时候临时存储数据
    for (int d = 1; d <= digit; d++) {  //每一位都要进行比较
        //入桶
        for (int i = 0; i < num; i++) { //进行一趟个位的排序,根据第n位进行入桶
            p_queue[arr[i] / (int)pow(10, d - 1) % 10]->push(arr[i]);
        }
        //出桶
        for (int i = 0, count = 0; i < 10; i++) {
            while (!p_queue[i]->empty())
            {
                temp[count++] = p_queue[i]->front();    //获取第一个元素
                p_queue[i]->pop();                      //第一个元素出队列
            }
        }
        //交换两个指针
        int* temp_2;
        temp_2 = arr;
        arr = temp;//保证每一次的arr都是经过排列的
        temp = temp_2;
    }
    delete[] temp;//清空临时指针指向的空间
    return arr;
}
/***************************************************
**名称:s_LSD_get_d
**参数:arr:数组,num:元素个数
**返回:数组中最大元素的位数
**功能:求数组中最大元素的位数
**性能:
**注意:同下面的s_MSD_get_d
****************************************************/
int Cpp_Sorts::s_LSD_get_d(int const* arr, int const num) {
    int max = arr[0];
    int digit = 1;
    for (int i = 1; i < num; i++) {
        if (arr[i] > max)
            max = arr[i];
    }
    while (max /= 10) {
        ++digit;
    }
    return digit;
}

16.MSD排序 [MSD sort]

描述:高位分桶,桶内递归

  1. 先根据最高位关键码K1排序,得到若干对象组,对象组中每个对象都有相同关键码K1。
  2. 再分别对每组中对象根据关键码K2进行排序,按K2值的不同,再分成若干个更小的子组,每个子组中的对象具有相同的K1和K2值。
  3. 依此重复,直到对关键码Kd完成排序为止。
  4. 最后,把所有子组中的对象依次连接起来,就得到一个有序的对象序列。
/***************************************************
**名称:s_MSD
**参数:无
**返回:指向有序数组的指针
**功能:随机使用一种s_MSD算法
**性能:
**注意:不同的初始化方式
****************************************************/
int* Cpp_Sorts::s_MSD() {
    //随机使用一下两种中的一种
    //数据拷贝,有序s_MSD算法使用递归,会对数据自身进行调整,因此需要在这里进行数据拷贝
    int* arr = new int[this->num];
    memcpy(arr, this->data, sizeof(int) * this->num);

    int digit = s_LSD_get_d(arr, this->num);//获取最大数字的位数
    srand(time(0));
    int x = rand() % 2;
    x = 10;
    if (x == 0) {
        int* arr_back = new int[this->num];
        int count = 0;
        s_MSD(arr, this->num, digit, arr_back, count);
        return arr_back;
    }
    else {
        vector<int> A(arr, arr + num);//初始化一个向量
        s_MSD(A, digit);
        for (int i = 0; i < num; i++) {//排序结束之后将向量中的元素放入数组之中并且返回
            arr[i] = A[i];
        }
        return arr;
    }
}
/***************************************************
**名称:s_MSD
**参数:arr:数组,num:数组元素个数,digit:位数,arr_back:用于回收的数组,count:用于记录回收了几个元素
**返回:无
**功能:对arr进行递归的MSD算法,将回收的元素放入arr_back数组之中并返回
**性能:最好:O(d(n+rd)),平均:O(d(n+r))[r为基数,d为位数][n是放一趟的次数,r为收集一趟的次数],最坏:O(d(n+r))
**备注:关于性能的描述并不是特别懂
**注意:count一定要传引用,内存的清理
****************************************************/
void Cpp_Sorts::s_MSD(int* arr, int const num, int const digit, int* arr_back, int& count) {
    //最终的元素应该如何收集
    //这里的递归不太会使用
    //自己写的版本,不能使用arr自身进行数据的回收
    //要么使用vector<vector<int>>,要么使用二维数组,都可以正确使用arr自身进行数据的回收
    queue<int>* p_queue[10];            //十个桶
    for (int i = 0; i < 10; i++)        //对这十个桶进行初始化
        p_queue[i] = new queue<int>;

    for (int i = 0; i < num; i++)       //按照最高位入桶
        p_queue[arr[i] / (int)pow(10, digit - 1) % 10]->push(arr[i]);

    for (int i = 0; i < 10; i++) {      //对于十个桶进行遍历
        int size = p_queue[i]->size();
        if (size == 1) {                //如果size等于1说明这个桶里面只有一个元素,直接放入arr_back中即可
            arr_back[count++] = p_queue[i]->front();//回收
        }
        else if (size > 1) {
            int* arr_b = new int[size]; //新的待排序的数组,将现在这个桶里的元素放入其中
            for (int j = 0; j < size; j++) {
                arr_b[j] = p_queue[i]->front();
                p_queue[i]->pop();
            }
            s_MSD(arr_b, size, digit - 1, arr_back, count);//对于元素个数大于1的桶进行递归
            delete[] arr_b;             //清空之前arr_b申请的空间,清理内存
        }
    }
}
/***************************************************
**名称:s_MSD
**参数:vec:待排序向量,digit:最大元素的位数
**返回:无
**功能:对vec向量递归排序
**性能:最好:O(d(n+rd)),平均:O(d(n+r))[r为基数,d为位数][n是放一趟的次数,r为收集一趟的次数],最坏:O(d(n+r))
**注意:推荐这种算法,比较精妙
****************************************************/
void Cpp_Sorts::s_MSD(vector<int>& vec, int digit) {
    int len = vec.size();
    vector<vector<int>> p_vector(10);//声明10个桶,并进行初始化

    if (digit >= 1 && len > 1) {//如果位数不为0,而且桶中的元素个数大于1个
        for (int i = 0; i < len; i++)//对于桶中的元素进行分配
            p_vector[vec[i] / (int)pow(10, digit - 1) % 10].push_back(vec[i]);

        for (int i = 0, j = 0; i < 10; i++) {
            //递归进行排序
            s_MSD(p_vector[i], digit - 1);//上面判断过桶中元素个数是否>1,这里如果不满足,就会跳出该轮递归,进行下面的语句
            //加入之前有一个桶中的元素是(23,24,21),经过分配之后会变成{(21),(23),(24)}
            //然后对这三个元素调用s_MSD,都会被阻断,然后进入下面的回收机制中
            //vec是原来有三个元素的桶,经过分配之后它已经为空,p_vector[i]每一个桶
            //21,23,24会依次进入原来的vec桶中,它大小未变,但vec中的元素已经有序
            //当前vec所处的桶序列回收结束之后,又会进入上一级递归
            while (!p_vector[i].empty()) {//单次回收的时候都是对的
                vec[j++] = p_vector[i].front();
                p_vector[i].erase(p_vector[i].begin());//回收一个元素,释放一个元素
            }
        }
    }
}
/***************************************************
**名称:s_MSD_get_d
**参数:arr:数组,num:元素个数
**返回:数组中最大元素的位数
**功能:求数组中最大元素的位数
**性能:
**注意:
****************************************************/
int Cpp_Sorts::s_MSD_get_d(int const* arr, int const num) {
    int max = arr[0];
    int digit = 1;
    for (int i = 1; i < num; i++) {//找出最大元素
        if (arr[i] > max)
            max = arr[i];
    }
    while (max /= 10) {//除10不为零,说明有了有了一位
        ++digit;//鉴于while循环的先判断性质,满足条件就直接先加上
    }
    return digit;
}

P3:相关链接 [relative link]

1.参考链接 [reference link]

1.C++ 随机生成区间范围内的随机数
2.标准库头文件 <algorithm>
3.标准库头文件 <numeric>
4.C++11标准库chrono库使用
5.基于C++11 chrono库的计时器
6.How to call a function by its name (std::string) in C++?
7.c++ 成员函数指针
8.error C3867:非标准语法;请使用“&”来创建指向成员的指针
9.error C3867:非标准语法;请使用“&”来创建指向成员的指针
10.c++基础之vector、数组初始化
11.【排序算法】基数排序:LSD 与 MSD
12.基数排序(LSD+MSD)详解
13.const 和 非const函数重载
14.C++ 求幂的运算符是什么?
15.c++中的运算符优先级
16.怎样快速提取一个数字中的每一位数
17.c++指针可以指向栈吗?
18.指针数组和数组指针的区别
19.C++ 标准库中的堆(heap)
20.分治法之归并排序三种实现方法(递归、非递归和自然合并排序)
21.基数排序(LSD)
22.十大经典排序算法(动图演示)
23.[图解] 归并排序
24.C++ iota()函数
25.std::shuffle-c++
26.C++ 11中的随机排列函数shuffle
27.网易有道编程题:洗牌算法(C++)
28.猴子排序法的实际意义?
29.排序算法——猴子排序(Bogosort)【代码实现】
30.Java排序 - 不实用的几个排序算法 – 睡眠排序、猴子排序、面条排序、珠排序
31.C++洗牌算法
32.栈的应用:栈混洗
33.栈混洗的概念
34.VS中展开和折叠代码,还有其他快捷操作
35.三十分钟理解:双调排序Bitonic Sort,适合并行计算的排序算法
36.【并行计算】Bitonic Sort(双调排序)基础
37.双调排序
38.五分钟学会一个高难度算法:希尔排序
39.指针常量和常量指针

2.参考文档 [reference doc]

1.cppreference.com
2.c++ reference oschina
3.cplusplus.com
4.c++ 中文手册 51yip
5.游戏蛮牛C++手册
6.C++ 教程 runoob
7.C++ References
8.C++ Tutorial w3schools
9.C++ Tutorial geeksforgeeks

3.在线编译 [online editor]

推荐使用前十个,最后一个,多数都不用上外网,前两个是菜鸟教程上的,【4】很简洁,【6】没有用过,似乎能建立工程,界面简单而且没有广告。

1.runoob.com
2.w3cschool.cn
3.godbolt.org
4.cpp.sh
5.ideone.com
6.winfengtech.com
7.c++ 代码测试
8.tool.lu/coderunner
9.wandbox.org
10.onlinegdb.com
11.jdoodle.com
12.tutorialspoint.com
13.codechef.com
14.codiva.io
15.geeksforgeeks
16.coliru
其他:
1.程序员专用十大在线编译器(IDE)整理
2.List of Online C++ Compilers

4.相关论坛 [relative forums]

1.https://isocpp.org/
2.CSDN C++论坛
3.stack overflow
4.综合型编程论坛
5.吾爱破解 - 编程语言区

5.给我写信 [write e-mail]

邮箱:[email protected]
写信地址:✉✉✉✉

6.下载资源 [download code]

下载:Cpp_Sort.zip

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章