第十章 排序
文章目录
绪论
排序算法(英语:Sorting algorithm)是将一组特定的数据按某种顺序进行排列的算法。排序算法有许多,性质和复杂度也各不相同。
排序的稳定性
稳定性是指相等的元素经过排序之后相对顺序是否发生了改变。
假设
R
i
=
R
j
R_i = R_j
Ri=Rj ,且排序前序列中
R
i
R_i
Ri 领先于
R
j
R_j
Rj;
若在排序后的序列中
R
i
R_i
Ri 仍领先于
R
j
R_j
Rj,则称排序方法是稳定的。
若在排序后的序列中
R
j
R_j
Rj 领先于
R
i
R_i
Ri,则称排序方法是不稳定的。
基数排序、计数排序、插入排序、冒泡排序、归并排序是稳定排序。
选择排序、堆排序、快速排序不是稳定排序。
排序的复杂度
内部排序vs.外部排序
- 内部排序: 指的是待排序记录存放在计算机随机存储器(内存)中进行的排序过程。
- 外部排序: 指的是待排序记录的数量很大,以致内存一次不能容纳全部记录,在排序过程中尚需对外存进行访问的排序过程。
下面我们介绍内部排序的相关算法。
内部排序
按照排序过程中所依据的原则的不同可以分类为 :
- 插入排序(希尔排序)
- 交换排序(快速排序)
- 选择排序(堆排序)
- 归并排序
- 基数排序
插入类
将无序子序列中的一个或几个记录“插入”到有序序列中,从而增加记录的有序子序列的长度。
直接插入排序
思想:利用有序表的插入操作进行排序
有序表的插入:将一个记录插入到已排好序的有序表中,从而得到一个新的有序表。插入排序是一种稳定排序。
算法步骤
- 将第一待排序序列第一个元素看做一个有序序列,把第二个元素到最后一个元素当成是未排序序列。
- 从头到尾依次扫描未排序序列,将扫描到的每个元素插入有序序列的适当位置。(如果待插入的元素与有序序列中的某个元素相等,则将待插入元素插入到相等元素的后面。)
代码实现
void insertion_sort(int a[] , int n){
//对 a[0],a[2],...,a[n-1] 进行插入排序
for(int i = 1 ; i <= n ; ++i){
int key = a[i] ; //设置“哨兵”
int j = i - 1 ;
while (j > 0 && a[j] > key){
a[j + 1] = a[j] ;
--j ;
}
a[j + 1] = key ;
}
}
时间分析
在插入排序中,当待排序数组是有序时,是最优的情况,只需当前数跟前一个数比较一下就可以了,这时一共需要比较 N − 1 N-1 N−1次,时间复杂度为 O ( N ) O(N) O(N)。
最坏的情况是待排序数组是逆序的,此时需要比较次数最多,总次数记为: 1 + 2 + 3 + … + N − 1 1+2+3+…+N-1 1+2+3+…+N−1,所以,插入排序最坏情况下的时间复杂度为 O ( N 2 ) O(N^2) O(N2)。
平均来说, A [ 1.. j − 1 ] A[1..j-1] A[1..j−1]中的一半元素小于 A [ j ] A[j] A[j],一半元素大于 A [ j ] A[j] A[j]。插入排序在平均情况运行时间与最坏情况运行时间一样,是输入规模的二次函数。
希尔排序
希尔排序,也称递减增量排序算法,是插入排序的一种更高效的改进版本。但希尔排序是非稳定排序算法。
希尔排序是基于插入排序的以下两点性质而提出改进方法的:
- 插入排序在对几乎已经排好序的数据操作时,效率高,即可以达到线性排序的效率;
- 但插入排序一般来说是低效的,因为插入排序每次只能将数据移动一位;
思想:先将待排序记录序列分割成为若干子序列分别进行直接插入排序。
待整个序列中的记录基本有序后,再全体进行一次直接插入排序。
算法步骤
- 选择一个增量序列 t 1 , t 2 , … … , t k t_1,t_2,……,t_k t1,t2,……,tk,其中 t i > t j , t k = 1 t_i > t_j, t_ k = 1 ti>tj,tk=1;
- 按增量序列个数 k k k,对序列进行 k k k 趟排序;
- 每趟排序,根据对应的增量 t i t_i ti,将待排序列分割成若干长度为 m m m 的子序列,分别对各子表进行直接插入排序。仅增量因子为 1 1 1 时,整个序列作为一个表来处理,表长度即为整个序列的长度。
代码实现
/*希尔排序*/
void shell_sort(int a[], int length){
int h = 1 ;
while(h < length / 3){
h = 3 * h + 1 ;
}
while(h >= 1){
for(int i = h ; i < length ; i++){
for(int j = i ; j >= h && a[j] < a[j - h] ; j -= h){
int tmp = a[j] ;
a[j] = a[j - h] ;
a[j - h] = tmp ;
}
}
h = h / 3 ;
}
}
时间分析
希尔排序是按照不同步长对元素进行插入排序,当刚开始元素很无序的时候,步长最大,所以插入排序的元素个数很少,速度很快;当元素基本有序了,步长很小,插入排序对于有序的序列效率很高。所以,希尔排序的时间复杂度会比 O ( N 2 ) O(N^2) O(N2)好一些。
交换类
通过“交换”无序序列中的记录从而得到其中关键字最小或最大的记录,并将它加入到有序子序列中,以此方法增加记录的有序子序列的长度。
冒泡排序
冒泡排序是一种简单的交换算法,这个算法是稳定的排序。
思想: 通过不断比较相邻元素大小,进行交换来实现排序。
算法步骤
- 从数组起点开始扫描数组,每次比较扫描到的元素和其后面的元素,如果相邻两个元素满足交换关系,就交换它们。
- 循环进行上面的过程直到整个数组都被排好序。
- 算法优化:为了减少不必要的重复比较,设置一个“上一次最后一个交换的下标”,因为前面已经是交换好的,不再交换的一定是大小关系确定的,所以每一次外循环的i被更新为该下标即可,减少了不必要的比较次数。
代码实现
/*冒泡排序*/
void Bubble_sort(int a[] , int n){
int i = n ;
while(i > 0){
int lastExchangeIndex = 0 ;
for(int j = 0 ; j < i ; j++){
if(a[j + 1] < a[j]){
int tmp = a[j] ;
a[j] = a[j + 1] ;
a[j + 1] = tmp ;
lastExchangeIndex = j ; //记录交换的位置
}
}
i = lastExchangeIndex ; //本趟进行过交换的最后一个记录的位置
}
}
时间分析
最好的情况(关键字在记录序列中顺序有序):只需进行 1 1 1趟起泡
比较的次数: n − 1 n-1 n−1;移动的次数: 0 0 0。
最坏的情况(关键字在记录序列中逆序有序):需要进行 n − 1 n-1 n−1趟起泡
比较的次数: ∑ i = 2 n ( i − 1 ) = n ( n − 1 ) 2 \sum_{i=2}^n(i-1)=\frac{n(n-1)}{2} ∑i=2n(i−1)=2n(n−1);移动的次数 3 n ( n − 1 ) 2 \frac{3n(n-1)}{2} 23n(n−1)。
快速排序
冒泡排序的改进版。
思想:
- 以首记录作为轴记录,从前、后双向扫描序列,通过交换,实现大值记录后移,小值记录前移,最终将轴记录安置在一个适当的位置。(小值记录在前、大值记录在后)
- 轴记录将原序列分割成两部分,依次对前后两部分重新设定轴记录,继而分别再进行快速排序。
- 直至整个序列有序。
算法步骤
-
从数列中挑出一个元素,称为 “基准”(pivot);
-
重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;
-
递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序;
递归的退出条件是数列的大小是零或一,也就是都已经被排序好了。虽然一直递归下去,但是这个算法总会退出,因为在每次调用中,它至少会把一个元素摆到它最后的位置去。
代码实现
/*快速排序*/
int Paritition(int A[] , int low , int high){
int pivot = A[low] ;
while (low < high){
while (low < high && A[high] >= pivot){
--high ;
}
A[low] = A[high] ;
while (low < high && A[low] <= pivot){
++low ;
}
A[high] = A[low] ;
}
A[low] = pivot ;
return low ;
}
//快排母函数
void Quick_Sort(int A[] , int low , int high) {
if(low < high){
int pivot = Paritition(A , low , high) ;
Quick_Sort(A, low, pivot - 1) ;
Quick_Sort(A, pivot + 1, high) ;
}
}
时间分析
在平均状况下,排序 n 个项目要 O ( n l o g n ) Ο(nlogn) O(nlogn)次比较。在最坏状况下则需要 O ( n 2 ) Ο(n^2) O(n2) 次比较,但这种状况并不常见。事实上,快速排序通常明显比其他 O ( n l o g n ) Ο(nlogn) O(nlogn)算法更快。
选择类
从记录的无序子序列中“选择”关键字最小或最大的记录,并将它加入到有序子序列中,以此方法增加记录的有序子序列的长度。
简单选择排序
思想:每一趟都选出一个最大或最小的元素,并放在合适的位置。
算法步骤
- 从头开始遍历数组(这是外循环),设置一个下标来在这个数后面的数组中遍历(内循环)。
- 如果满足条件就更新下标,最后如果下标与外循环此时的下标不同就交换这两个下标对应的值。
代码实现
/*简单选择排序*/
void Select_sort(int a[] , int n){
for(int i = 0 ; i < n ; i++){
int k = i ;
for(int j = i + 1 ; j < n ; j++){
if(a[j] < a[k]) k = j ;
}
if(k != i){
int tmp = a[i] ;
a[i] = a[k] ;
a[k] = tmp ;
}
}
}
时间分析
对 n 个记录进行简单选择排序,所需进行的 关键字间的比较次数总计为 ∑ i = 1 n − 1 n = n ( n − 1 ) 2 \sum_{i=1}^{n-1}n=\frac{n(n-1)}{2} ∑i=1n−1n=2n(n−1)。
移动的次数,最小为 0 0 0,最大为 3 ( n − 1 ) 3(n-1) 3(n−1)。
选择排序的主要操作是进行关键字间的比较。
在 n n n个关键字中选出最小值,至少需要 n − 1 n-1 n−1次比较。
在剩余 n − 1 n-1 n−1个关键字中选出最小值,至少需要 n − 2 n-2 n−2次比较?(思考)
利用前 n − 1 n-1 n−1次比较所得信息,可减少后面选择的比较次数。
堆排序
堆的概念: 一棵完全二叉树,任一个非终端结点的值均小于等于或大于等于其左、右儿子结点的值。分为大顶堆和小顶堆。
- 大顶堆:每个节点的值都大于或等于其子节点的值,在堆排序算法中用于升序排列;
- 小顶堆:每个节点的值都小于或等于其子节点的值,在堆排序算法中用于降序排列;
思想:将序列构造成一棵完全二叉树 ,然后将这棵普通的完全二叉树改造成堆,便可以获取最小(最大)值。每次输出最小(最大)值并删除根结点,再重复改造剩下的二叉树为堆,重复该过程就可以得到排序后的序列。
算法步骤
- 将待排序序列构建成一个堆 H [ 0 … … n − 1 ] H[0……n-1] H[0……n−1],根据(升序降序需求)选择大顶堆或小顶堆;
- 把堆首(最大值)和堆尾互换;
- 把堆的尺寸缩小 1 1 1,并把新的数组顶端数据调整到相应位置输出;
- 重复步骤 2,直到堆的尺寸为 1 1 1。
- 得到的就是排好序的数组。
代码实现
/*堆排序*/
//已知 R[s..m]中记录的关键字除 R[s] 之外均满足堆的特征,本函数自上而下调整 R[s] 的关键字,使 R[s..m] 也成为一个大顶堆
void HeapAdjust(int a[] , int s , int e){
int rc = a[s] ; //暂存a[s],s可以看作父亲结点的下标
for(int i = 2 * s + 1 ; i <= e ; i *= 2){
if(i + 1 <= e && a[i] < a[i + 1]) i++ ;
if(a[s] >= a[i]) break ;
else{
a[s] = a[i] ;
s = i ;
}
a[s] = rc ;
}
}
//堆排序母函数
void Heap_Sort(int a[] , int length){
//从最后一个结点的父亲结点开始,完成堆化
for(int i = (length - 1 - 1) / 2 ; i >= 0 ; i--) HeapAdjust(a , i , length - 1) ;
//先将第一个元素和已经排好的元素前一位作交换,再重新堆化,直到排序完成
for(int i = length - 1 ; i > 0 ; i--){
int tmp = a[0] ;
a[0] = a[i] ;
a[i] = tmp ;
HeapAdjust(a , 0 , i - 1) ;
}
}
时间分析
堆排序的平均时间复杂度为 O ( n l o g n ) Ο(nlogn) O(nlogn)。
归并类
通过“归并”两个或两个以上的记录有序子序列,逐步增加记录有序序列的长度。
归并排序
归并: 将两个或两个以上的有序表合并成一个新的有序表。
之前已经学过的归并:有序线性表、有序链表的归并。
思想: 将两个或两个以上的有序表合并成一个新的有序表。
初始, n n n 个记录看作是 n n n个有序的子序列,长度为 1 1 1;
两两归并,得到 n 2 \frac{n}{2} 2n个长度为 2 2 2或 1 1 1的有序的子序列 ;
重复执行直至得到一个长度为 n n n的有序序列为止 。
这种算法称之为 2 2 2路归并排序。
算法步骤
- 申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列;
- 设定两个指针,最初位置分别为两个已经排序序列的起始位置;
- 比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置;
- 重复步骤3直到某一指针达到序列尾;
- 将另一序列剩下的所有元素直接复制到合并序列尾。
代码实现
/*归并排序*/
void Merge(int a[], int i , int mid , int n){
// 将有序的记录序列 a[i..m] 和 a[m+1..n]
// 归并为有序的记录序列 tmp[i..n]
int k = i ;
int j = mid + 1 ;
int i0 = i ;
int n0 = n ;
int *tmp = (int *)malloc((n - i + 1)*sizeof(int));
//归并步骤
while(i<= mid & j <= n){
if(a[i] <= a[j]) tmp[k++] = a[i++] ;
else tmp[k++] = a[j++] ;
}
//处理剩余元素,将其加到尾上
if(i <= mid){
while(i <= mid){
tmp[k++] = a[i++] ;
}
}
else if(j <= n){
while(j <= n){
tmp[k++] = a[j++] ;
}
}
//将这个临时数组放到原数组对应的位置上,完成局部的归并
for(int index = i0; index <= n0 ; index++){
a[index] = tmp[index] ;
}
//不要忘记释放指针
free(tmp) ;
}
//归并排序母函数
void Merge_Sort(int a[] , int s , int e){
int mid = 0 ;
if(s < e){
mid = (s + e) / 2 ;
//递归调用归并排序当前数组的前半段
Merge_Sort(a , s , mid) ;
//递归调用归并排序当前数组的后半段
Merge_Sort(a , mid + 1 , e) ;
//将当前数组的前后两个part归并,得到
Merge(a , s , mid , e) ;
}
}
时间分析
归并排序是一种分治的思想,其时间复杂度为 O ( n l o g n ) O(nlogn) O(nlogn)。
其他类
基数排序
基数排序借助“分配”和“收集”两种操作实现对单逻辑关键字的排序。
基数排序是一种非比较型整数排序算法,其思想是每次将待比较的数分割成好几个位,从低到高,按照位关键字相同的放在一起,将其中放在一起的数按照大小关系排好序,然后将其再依次串联,选取下一位,重复上面的操作,直到所有的位都被用完且串联在一起,这样得到的就是排好序的数组。
由于基数排序的算法实现需要定义一系列的数据结构来存储和实现,不大方便测试样例中的数组的排序,故在此不赘述。
对比和总结
排序算法 | 平均时间复杂度 | 最好情况 | 最坏情况 | 空间复杂度 | 稳定性 |
---|---|---|---|---|---|
插入排序 | O ( n 2 ) O(n^2) O(n2) | O ( n ) O(n) O(n) | O ( n 2 ) O(n^2) O(n2) | O ( 1 ) O(1) O(1) | 稳定 |
希尔排序 | O ( n l o g n ) O(nlogn) O(nlogn) | O ( n ) O(n) O(n) | O ( n l o g 2 n ) O(nlog^2n) O(nlog2n) | O ( 1 ) O(1) O(1) | 不稳定 |
冒泡排序 | O ( n 2 ) O(n^2) O(n2) | O ( n ) O(n) O(n) | O ( n 2 ) O(n^2) O(n2) | O ( 1 ) O(1) O(1) | 稳定 |
快速排序 | O ( n l o g n ) O(nlogn) O(nlogn) | O ( n l o g n ) O(nlogn) O(nlogn) | O ( n 2 ) O(n^2) O(n2) | O ( l o g n ) O(logn) O(logn) | 不稳定 |
选择排序 | O ( n 2 ) O(n^2) O(n2) | O ( n 2 ) O(n^2) O(n2) | O ( n 2 ) O(n^2) O(n2) | O ( 1 ) O(1) O(1) | 不稳定 |
堆排序 | O ( n l o g n ) O(nlogn) O(nlogn) | O ( n l o g n ) O(nlogn) O(nlogn) | O ( n l o g n ) O(nlogn) O(nlogn) | O ( 1 ) O(1) O(1) | 不稳定 |
归并排序 | O ( n l o g n ) O(nlogn) O(nlogn) | O ( n l o g n ) O(nlogn) O(nlogn) | O ( n l o g n ) O(nlogn) O(nlogn) | O ( n ) O(n) O(n) | 稳定 |
基数排序 | O ( n × k ) O(n\times k) O(n×k) | O ( n × k ) O(n\times k) O(n×k) | O ( n × k ) O(n\times k) O(n×k) | O ( n + k ) O(n+ k) O(n+k) | 稳定 |
完整代码及测试样例
#include <iostream>
using namespace std ;
/*插入排序*/
void insertion_sort(int a[] , int n){
//对 a[1],a[2],...,a[n] 进行插入排序
for(int i = 1 ; i < n ; i++){
int key = a[i] ;
int j = i - 1 ;
while (j >= 0 && a[j] > key){
a[j + 1] = a[j] ;
--j ;
}
a[j + 1] = key ;
}
}
/*希尔排序*/
void shell_sort(int a[], int length){
int h = 1 ;
while(h < length / 3){
h = 3 * h + 1 ;
}
while(h >= 1){
for(int i = h ; i < length ; i++){
for(int j = i ; j >= h && a[j] < a[j - h] ; j -= h){
int tmp = a[j] ;
a[j] = a[j - h] ;
a[j - h] = tmp ;
}
}
h = h / 3 ;
}
}
/*冒泡排序*/
void Bubble_sort(int a[] , int n){
int i = n ;
while(i > 0){
int lastExchangeIndex = 0 ;
for(int j = 0 ; j < i ; j++){
if(a[j + 1] < a[j]){
int tmp = a[j] ;
a[j] = a[j + 1] ;
a[j + 1] = tmp ;
lastExchangeIndex = j ; //记录交换的位置
}
}
i = lastExchangeIndex ; //本趟进行过交换的最后一个记录的位置
}
}
/*快速排序*/
int Paritition(int A[] , int low , int high){
int pivot = A[low] ;
while (low < high){
while (low < high && A[high] >= pivot){
--high ;
}
A[low] = A[high] ;
while (low < high && A[low] <= pivot){
++low ;
}
A[high] = A[low] ;
}
A[low] = pivot ;
return low ;
}
//快排母函数
void Quick_Sort(int A[] , int low , int high) {
if(low < high){
int pivot = Paritition(A , low , high) ;
Quick_Sort(A, low, pivot - 1) ;
Quick_Sort(A, pivot + 1, high) ;
}
}
/*简单选择排序*/
void Select_sort(int a[] , int n){
for(int i = 0 ; i < n ; i++){
int k = i ;
for(int j = i + 1 ; j < n ; j++){
if(a[j] < a[k]) k = j ;
}
if(k != i){
int tmp = a[i] ;
a[i] = a[k] ;
a[k] = tmp ;
}
}
}
/*堆排序*/
//已知 R[s..m]中记录的关键字除 R[s] 之外均满足堆的特征,本函数自上而下调整 R[s] 的关键字,使 R[s..m] 也成为一个大顶堆
void HeapAdjust(int a[] , int s , int e){
int rc = a[s] ;
for(int i = 2 * s + 1 ; i <= e ; i *= 2){
if(i + 1 <= e && a[i] < a[i + 1]) i++ ;
if(a[s] >= a[i]) break ;
else{
a[s] = a[i] ;
s = i ;
}
a[s] = rc ;
}
}
//堆排序母函数
void Heap_Sort(int a[] , int length){
//从最后一个结点的父亲结点开始,完成堆化
for(int i = (length - 1 - 1) / 2 ; i >= 0 ; i--) HeapAdjust(a , i , length - 1) ;
//先将第一个元素和已经排好的元素前一位作交换,再重新堆化,直到排序完成
for(int i = length - 1 ; i > 0 ; i--){
int tmp = a[0] ;
a[0] = a[i] ;
a[i] = tmp ;
HeapAdjust(a , 0 , i - 1) ;
}
}
/*归并排序*/
void Merge(int a[], int i , int mid , int n){
// 将有序的记录序列 a[i..m] 和 a[m+1..n]
// 归并为有序的记录序列 tmp[i..n]
int k = i ;
int j = mid + 1 ;
int i0 = i ;
int n0 = n ;
int *tmp = (int *)malloc((n - i + 1)*sizeof(int));
//归并步骤
while(i<= mid & j <= n){
if(a[i] <= a[j]) tmp[k++] = a[i++] ;
else tmp[k++] = a[j++] ;
}
//处理剩余元素,将其加到尾上
if(i <= mid){
while(i <= mid){
tmp[k++] = a[i++] ;
}
}
else if(j <= n){
while(j <= n){
tmp[k++] = a[j++] ;
}
}
//将这个临时数组放到原数组对应的位置上,完成局部的归并
for(int index = i0; index <= n0 ; index++){
a[index] = tmp[index] ;
}
//不要忘记释放指针
free(tmp) ;
}
//归并排序母函数
void Merge_Sort(int a[] , int s , int e){
int mid = 0 ;
if(s < e){
mid = (s + e) / 2 ;
//递归调用归并排序当前数组的前半段
Merge_Sort(a , s , mid) ;
//递归调用归并排序当前数组的后半段
Merge_Sort(a , mid + 1 , e) ;
//将当前数组的前后两个part归并,得到
Merge(a , s , mid , e) ;
}
}
/*测试part*/
void test_insert(){
int a[] = {49 , 38 , 65 , 97 , 76 , 13 , 27 , 48 , 55 , 4 , 19} ;
insertion_sort(a , 11) ;
for(int i = 0 ; i < 11 ; i++){
cout << a[i] << ' ' ;
}
cout << endl ;
}
void test_shell(){
int b[] = {49 , 38 , 65 , 97 , 76 , 13 , 27 , 48 , 55 , 4 , 19} ;
shell_sort(b , 11) ;
for(int i = 0 ; i < 11 ; i++){
cout << b[i] << ' ' ;
}
cout << endl ;
}
void test_Bubble(){
int c[] = {49 , 38 , 65 , 97 , 76 , 13 , 27 , 48 , 55 , 4 , 19} ;
Bubble_sort(c , 10) ;
for(int i = 0 ; i < 11 ; i++){
cout << c[i] << ' ' ;
}
cout << endl ;
}
void test_Quick(){
int d[] = {49 , 38 , 65 , 97 , 76 , 13 , 27 , 48 , 55 , 4 , 19} ;
Quick_Sort(d , 0 , 10) ;
for(int i = 0 ; i < 11 ; i++){
cout << d[i] << ' ' ;
}
cout << endl ;
}
void test_Select(){
int e[] = {49 , 38 , 65 , 97 , 76 , 13 , 27 , 48 , 55 , 4 , 19} ;
Select_sort(e , 11) ;
for(int i = 0 ; i < 11 ; i++){
cout << e[i] << ' ' ;
}
cout << endl ;
}
void test_Heap(){
int f[] = {49 , 38 , 65 , 97 , 76 , 13 , 27 , 48 , 55 , 4 , 19} ;
Heap_Sort(f , 11) ;
for(int i = 0 ; i < 11 ; i++){
cout << f[i] << ' ' ;
}
cout << endl ;
}
void test_Merge(){
int g[] = {49 , 38 , 65 , 97 , 76 , 13 , 27 , 48 , 55 , 4 , 19} ;
Merge_Sort(g , 0 , 10) ;
for(int i = 0 ; i < 11 ; i++){
cout << g[i] << ' ' ;
}
cout << endl ;
}
int main(){
test_insert() ;
test_shell() ;
test_Bubble() ;
test_Quick() ;
test_Select() ;
test_Heap() ;
test_Merge() ;
return 0 ;
}
运行结果:
如有错误请多多指教!!!
参考资料:
[1]Oi Wiki :https://oi-wiki.org/basic/sort-intro/
[2]图解排序算法(二)之希尔排序 :https://www.cnblogs.com/chengxiao/p/6104371.html
[3]数据结构(C语言版) 第2版 (严蔚敏等)
[4]百度百科-插入排序 https://baike.baidu.com/link?url=KlekmiNQt9t7DEdH-waoOzLN2eh6Nc1VL18b1iaaJpo1s_sioeHOnF78imOVpEV0LRF0yOYLKRDQ5X40sdvtL9A2RYKtleBeW9qyb59ukQQP5XddrfC5D7hLuCVPR-eH
[5]百度百科-希尔排序 https://baike.baidu.com/item/希尔排序
[6]《十大经典排序算法》https://github.com/zhangyuuao/JS-Sorting-Algorithm