直接插入法是最简单的排序法,其排序思想如下:
打牌时,每抓一张牌就将其放到对应的顺序位置。
下面是待排记录的类型定义:
1 2 3 4 5 6 7 8 9 10
| #define MAXSIZE 200 typedef int KeyType typedef struct{ KeyType key; InfoType otherinfo; }RedType; typededf struct{ RedType r[MAXSIZE+1]; int length; }SqList;
|
上述的RedType结构体类型实际上是没必要定义的,实际做题时可以写成如下代码,更方便一点:
1 2 3 4 5 6
| #define MAXSIZE 200 typedef int KeyType typededf struct{ KeyType r[MAXSIZE+1]; int length; }SqList;
|
直接插入排序算法的描述:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| void InsertSort(SqList &L) {
for(i=2;i<=L.length;i++) { if(L.r[i]<L.r[i-1]) { L.r[0]=L.r[i]; L.r[i]=L.r[i-1]; for(j=i-2;L.r[0]<L.r[j];j--) L.r[j+1]=L.r[j]; L.r[j+1]=L.r[0]; } } }
|
常见错误^by小胡^:
1)低位输出随机数。(记录逐个后移,找到插入位置)三行代码的位置放在了if语句外,导致即便低位符合降序,但仍执行代码,致溢出。
直接插入排序算法分析:
- 最好的情况是记录序列按关键字有序
- 比较次数n-1;移动次数0
- 最坏的情况是记录序列按关键字逆序
- 比较次数(n+2)(n-1)/2;移动次数(n+4)(n-1)/2
- 平均情况下比较次数和移动次数约为n^2^/4,时间复杂度为O(N^2^)
- 直接插入排序容易实现,是稳定排序
- 直接插入排序也适用于链式结构,不需要移动
- 适用于初始基本有序的情况
希尔排序法是直接插入法的优化,它的思想是先将整个排序序列分成若干子序列,并分别进行直接插入排序,当整个记录中的序列基本有序时,再对全体记录进行一次直接插入排序。
希尔排序法的描述:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| void ShellInsert(SqList& L, int dk) { int i, j; for (i = dk + 1; i <= L.length; ++i) { if (L.r[i] < L.r[i - dk]) { L.r[0] = L.r[i]; for (j = i - dk; j > 0 && L.r[0] < L.r[j]; j -= dk) L.r[j + dk] = L.r[j]; L.r[j + dk] = L.r[0]; } } } void ShellSort(SqList& L, int dt[], int t) { int k; for (k = 0; k < t; ++k) ShellInsert(L, dt[k]); }
|
希尔排序法算法分析:
- 时间复杂度取决于增量序列,可到 O(N ^4/3^ ),优于直接插入排序
- 如何选择最佳 d序列,目前尚未解决,但增量系列应该无公因子,最后一个增量值必须为 1
- 空间复杂度为 O(1)
- 记录跳跃移动导致排序方法不稳定
- 只能用于顺序结构,不能用于链式结构
利用希尔排序解决如下问题:
输入
输入的第一行包含1个正整数n,表示共有n个整数需要参与排序。其中n不超过1000。
第二行包含n个用空格隔开的正整数,表示n个需要排序的整数.
输出
只有1行,包含n个整数,表示从小到大排序完毕的所有整数。
请在每个整数后输出一个空格,并请注意行尾输出换行。
样例输入
10
2 8 4 6 1 10 7 3 5 9
样例输出
1 2 3 4 5 6 7 8 9 10
下面是源码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| #include <iostream> using namespace std; #define KeyType int #define MAXSIZE 1000 typedef struct { KeyType r[MAXSIZE+1]; int length; }SqList; void ShellInsert(SqList& L, int dk) { int i, j; for (i = dk + 1; i <= L.length; ++i) { if (L.r[i] < L.r[i - dk]) { L.r[0] = L.r[i]; for (j = i - dk; j > 0 && L.r[0] < L.r[j]; j -= dk) L.r[j + dk] = L.r[j]; L.r[j + dk] = L.r[0]; } } } void ShellSort(SqList& L, int dt[], int t) { int k; for (k = 0; k < t; ++k) ShellInsert(L, dt[k]); } int main() { int n; cin >> n; SqList L; int dlta[1000]; L.length = n; for (int i = 1; i <= n; i++) { cin >> L.r[i]; dlta[i - 1] = n-i; } ShellSort(L,dlta,n); for (int i = 1; i <= n; i++) cout << L.r[i] << " "; cout << endl; return 0; }
|
冒泡排序排序法,相邻元素之间进行比较,如果逆序则交换位置。
冒泡排序法的描述:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| void BubbleSort(SqList &L) { m = L.length − 1; flag = 1; while((m > 0) && (flag == 1)) { flag = 0; for(j = 1; j <= m; j++) if(L.r[j].key > L.r[j + 1].key) { flag = 1; t = L.r[j]; L.r[j] = L.r[j + 1]; L.r[j + 1] = t; } −−m; } }
|
实际上此冒泡排序是对原冒泡排序的一种优化,flag标识符实现了优化。
例如:1 2 5 3 7 8 9 10
原冒泡排序要比较7趟,而此冒泡排序只需比较3趟。
下面贴上原冒泡排序算法描述:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| void BubbleSort(SqList &L) { int i, j,temp; int m=L.length-1; while(m>0) for (j = 1; j <= m; j++) if (L.r[j] > L.r[j + 1]) { temp = L.r[j]; L.r[j] = L.r[j + 1]; L.r[j + 1] = temp; } --m; }
|
冒泡排序算法分析:
- 最好情况下初始有序,只需一趟排序,比较次数为n-1,不需要移动记录
- 最坏情况是初始逆序,需 n-1 趟排序
- 比较的次数:n(n-1)/2;移动的次数:3n(n-1)/2
- 平均时间复杂度为 O(n 2 )
- 辅助空间复杂度为 O(1)
- 是一种稳定的排序方法,可用于链式存储结构
快速排序法是最常用的一种排序方法,同时也是很典型的分治思想,分而治之。它的排序思路如下:
1.任取一个记录作为枢轴
2.划分:所有比枢轴小的记录一律前放,比枢轴大的记录一律后放,形成左右两个子表
3.对子表重新进行前两步操作,直到每个子表元素只剩一个
快速排序法算法描述:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| int Partition(SqList &L,int low,int high) { L.r[0]=L.r[low]; pivotkey=L.r[low]; while(low<high){ while(low<high&&L.r[high]>=pivotkey) −−high; L.r[low]=L.r[high]; while(low<high&&L.r[low]<=pivotkey) ++low; L.r[high]=L.r[low]; } L.r[low]=L.r[0]; return low; } void QSort(SqList &L, int low, int high) { if(low < high) { pivotloc = Partition(L, low, high); QSort(L, low, pivotloc − 1); QSort(L, pivotloc + 1, high); } } void QuickSort(SqList &L) { QSort(L, 1, L.length); }
|
快速排序算法分析:
- 性能最好情况是均匀划分,时间复杂度是 O(nlog 2 n)。
- 性能最差情况如初始序列有序,导致最不均匀划分,性能退至O(n ^2^)。
- 为避免最坏情况发生,可以三者取中,即头、尾和中间记录三者中关键字居中的记录作为枢轴
- 可以证明,平均计算时间是 O(nlog n)。
- 实验结果表明:就平均计算时间而言,快速排序是所有内部排序方法中最好的一个。
- 辅助空间复杂度取决于栈的层次,最坏 O(n), 最好O(log 2 n)。
- 快速排序不稳定,不适用于链表结构。