在考研中,经常会考到各种排序算法,所以总结一下,以便温习。

各类排序的时刻复杂度和空间复杂度概览;

考研数据结构--排序算法汇总,代码完好可完成

1. 直接刺进排序

其根本操作是将一条记载刺进到已排好的有序表中,然后得到一个新的、记载数量增1的有序表。

特性

  1. 时刻复杂度
  • 最好状况便是悉数有序,此刻只需遍历一次,最好的时刻复杂度为 O(n)
  • 最坏状况悉数反序,内层每次遍历已排序部分,最坏时刻复杂度为 O(n^2)
  • 综上,因而直接刺进排序的均匀时刻复杂度为 O(n^2)

2.空间复杂度

  • 辅佐空间是常量
  • 均匀的空间复杂度为:O(1)

3.算法安稳性

  • 判别规范:相同元素的前后次序是否改动
  • 刺进到比它大的数前面,所以直接刺进排序是安稳的

4. 完好可完成代码

#include <bits/stdc  .h>
using namespace std;
// 直接刺进排序
// 其根本操作是将一条记载刺进到已排好的有序表中,然后得到一个新的、记载数量增1的有序表
void InsertSort(int a[], int l) {
    int temp;
    int j;
    // 先看榜首个数,将数组划分为有序和无序部分,榜首个数默许有序。
    for (int i = 1; i < l; i  ) {
        // 取出无序部分的首个,在有序部分从后向前比较,刺进到合适的方位
        if (a[i] < a[i - 1]) {
            temp = a[i];  // temp暂存需求刺进的数
            for (j = i - 1; j >= 0 && temp < a[j]; j--) {
                a[j   1] = a[j];  // 取出原序列中大于暂存的数,往后移
            }  // 循环完毕后,现已找到需求刺进元素的方位
            a[j   1] = temp;  // 将该方位刺进元素
        }
        cout << "第" << i << "次刺进:";
        for (int k = 0; k < l; k  ) {
            cout << a[k] << " ";
        }
        cout << endl;
    }
}
int main() {
    int a[10] = {2, 5, 8, 3, 6, 9, 1, 4, 7};
    int b[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
    int len = 9;
    InsertSort(a, len);
    return 0;
}

2. 减半刺进排序

  1. 特性
  • 时刻复杂度:

减半查找只是减少了比较次数,但是元素的移动次数不变,所以时刻复杂度为O(n^2)

  • 空间复杂度

均匀的空间复杂度也是:O(1)

  1. 安稳性: 安稳的排序算法
#include <bits/stdc  .h>
using namespace std;
void BInsertSort(int a[], int l) {
    int temp;
    int low, high;
    int m;
    for (int i = 1; i < l; i  ) {
        // 取出无序部分的首个,在有序部分二分查找到方位
        if (a[i] < a[i - 1]) {
            low = 0;
            high = i - 1;
            // 在前面有序的部分进行二分查找
            while (low <= high) {
                m = low   (high - low) / 2;
                if (a[m] > a[i])  // 有序部分的中心数,大于无序部分的首个元素
                    high = m - 1;
                else
                    low = m   1;  // low的方位终究为小于等于a[i]的数
            }
            temp = a[i];  // 暂存需求刺进的元素
            // 遍历,将元素向右移,找到刺进的方位
            for (int j = i; j > low; j--) {
                a[j] = a[j - 1];
            }
            a[low] = temp;  // 在找到的方位进行赋值
        }
        cout << "第" << i << "次刺进:";
        for (int k = 0; k < l; k  ) {
            cout << a[k] << " ";
        }
        cout << endl;
    }
}
int main() {
    int a[10] = {2, 5, 8, 3, 6, 9, 1, 4, 7};
    int b[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
    int len = 9;
    BInsertSort(a, len);
    return 0;
}

3. 减半查找

也称二分查找,它是一种效率较高的查找办法。但是,减半查找要求线性表有必要选用次序存储结构,并且表中元素按关键字有序摆放。

#include <bits/stdc  .h>
using namespace std;
// 也称二分查找,它是一种效率较高的查找办法。但是,减半查找要求线性表有必要选用次序存储结构,并且表中元素按关键字有序摆放
int BinarySearch(int a[], int low, int high, int target) {
    // 终究low与high会兼并(注意兼并时也要比较),按照规律,此刻high会到low前面,这时才能够确定没有,所以这个循环的条件便是low<=high
    while (low <= high) {
        int mid = low   (high - low) / 2;  // 溢出问题
        cout << "low:" << low << " high:" << high << " mid:" << mid << endl;
        if (a[mid] >
            target) {  // 将两个数进行比较,然后缩小查找范围,。当中心数大于查找数时,往左面找
            high = mid - 1;
        } else if (a[mid] < target) {  // 中心数小于查找数时,往右边找
            low = mid   1;
        } else {  // 持平时,直接返回
            return mid;
        }
    }
    return -1;
}
int main() {
    int a[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
    int len = 9;
    cout << BinarySearch(a, 0, len - 1, 3) << endl;
    return 0;
}

4. 希尔排序

是刺进排序的一种又称“缩小增量排序”(Diminishing Increment Sort),是直接刺进排序算法的一种更高效的改善版本。

特性 1.时刻复杂度 对于这个算法有两个常见成果 O ( n^2 ) O(n^1.5)

2.空间复杂度 和直接刺进一样 均匀的空间复杂度为:O(1)

3.算法安稳性 相同元素的前后次序改动

#include <bits/stdc  .h>
using namespace std;
void ShellSort(int a[], int len) {
    int temp;
    int j;
    int gap = len / 2;
    int index = 0;  // 增加增量
    while (gap)     // 多组
    {
        for (int i = gap; i < len; i  )  // 直接从组内第二个判起
        {
            //  是否需求变,比方榜首步的2和9方位不需求改动
            if (a[i] < a[i - gap]) {
                index  ;
                cout << "第" << index << "次交流" << a[i] << "和" << a[i - gap]
                     << "后:";
                temp = a[i];  // 取出改动值,便是需求交流方位,更大的那个数
                // 需求交流方位的数,小于大的数,还要确保下标大于等于0,
                for (j = i - gap; j >= 0 && temp < a[j]; j = j - gap)  // 找方位
                {
                    a[j   gap] = a[j];  // 右移
                }
                a[j   gap] = temp;  // 刺进
                for (int k = 0; k < len; k  )
                    cout << a[k] << " ";
                cout << endl;
            }
        }
        gap /= 2;
    }
}
int main() {
    int a[10] = {2, 5, 8, 3, 6, 9, 1, 4, 7, 0};
    int b[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    int len = 10;
    ShellSort(a, len);
    return 0;
}

5. 冒泡排序

是一种简略的排序算法
由来是由于越小的元素会经由交流慢慢“浮”到数列的顶端(升序或降序摆放),就好像碳酸饮料中二氧化碳的气泡终究会上浮到顶端一样,故名“冒泡排序”。

特性

  1. 时刻复杂度
  • 最好状况便是悉数有序,此刻只需遍历一次,因而冒泡排序最好的时刻复杂度为O(n)
  • 最坏状况悉数反序,两重for循环悉数执行,因而冒泡排序的最坏时刻复杂度为 O(n^2)

综上,因而冒泡排序总的均匀时刻复杂度为O(n^2)

  1. 空间复杂度
  • 最优的空间复杂度便是不需求借用第三方内存空间,则复杂度为0
  • 最差的空间复杂度便是开端元素逆序排序,每次都要借用一次内存,按照实际的循环次数,为O(n)

均匀的空间复杂度为:O(1)

  1. 算法安稳性
  • 相同元素的前后次序是否改动, 由于只交流不同巨细的,所以冒泡排序是安稳的

考研数据结构--排序算法汇总,代码完好可完成

#include <iostream>
using namespace std;
void BubbleSort(int a[], int l) {
    int ans;
    int temp;
    int index;
    for (int i = l - 1; i >= 1; i--)  // 一重循环,n-1次
    {
        ans = 1;
        for (int j = 1; j <= i; j  )  // 第二个数到未排序数
        {
        //大的往后提
            if (a[j - 1] > a[j]) {
                ans = 0;  // 有未排序的
                //                temp = arr[j];
                //                arr[j] = arr[j   1];
                //                arr[j   1] = temp;
                swap(a[j], a[j - 1]);  // 交流
                cout << "第" <<   index << "次排序:";
                for (int k = 0; k < l; k  )  // 打印数组
                {
                    cout << a[k] << " ";
                }
                cout << endl;
            }
        }
        if (ans)  // 额定标记
            break;
    }
}
int main() {
    int a[20] = {2, 5, 8, 3, 6, 9, 1, 4, 7};
    int b[20] = {1, 1, 1, 1, 1, 1, 1, 1, 1};
    int len = 9, temp;
    BubbleSort(a, len);
    return 0;
}

6. 快速排序

快速排序的根本思想是:经过一次排序行将排序的数据分红两部分,其间一部分的一切数据都比另外一部分的一切数据都要小,然后,再按此办法对这两部分数据分别进行快速排序,直到有序。

  1. 算法设计

(1)分化: 先从数列中取出一个元素作为基准元素。以基准元素为规范,将问题分化为两个子序列,使小于或许等于基准元素的子序列在左侧,使大于基准元素的子序列在右侧。

(2)管理 : 对两个子序列进行快速排序(递归快速排序)。

(3)兼并: 将排好的两个子序列兼并在一起,得到原问题的解。

(4)基准元素的选取:

①:取榜首个元素。(通常选取榜首个元素)

②:取终究一个元素

③:取中心方位的元素

④:取榜首个、终究一个、中心方位元素三者之中位数

⑤:取榜首个和终究一个之间方位的随机数 k (low<=k<=hight)

  1. 快速排序的状况比较棘手,在最糟状况下,其运行时刻为O(n2)。在均匀状况下,快速排序的运行时刻为O(nlogn)。

考研数据结构--排序算法汇总,代码完好可完成


#include <bits/stdc  .h>
using namespace std;
// 快速排序(从小到大)
void quickSort(int left, int right, int arr[]) {
    if (left >= right)
        return;
    int i, j, base, temp;
    i = left,
    j = right;  // 把待排序数组元素的榜首个和终究一个下标分别赋值给i,j,运用i,j进行排序;
                // 将待排序数组的榜首个元素作为岗兵,将数组划分为大于岗兵以及小于岗兵的两部分
    base = arr[left];  // 取最左面的数为基准数
    while (i < j) {
        while (arr[j] >= base && i < j)
            j--;
        while (arr[i] <= base && i < j)
            i  ;
        if (i < j) {
            temp = arr[i];
            arr[i] = arr[j];
            arr[j] = temp;
        }
    }
    // 基准数归位
    arr[left] = arr[i];
    arr[i] = base;
    quickSort(left, i - 1, arr);   // 递归左面
    quickSort(i   1, right, arr);  // 递归右边
}
void print(int a[], int n) {
    for (int j = 0; j < n; j  ) {
        cout << a[j] << "  ";
    }
    cout << endl;
}
int main() {
    int a[10] = {8, 1, 9, 7, 2, 4, 5, 6, 10, 3};
    cout << "初始序列:";
    print(a, 10);
    quickSort(0, 9, a);
    cout << "排序成果:";
    print(a, 10);
    return 0;
}

7. 简略选择排序

特性

1.时刻复杂度:O(n^2)

2.空间复杂度:均匀的空间复杂度为:O(1)

3.算法安稳性: 不安稳

考研数据结构--排序算法汇总,代码完好可完成
可完成代码

#include <bits/stdc  .h>
using namespace std;
void SelectSort(int a[], int len) {
    int k;
    for (int i = 0; i < len - 1; i  )  // 未排序榜首个数
    {
        k = i;                             // 取榜首个数
        for (int j = i   1; j < len; j  )  // 遍历未排序部分
        {
            if (a[j] < a[k]) {
                k = j;  // 取更小
            }
        }
        if (i != k)  // 不在未排序榜首的方位
        {
            swap(a[k], a[i]);
        }
        cout << "第" << i   1 << "次排序后";
        for (int t = 0; t < len; t  )
            cout << a[t] << " ";
        cout << endl;
    }
};
int main() {
    int a[9] = {2, 7, 8, 3, 7, 9, 1, 3, 7};
    int len = 9;
    SelectSort(a, len);
    return 0;
}

8. 堆排序

  1. 什么是堆 首先堆heap是一种数据结构,是一棵彻底二叉树且满意性质:一切非叶子结点的值均不大于或均不小于其左、右孩子结点的值.
  2. 下面经过一组数据阐明堆排序的办法: 9, 79, 46, 30, 58, 49

根本进程:

  1. 先将待排序的数视作彻底二叉树(按层次遍历次序进行编号, 从0开端),如下图:

考研数据结构--排序算法汇总,代码完好可完成

  1. 彻底二叉树的终究一个非叶子节点,也便是终究一个节点的父节点。
  • 终究一个节点的索引为数组长度len-1,那么终究一个非叶子节点的索引应该是为(len-1)/2.也便是从索引为2的节点开端
  • 假如其子节点的值大于其本身的值。则把他和较大子节点进行交流,行将索引2处节点和索引5处元素交流。交流后的成果如图:

考研数据结构--排序算法汇总,代码完好可完成

建堆从终究一个非叶子节点开端即可

3:向前处理前一个节点,也便是处理索引为1的节点,此刻79>30,79>58,因而无需交流。

考研数据结构--排序算法汇总,代码完好可完成

  1. 向前处理前一个节点,也便是处理索引为0的节点,此刻9 < 79,9 < 49, 因而需交流。应该拿索引为0的节点与索引为1的节点交流,由于79>49. 如图:

考研数据结构--排序算法汇总,代码完好可完成

  1. 假如某个节点和它的某个子节点交流后,该子节点又有子节点,系统还需求再次对该子节点进行判别。如上图由于1处,3处,4处中,1处的值大于3,4处的值,所以还需交流。

考研数据结构--排序算法汇总,代码完好可完成
牢记: 将每次堆排序得到的最大元素与当前规划的数组终究一个元素交流。

考研数据结构--排序算法汇总,代码完好可完成
6. 完好可完成代码

#include <bits/stdc  .h>
using namespace std;
/*
           8
      1         14
   3    21    5    7
10
*/
void adjust(int arr[], int len, int index) {
    int left = 2 * index   1;
    int right = 2 * index   2;
    int maxIdx = index;
    if (left < len &&
        arr[left] >
            arr[maxIdx])  // 左面的小标小于总的数组长度,和左面的值小于传入索引的值
        maxIdx = left;    // 记载大值的索引
    if (right < len && arr[right] > arr[maxIdx])
        maxIdx = right;   // maxIdx是3个数中最大数的下标
    if (maxIdx != index)  // 假如maxIdx的值有更新
    {
        swap(arr[maxIdx], arr[index]);
        adjust(arr, len, maxIdx);  // 递归调整其他不满意堆性质的部分
    }
}
void heapSort(int arr[], int size) {
    for (int i = size / 2 - 1; i >= 0;
         i--) {  // 对每一个非叶结点进行堆调整(从终究一个非叶结点开端)
        adjust(arr, size, i);  // i为调整元素的下标
    }
    for (int i = size - 1; i >= 1; i--) {
        swap(arr[0], arr[i]);  // 将当前最大的放置到数组结尾
        adjust(arr, i, 0);  // 将未完成排序的部分继续进行堆排序
    }
}
int main() {
    int array[8] = {8, 1, 14, 3, 21, 5, 7, 10};
    heapSort(array, 8);
    for (auto it : array) {
        cout << it << " ";
    }
    return 0;
}

其实整个堆排序进程中, 咱们只需重复做两件事:

  • 建堆(初始化 调整堆, 时刻复杂度为O(n);
  • 拿堆的根节点和终究一个节点交流(siftdown, 时刻复杂度为O(n*log n);

因而堆排序全体的时刻复杂度为O(n*log n).

9. 归并排序

  1. 归并排序是比较安稳的排序办法。
  • 它的根本思想是把待排序的元素分化成两个规划大致持平的子序列。
  • 假如不易分化,将得到的子序列继续分化,直到子序列中包含的元素个数为1。
  • 由于单个元素的序列本身便是有序的,此刻便能够进行兼并,然后得到一个完好的有序序列。
  1. 流程图示例
  • 首先咱们先给定一个无序的数列(42,15,20,6,8,38,50,12),咱们进行兼并排序数列,如下图流程图所示:

考研数据结构--排序算法汇总,代码完好可完成

思路:

  • 进程一:首先将待排序的元素分红巨细大致相同的两个序列。

  • 进程二:再把子序列分红巨细大致相同的两个子序列。

  • 进程三:如此下去,直到分化成一个元素停止,这时含有一个元素的子序列都是有序的。

  • 进程四:进行兼并操作,将两个有序的子序列兼并为一个有序序列,如此下去,直到一切的元素都兼并为一个有序序列。

考研数据结构--排序算法汇总,代码完好可完成
解释一下int *

在C 中,“int * a”通常表明一个整型指针。这是一个指针变量,它存储的是整型变量的地址。

举个比方,假设咱们有一个整数变量int num = 5;,假如咱们声明一个指针变量int * a;并使其指向 num,那么a = &num;a = num;(此处是取地址运算符的应用)之后,a 就存储了 num 的地址。

之后,咱们能够经过解引证操作符 * 来访问 num 的值,如cout << *a;就会输出 5。由于咱们把 a 理解为存储了 num 的地址,解引证操作符 * 便是取出该地址处存储的值。

此外,假如咱们声明晰一个指针数组,例如int * a[5];,那么 a 就存储了五个整型指针的地址,每个整型指针都能够存储一个整型变量的地址。

#include <bits/stdc  .h>
using namespace std;
void merge(int* a, int low, int mid, int hight)  // 兼并函数
{
    cout << "a: " << *a << endl;
    cout << "low: " << low << endl;
    cout << "mid: " << mid << endl;
    cout << "hight: " << hight << endl;
    // 用 new 请求一个辅佐函数,创建一个新的动态数组,用于存放兼并后的有序数组。
    int* b = new int[hight - low   1];
    int i = low, j = mid   1, k = 0;  // k为 b 数组的小标
    cout << "a[i]: " << a[i] << endl;
    cout << "a[j]: " << a[j] << endl;
    while (i <= mid && j <= hight) {
        if (a[i] <= a[j]) {
            b[k  ] = a[i  ];  // 按从小到大存放在 b 数组里边
        } else {
            b[k  ] = a[j  ];
        }
    }
    while (i <= mid)  // j 序列完毕,将剩下的 i 序列弥补在 b 数组中
    {
        b[k  ] = a[i  ];
    }
    while (j <= hight)  // i 序列完毕,将剩下的 j 序列弥补在 b 数组中
    {
        b[k  ] = a[j  ];
    }
    k = 0;                              // 从小标为 0 开端传送
    for (int i = low; i <= hight; i  )  // 将 b 数组的值传递给数组 a
    {
        a[i] = b[k  ];
    }
    delete[] b;  // 辅佐数组用完后,将其的空间进行开释(销毁)
}
void mergesort(int* a, int low, int hight)  // 归并排序
{
    if (low < hight) {
        int mid = (low   hight) / 2;
        mergesort(a, low, mid);        // 对 a[low,mid]进行排序
        mergesort(a, mid   1, hight);  // 对 a[mid 1,hight]进行排序
        merge(a, low, mid, hight);  // 进行兼并操作
    }
}
int main() {
    int a[9] = {2, 4, 8, 3, 565, 9, 1, 56, 7};
    int n = 9;
    mergesort(a, 0, n - 1);
    cout << "归并排序成果:" << endl;
    for (int i = 0; i < n; i  ) {
        cout << a[i] << " ";
    }
    cout << endl;
    return 0;
}

10. 基数排序

基数排序是一种非比较型整数排序算法,其原理是将整数按位数切割成不同的数字,然后按每个位数分别比较。由于整数也能够表达字符串(比方名字或日期)和特定格式的浮点数,所以基数排序也不是只能运用于整数。基数排序基于计数排序。

图像进程:

考研数据结构--排序算法汇总,代码完好可完成

#include <bits/stdc  .h>
using namespace std;
int maxBit(vector<int> vec) {
    int max = vec[0];
    for (int i = 1; i < vec.size(); i  ) {
        if (vec[i] > max)
            max = vec[i];
    }
    int p = 1;
    while (max / 10 != 0) {
        max = max / 10;
        p  ;
    }
    return p;
}
vector<int> countSort(vector<int> vec,
                      int place) {  // 按指定方位的值进行计数排序
    vector<int> temp(vec);          // 暂时数组,存储place方位的值
    vector<int> ordered(vec);       // 排好序的数组放进ordered
    int count[10] = {0};
    for (int i = 0; i < vec.size(); i  ) {
        // 找到每个元素place方位上的数字存在temp中
        temp[i] = (int)(vec[i] / pow(10, place - 1)) % 10;
    }
    for (int i = 0; i < vec.size(); i  ) {
        count[temp[i]]  ;  // 统计数字呈现的频率
    }
    for (int i = 1; i < 10; i  ) {
        // 此刻count中存放着temp中每个数字的索引
        count[i] = count[i]   count[i - 1];
    }
    for (int i = vec.size() - 1; i >= 0; i--) {
        int index = count[temp[i]] - 1;
        count[temp[i]]--;
        ordered[index] = vec[i];
    }
    // cout << "next:" << endl;
    return ordered;
}
void radixSort(vector<int>& vec) {
    // 最长位数是多少,代表要进行几次排序
    int maxbit = maxBit(vec);
    // i为多少,代表按第几位数字比较(从右向左)
    for (int i = 1; i <= maxbit; i  ) {
        vec = countSort(vec, i);
    }
    for (int i = 0; i < vec.size(); i  ) {
        cout << vec[i] << "  ";
    }
}
int main() {
    // vector<int> 是 C  
    // 规范模板库(STL)中的一个模板类,它表明一个动态数组,能够存储整数类型(int)的数据。
    // 具体来说,vector<int>
    // 是一个能够动态改动巨细的数组,它供给了便利的接口来管理数组中的元素。你能够经过
    // push_back() 办法添加元素,经过 erase()
    // 办法删除元素,也能够经过下标访问或许迭代器访问元素。
    vector<int> vec{3, 5, 34, 10, 8, 6,  16, 5, 8, 6, 2, 4, 900, 4, 7,
                    0, 1, 8,  9,  7, 38, 1,  2, 5, 9, 7, 4, 0,   2, 6};
    radixSort(vec);
    return 0;
}

11. 计数排序

当待排序数组中的元素是某个区间的整数时,能够运用计数排序。

  • 其根本思想便是对每一个输入元素,确定出小于x的元素个数。有了这一信息,就能够把x直接放到它在终究输出数组中的方位上。
  • 例如,假如有17个元素小于x,则x就归于第18个输出方位。

考研数据结构--排序算法汇总,代码完好可完成


/*算法:计数排序*/
#include <cstring>
#include <iostream>
using namespace std;
/***************************************************************************
    1.计数排序法,仅可用于正整数排序。
    2.计数排序的根本思想便是对每一个输入元素x,确定出小于x的元素个数。
    有了这一信息,就能够把x直接放到它在终究输出数组中的方位上。
    3.例如,假如有17个元素小于x,则x就归于第18个输出方位。
    4.当排序的数较为稠密时,所需的辅佐空间较小。
****************************************************************************/
bool ctsort(int A[], int size) {
    if (NULL == A || size < 0)
        return false;
    /*寻找数组A中的最大整数k*/
    int k = A[0];
    for (int i = 1; i < size; i  )
        if (A[i] > k)
            k = A[i];
    k  ;  // 最大整数 1
    // 请求数组,使counts数组最大下标对应A中的最大整数
    int* counts = new int[k];
    int* tmp = new int[size];  // 存放排序的成果
    // 初始化counts数组
    for (int i = 0; i < k; i  )
        counts[i] = 0;
    // counts[i]表明数字i在data中的个数
    for (int i = 0; i < size; i  )
        counts[A[i]] = counts[A[i]]   1;
    // 将counts累计起来,这样counts[i]表明小于等于i的元素个数
    for (int i = 1; i < k; i  )
        counts[i] = counts[i]   counts[i - 1];
    // 从后往前开端数A中元素排序
    for (int i = size - 1; i >= 0; i--) {
        // counts[data[j]]表明当前小于等于数据data[j]的元素个数,counts[data[j]]
        // -1则将其转换为temp中的方位
        tmp[counts[A[i]] - 1] = A[i];
        counts[A[i]] = counts[A[i]] - 1;
    }
    memcpy(A, tmp, size * sizeof(int));
    delete[] counts;
    delete[] tmp;
    return true;
}
int main() {
    int A[] = {9, 3, 7, 4, 5, 2, 1};  // 测试用例1
    // int A[] = {9,3,7,4,5,2,7};//测试用例2
    ctsort(A, 7);
    for (int i = 0; i < 7; i  )
        cout << A[i] << " ";
}