数据结构学习笔记——排序

1. 分类

2. 7种内排序算法的各种指标

排序方法

平均情况

最好情况

最坏情况

辅助空间

稳定性

移动次数的平均情况

移动次数的最好情况

移动次数的最坏情况

冒泡排序

O(n2)

O(n)

O(n2)

O(1)

稳定

O(n2)

0

O(n2)

简单选择排序

O(n2)

O(n2)

O(n2)

O(1)

稳定

O(n)

0

O(n)

直接插入排序

O(n2)

O(n)

O(n2)

O(1)

稳定

O(n2)

O(n)

O(n2)

希尔排序

O(nlogn)~O(n2)

O(n1.3)

O(n2)

O(1)

不稳定

 

 

 

堆排序

O(nlogn)

O(nlogn)

O(nlogn)

O(1)

不稳定

 

 

 

归并排序

O(nlogn)

O(nlogn)

O(nlogn)

O(n)

稳定

 

 

 

快速排序

O(nlogn)

O(nlogn)

O(n2)

O(logn)~O(n)

不稳定

 

 

 

3. 分析(“>”表示优于)

从平均情况看:

堆排序、归并排序、快速排序 > 希尔排序 > 简单算法

从最好情况看:

冒泡排序、直接插入排序 > 其它算法

从最坏情况看:

堆排序、归并排序 > 其它算法

从空间复杂度看:

简单算法、希尔排序、堆排序 > 快速排序 > 归并排序

从稳定性看:

简单算法、归并排序 > 希尔排序、堆排序、快速排序

从移动次数的平均情况看:

简单选择排序 > 冒泡排序、直接插入排序

从移动次数的最好情况看:

简单选择排序、冒泡排序 > 直接插入排序

从移动次数的最坏情况看:

简单选择排序 > 冒泡排序、直接插入排序

4. 结论

  • 堆排序和归并排序发挥稳定,快速排序最坏情况时发挥较差;
  • 如果待排序序列总是基本有序,应选择简单算法
  • 如果对内存使用量有要求(即对空间大小有要求),应选择归并排序和快速排序
  • 如果对排序稳定性有要求,应选择简单算法或归并排序
  • 如果待排序记录的个数n越小,应采用简单算法,反之,n越大,应选择改进算法;
  • 如果数据量不大但记录的关键字信息量较大,应选择简单选择排序(在简单算法中),因为当记录的关键字本身信息量较大时,其占用存储空间较大,在移动记录时所花费的时间就越多,另外,记录的关键字信息量大小对改进算法影响不大
  • 综合各项指标来说,经过优化的快速排序性能最好的排序算法。

5. 具体实现

冒泡排序

  • 正宗冒泡排序
 1 /* 对顺序表L作冒泡排序 */
 2 void BubbleSort(SqList *L)
 3 {
 4     int i,j;
 5     for(i = 1; i < L->length; i++)
 6     {
 7         for(j = L->length - 1; j >= 1; j--) /* 注意j是从后往前循环 */
 8         {
 9             if(L->r[j]>L->[j + 1]) /* 若前者大于后者 */
10             {
11                 swap(L, j, j + 1); /* 变换L->r[j]与L->r[j + 1]的值 */
12             }
13         }
14     }
15 }
  • 改进冒泡排序
 1 void BubbleSort(SqList *L)
 2 {
 3     int i,j;
 4     Status flag = TRUE; /* flag用来作为标记 */
 5     for(i = 1; i < L->length && flag; i++) /* 若flag为true则退出循环 */
 6     {
 7         flag = FALSE; /* 初始为false */
 8         for(j = L->length - 1; j >= 1; j--) /* 注意j是从后往前循环 */
 9         {
10             if(L->r[j]>L->[j + 1]) /* 若前者大于后者 */
11             {
12                 swap(L, j, j + 1); /* 变换L->r[j]与L->r[j + 1]的值 */
13                 flag = TRUE; /* 如果有数据交换,则flag为true */
14             }
15         }
16     }
17 }

简单选择排序

 1 /* 对顺序表L作简单选择排序 */
 2 void SelectSort(SqList *L)
 3 {
 4     int i,j,min;
 5     for(i = 1; i < L->length; i++)
 6     {
 7         min = 1; /* 将当前下标定义为最小值下标 */
 8         for(j = i + 1; j <= L->length; j++) /* 循环之后的数据 */
 9         {
10             if(L->r[min] > L->r[j]) /* 如果有小于当前最小值的关键字 */
11                 min = j; /* 将此关键字的下标赋值给min */
12         }
13         if(i != min) /* 若min不等于i,说明找到最小值,交换 */
14             swap(L,i,min); /* 变换L->r[i]与L->r[min]的值 */
15     }
16 }

直接插入排序

 1 /* 对顺序表L作直接插入排序 */
 2 void InsertSort(SqList *L)
 3 {
 4     int i,j;
 5     for(i = 2; i <= L->length; i++)
 6     {
 7         if(L->r[i] < L->r[i - 1]) /* 需将L->r[i]插入有序子表 */
 8         {
 9             L->r[0] = L->r[i]; /* 设置哨兵 */
10             for(j = i - 1; L->r[j] > L->r[0]; j--)
11                 L->r[j + 1] = L->r[j]; /* 记录后移 */
12             L->r[j + 1] = L->r[0]; /* 插入到正确位置 */
13         }
14     }
15 }

希尔排序

 1 /* 对顺序表L作希尔排序 */
 2 void ShellSort(SqList *L)
 3 {
 4     int i,j;
 5     int increment = L->length;
 6     do
 7     {
 8         increment = increment/3 + 1; /* 增量序列 */
 9         for(i = increment + 1; i <= L->length; i++)
10         {
11             if(L->r[i] < L->r[i - increment])
12             { /* 需将L->r[i]插入有序增量子表 */
13                 L->r[0] = L->r[i]; /* 暂存在L->r[0] */
14                 for(j = i - increment; j > 0 && L->r[0] < L->r[j]; j -= increment)
15                 {
16                     L->r[j + increment] = L->r[j]; /* 记录后移,查找插入位置 */
17                     L->r[j + increment] = L->r[0]; /* 插入 */
18                 }
19             }
20         }
21     }
22     while(increment > 1);
23 }

堆排序

 1 /* 对顺序表L作堆排序 */
 2 void HeapSort(SqList *L)
 3 {
 4     int i;
 5     for(i = L->length / 2; i > 0; i--) /* 把L中的r构建成一个大顶堆 */
 6         HeapAdjust(L,i,L->length);
 7 
 8     for(i = L->length; i > 1; i--)
 9     {
10         swap(L,1,i); /* 将堆顶记录和当前未经排序子序列的最后一个记录交换 */
11         HeapAdjust(L,1,i - 1); /* 将L->r[1..i-1]重新调整为大顶堆 */
12     }
13 }
14 
15 /* 已知L->r[s..m]中记录的关键字除L->r[s]之外均满足堆的定义 */
16 /* 本函数调整L->r[s]的关键字,使L->r[s..m]成为一个大顶堆 */
17 void HeapAdjust(SqList *L, int s, int m)
18 {
19     int temp,j;
20     temp = L->r[s];
21     for(j = 2 * s; j <= m; j *= 2) /* 沿关键字较大的孩子结点向下筛选 */
22     {
23         if(j < m && L->r[j] < L->r[j + 1])
24             ++j; /* j为关键字中较大的记录的下标 */
25         if(temp >= L->r[j])
26             break; /* rc应插入在位置s上 */
27         L->r[s] = L->r[j];
28         s = j;
29     }
30     L->r[s] = temp; /* 插入 */
31 }

归并排序

  • 递归实现
 1 /* 对顺序表L作归并排序 */
 2 void MergeSort(SqList *L)
 3 {
 4     MSort(L->r, L->r, 1, L->length);
 5 }
 6 
 7 /* 将SR[s..t]归并排序为TR1[s..t] */
 8 void MSort(int SR[], int TR1[], int s, int t)
 9 {
10     int m;
11     int TR2[MAXSIZE + 1];
12     if(s == t)
13         TR1[s] = SR[s];
14     else
15     {
16         m = (s + t) / 2; /* 将SR[s..t]平分为SR[s..m]和SR[m+1..t] */
17         MSort(SR,TR2,s,m); /* 递归将SR[s..m]归并为有序的TR2[s..m] */
18         MSort(SR,TR2,m+1,t); /* 递归将SR[m+1..t]归并为有序的TR2[m+1..t] */
19         Merge(TR2,TR1,s,m,t); /* 将TR2[s..m]和TR2[m+1..t]归并到TR1[s..t] */
20     }
21 }
22 
23 /* 将有序的SR[i..m]和SR[m+1..n]归并为有序的TR[i..n] */
24 void Merge(int SR[], int TR[], int i, int m, int n)
25 {
26     int j,k,l;
27     for(j = m + 1, k = i; i <= m && j <= n; k++) /* 将SR中记录由小到大归并入TR */
28     {
29         if(SR[i] < SR[j])
30             TR[k] = SR[i++];
31         else
32             TR[k] = SR[j++];
33     }
34     if(i <= m)
35     {
36         for(l = 0; l <= m - i; l++)
37             TR[k + l] = SR[i + l]; /* 将剩余的SR[i..m]复制到TR */
38     }
39     if(j <= n)
40     {
41         for(l = 0; l <= n - j; l++)
42             TR[k + l] = SR[j + l]; /* 将剩余的SR[j..n]复制到TR */
43     }
44 }
  • 非递归实现
 1 /* 对顺序表L作归并非递归排序 */
 2 void MergeSort(SqList *L)
 3 {
 4     int* TR = (int*)malloc(L->length * sizeof(int)); /* 申请额外空间 */
 5     int k = 1;
 6     while(k < L->length)
 7     {
 8         MergePass(L->r, TR, k, L->length);
 9         k = 2 * k; /* 子序列长度加倍 */
10         MergePass(TR, L->r, k, L->length);
11         k = 2 * k; /* 子序列长度加倍 */
12     }
13 }
14 
15 /* 将SR[]中相邻长度为s的子序列两两归并到TR[] */
16 void MergePass(int SR[], int TR[], int s, int n)
17 {
18     int i = 1;
19     int j;
20     while(i <= n - 2 * s + 1)
21     {
22         Merge(SR,TR,i,i+s-1,i+2*s-1); /* 两两归并 */
23         i = i + 2 * s;
24     }
25     if(i < n-s+1) /* 归并最后两个序列 */
26         Merge(SR, TR, i, i+s-1, n);
27     else
28         for(j = i; j <= n; j++)
29             TR[j] = SR[j];
30 }
31 
32 /* 将有序的SR[i..m]和SR[m+1..n]归并为有序的TR[i..n] */
33 void Merge(int SR[], int TR[], int i, int m, int n)
34 {
35     int j,k,l;
36     for(j = m + 1, k = i; i <= m && j <= n; k++) /* 将SR中记录由小到大归并入TR */
37     {
38         if(SR[i] < SR[j])
39             TR[k] = SR[i++];
40         else
41             TR[k] = SR[j++];
42     }
43     if(i <= m)
44     {
45         for(l = 0; l <= m - i; l++)
46             TR[k + l] = SR[i + l]; /* 将剩余的SR[i..m]复制到TR */
47     }
48     if(j <= n)
49     {
50         for(l = 0; l <= n - j; l++)
51             TR[k + l] = SR[j + l]; /* 将剩余的SR[j..n]复制到TR */
52     }
53 }

快速排序

  • 基本实现
 1 /* 对顺序表L作快速排序 */
 2 void QuickSort(SqList *L)
 3 {
 4     QSort(L, 1, L->length);
 5 }
 6 
 7 /* 将顺序表L中的子序列L->r[low..high]作快速排序 */
 8 void QSort(SqList *L, int low, int high)
 9 {
10     int pivot;
11     if(low < higt)
12     {
13         pivot = Partition(L,low,high); /* 将L->r[low..high]一分为二,算出枢轴值pivot */
14         QSort(L,low,pivot-1); /* 对低子表递归排序 */
15         QSort(L,pivot+1,high); /* 对高子表递归排序 */
16     }
17 }
18 
19 /* 变换顺序表L中子表的记录,使枢轴记录到位,并返回其所在位置 */
20 /* 此时在它之前(后)的记录均不大(小)于它 */
21 int Partition(SqList *L, int low, int high)
22 {
23     int pivotkey;
24     pivotkey = L->r[low]; /* 用子表的第一个记录作枢轴记录 */
25     while(low < high) /* 从表的两段交替向中间扫描 */
26     {
27         while(low < high && L->[high] >= pivotkey)
28             high--;
29         swap(L,low,high); /* 将比枢轴记录小的记录交换到低端 */
30         while(low < high && L->[low] >= pivotkey)
31             low++;
32         swap(L,low,high); /* 将比枢轴记录大的记录交换到高端 */
33     }
34     return low; /* 返回枢轴所在位置 */
35 }
  • 优化实现
 1 /* 优化1:优化选取枢轴,使用三数取中法或九数取中法 */
 2 /* 优化2:优化不必要的交换,采用替换进行操作 */
 3 /* 优化3:优化小数组时的排序方案,长度小时用直接插入排序,长度大时才使用快速排序 */
 4 /* 优化4:优化递归操作,采用迭代的方法代替可以缩减堆栈深度,提高整体性能 */
 5 
 6 /* 对顺序表L作快速排序 */
 7 void QuickSort(SqList *L)
 8 {
 9     QSort(L, 1, L->length);
10 }
11 
12 #define MAX_LENGTH_INSERT_SORT 7 /* 数组长度阀值 */
13 /* 将顺序表L中的子序列L->r[low..high]作快速排序 */
14 void QSort(SqList *L, int low, int high)
15 {
16     int pivot;
17     if((high - low) > MAX_LENGTH_INSERT_SORT)
18     { /* 当high-low大于常数时用快速排序 */
19         while(low < higt)
20         {
21             pivot = Partition(L,low,high); /* 将L->r[low..high]一分为二,算出枢轴值pivot */
22             QSort(L,low,pivot-1); /* 对低子表递归排序 */
23             low = pivot + 1; /* 尾递归 */
24         }
25     }
26     else /* 当high-low小于等于常数时用直接插入排序 */
27         InsertSort(L);
28 }
29 
30 /* 变换顺序表L中子表的记录,使枢轴记录到位,并返回其所在位置 */
31 /* 此时在它之前(后)的记录均不大(小)于它 */
32 int Partition(SqList *L, int low, int high)
33 {
34     int pivotkey;
35 
36     /* 优化选取枢轴,使用三数取中法 */
37     int m = low + (high - low) / 2; /* 计算数组中间的元素的下标 */
38     if(L->r[low] > L->r[high])
39         swap(L,low,high); /* 交换左端与右端数据,保证左端较小 */
40     if(L->r[m] > L->r[high])
41         swap(L,high,m); /* 交换中间与右端数据,保证中间较小 */
42     if(L->r[m] > L->r[low])
43         swap(L,m,low); /* 交换中间与左端数据,保证中间较小 */
44     /* 此时L.r[low]已经为整个序列左中右三个关键字的中间值 */
45 
46     pivotkey = L->r[low]; /* 用子表的第一个记录作枢轴记录 */
47     L->r[0] = pivotkey; /* 将枢轴关键字备份到L->r[0] */
48     while(low < high) /* 从表的两段交替向中间扫描 */
49     {
50         while(low < high && L->[high] >= pivotkey)
51             high--;
52         L->r[low] = L->r[high]; /* 采用替换而不是交换的方式进行操作 */
53         while(low < high && L->[low] >= pivotkey)
54             low++;
55         L->r[high] = L->r[low]; /* 采用替换而不是交换的方式进行操作 */
56     }
57     L->r[low] = L->r[0]; /* 将枢轴数值替换回L.r[low] */
58     return low; /* 返回枢轴所在位置 */
59 }
原文地址:https://www.cnblogs.com/dgjamin/p/4328589.html