数据结构【排序】—排序算法大集合
影响排序性能的要素:
时间性能;
辅助空间
算法的复杂度
简单排序【n^2】
算法思想:
第一趟:
从第一个记录开始,通过n-1次关键字比较,从n个记录中选出最小的并和第一个记录交换;
第二趟:
从第二个记录开始,通过n-2次关键字比较,从n -1个记录中选出最小的并和第二个记录交换;
1 /**************************简单排序****************************/ 2 //初级冒泡排序,就是一个一个与后面对比 3 void BubbleSort0(SqList* &l,int (&a)[2]) { 4 for (int i = 1; i < l->len; ++i) { 5 for (int j = i + 1; j <= l->len; ++j) { 6 a[0]++; 7 if (l->v[i] > l->v[j]){ 8 swap(l->v[i], l->v[j]); 9 a[1]++; 10 } 11 } 12 } 13 }
冒泡排序【n^2】:
冒泡排序(Bubble Sort)一种交换排序,它的基本思想是:两两比较相邻记录的关键字,如果反序则交换,直到没有反序的记录为止。
从末尾两两比较将最小数浮上来!
1 /**************************冒泡排序****************************/ 2 //冒泡排序,两两比较排序 3 void BubbleSort(SqList* &l, int(&a)[2]) { 4 for (int i = 1; i < l->len; ++i) { 5 for (int j = l->len - 1; j >= i; --j) { 6 a[0]++; 7 if (l->v[j + 1] < l->v[j]) { 8 swap(l->v[j + 1], l->v[j]); 9 a[1]++; 10 } 11 } 12 } 13 }
1 /**************************改进冒泡排序****************************/ 2 //改进冒泡排序,加入哨兵模式 3 void BubbleSort2(SqList* &l, int(&a)[2]) { 4 bool flag = true;//哨兵 5 for (int i = 1; flag&&i < l->len; ++i) { 6 flag = false; 7 for (int j = l->len - 1; j >= i; --j) { 8 a[0]++; 9 if (l->v[j + 1] < l->v[j]) { 10 a[1]++; 11 swap(l->v[j + 1], l->v[j]); 12 flag = true;//数据交换后才需要遍历 13 } 14 } 15 } 16 }
选择比较【n^2】:
简单选择排序法(Simple Selection Sort)就是通过n-i次关键字间的比较,从n-i+1个记录中选出关键字最小的坐标记录,并和第i(1<i<n)个记录交换之。找到最小值,然后依次放到前到后的值;
1 /**************************选择排序****************************/ 2 //选择排序,就是依次找出未排序中的最小值放入前面 3 void SelectSort(SqList* &l, int(&a)[2]) { 4 int i, j, min; 5 for (i = 1; i < l->len; ++i) { 6 min = i; 7 for (j = i + 1; j < l->len; ++j) { 8 a[0]++; 9 if (l->v[min] > l->v[j]) 10 min = j; 11 } 12 if (i != min) { 13 swap(l->v[min], l->v[i]); 14 a[1]++; 15 } 16 } 17 }
直接插入排序【n^2】:
直接插入排序(Straight Insertion Sort)的基本操作是将一个记录插入到已经排好序的有序表中,从而得到一个新的、记录数增1的有序表。
即:从前向后两两比较,若第二个数为小的数,然后将该数记住,从此位置从后向前比较该数,并将比他大的数向后挪,直到找到存放该数的位置
此算法需要一个辅助的临时空间存放:
算法思想:将第i个记录插入到前面已经排好序的i - 1个记录中去。
算法要点:
·使用监视哨r[0]临时保存带插入记录
·从后往前查找应插入的位置
·查找与移动用同一循环完成
·算法时间复杂度:o(n^2)
1 /**************************插值排序****************************/ 2 //插值排序,就是从前向后两两比较,若第二个数为小的数,然后将该数记住, 3 //从此位置从后向前比较该数,并将比他大的数向后挪,直到找到存放该数的位置 4 //此算法需要一个辅助的临时空间存放: 5 6 void InsertSort(SqList* &l, int(&a)[2]) { 7 int i, j; 8 for (int i = 2; i < l->len; ++i) { 9 a[0]++; 10 if (l->v[i] < l->v[i - 1]) {//找到了前比后大的数 11 l->v[0] = l->v[i];//设置哨兵 12 for (j = i - 1; l->v[0] < l->v[j]; --j) {//返回比较 13 a[1]++; 14 l->v[j + 1] = l->v[j];//大的数向后挪 15 } 16 l->v[j+1] = l->v[0]; 17 } 18 } 19 }
折半插入排序:
算法思想:
利用折半查找的思想找到需要插入的位置
算法时间复杂度:
o(n^2),虽然减少了查找插入位置的次数,但是移动元素的时间仍未改变
希尔排序【nlogn】:
将相距某个“增量”的记录组成一个子序列,这样才能保证在子序列内分别进行直接插入排序后得到的结果是基本有序而不是局部有序。
与直接插入排序很相似,但需要一个变动的跨度n进行比较,然后第二次跨度为n-1进行比较
算法思想:
将待排序的关键字序列分成若干个较小的子序列,对子序列进行直接插入排序,使整个待排序序列排好序。
1 /**************************希尔排序****************************/ 2 //希尔排序 3 //与插值排序很像,不过是间隔比较 4 void ShellSort(SqList* &l, int(&a)[2]) { 5 int gap = l->len; 6 int i, j; 7 do { 8 gap = gap / 3 + 1;//间隔大小 9 for (i = gap + 1; i < l->len; ++i) { 10 a[0]++; 11 if (l->v[i] < l->v[i - gap]) { 12 l->v[0] = l->v[i]; 13 for (j = i - gap; j > 0 && l->v[0] < l->v[j]; j -= gap) {//向前去排序 14 l->v[j + gap] = l->v[j];//大数向后移; 15 a[1]++; 16 } 17 l->v[j + gap] = l->v[0];//插入 18 } 19 } 20 } while (gap > 1); 21 }
堆排序【nlogn】:
堆是具有下列性质的完全二叉树:每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆;或者每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆。
1 /**************************************堆排序*****************************/ 2 //调整为大根堆 3 void HeapAdjust(SqList* &l, int n, int len) { 4 int temp = l->v[n]; 5 for (int i = 2 * n; i <= len; i *= 2) {//沿着关键字中较大的孩子结构向在筛选 6 if (i < len&&l->v[i] < l->v[i + 1])++i;//i为关键字中较大的记录的下标 7 if (temp >= l->v[i])break;//找到插入的位置了 8 l->v[n] = l->v[i];// 9 n = i; 10 } 11 l->v[n] = temp;//进行插入 12 } 13 14 //堆排序 15 void HeapSort(SqList* &l, int(&a)[2]) { 16 for (int i = l->len / 2; i > 0; --i) {//把数组中的数据变为一个大堆 17 a[0]++; 18 HeapAdjust(l, i, l->len); 19 } 20 for (int i = l->len; i > 1; --i) { 21 a[1]++; 22 swap(l->v[1], l->v[i]);/* 将堆顶记录和当前未经排序子序列的最后一个记录交换 */ 23 HeapAdjust(l, 1, i - 1);/* 将L->r[1..i-1]重新调整为大根堆 */ 24 } 25 }
算法思想:
将向量中存储的数据看成一棵完全二叉树,利用完全二叉树中双亲节点和孩子节点之间的内在关系选择关键字最小的记录。
·将待排序的序列构造成一个大顶堆。
·此时,整个序列的最大值就是堆顶的根结点。将它移走(其实就是将其与堆数组的末尾元素交换,此时末尾元素就是最大值),
·然后将剩余的n-1个序列重新构造成一个堆,这样就会得到n个元素中的次大值。
·如此反复执行,便能得到一个有序序列了。
大根堆:
根节点最大;
各节点关键字满足:a[i] >= a[2i]并且a[i] >= a[2i + 1]
小根堆:
根节点最小;
各节点关键字满足:a[i] <= a[2i]并且a[i] <= a[2i+1]
归并排序:
归并排序(Merging Sort)就是利用归并的思想实现的排序方法。它的原理是假设初始序列含有n个记录,则可以看成是n个有序的子序列,每个子序列的长度为1,然后两两归并,得到[n/21([x1表示不小于x的最小整数)个长度为2或1的有序子序列;再两两归并,……,如此重复,直至得到一个长度为n的有序序列为止,这种排序方法称为2路归并排序。
算法思想:
设初始序列长度为n,将这n个序列看成n个有序的子序列,然后辆辆合并,得到一个ceil(n/2)长度为2的有序子序列。
在此基础上再对长度为2 的有序子序列进行归并排序,得到若干长度为4的子序列,如此重复直到得到一个长度为n的有序子序列为止
1 /**************************递归法归并+排序****************************/ 2 /* 将有序的SR[i..m]和SR[m+1..n]归并为有序的TR[i..n] */ 3 //归并 4 void Merge(int *SR, int *TR, int pot, int m, int len) { 5 int i,j,k; 6 for (i = m + 1, k = pot; pot <= m && i <= len; ++k) {/*将SR中记录由小到大地并入TR * /*/ 7 if (SR[pot] < SR[i])TR[k] = SR[pot++]; 8 else TR[k] = SR[i++]; 9 } 10 if (pot <= m) 11 for (j = 0; j <= m; ++j) 12 TR[k + j] = SR[pot + j]; /* 将剩余的SR[i..m]复制到TR */ 13 if (i <= len) 14 for (j = 0; j <= len - i; ++j) 15 TR[k + j] = SR[i + j];/* 将剩余的SR[j..n]复制到TR */ 16 } 17 //进行递归 18 void MSort(int *SR,int*TR1,int pot, int len, int(&a)[2]) { 19 int TR2[MAXSIZE + 1]; 20 if (len == pot) 21 TR1[pot] = SR[pot]; 22 else { 23 int m = (len + pot) / 2; //将原数组平分两个数组 24 a[0]++; 25 MSort(SR, TR2, pot, m, a); /* 递归地将SR[pot..m]归并为有序的TR2[pot..m] */ 26 MSort(SR, TR2, m + 1, len, a); /* 递归地将SR[m+1..len]归并为有序的TR2[m+1..len] */ 27 Merge(TR2, TR1, pot, m, len); /* 将TR2[pot..m]和TR2[m+1..t]归并到TR1[pot..len] */ 28 a[1]++; 29 } 30 31 } 32 33 //递归法归并排序 34 void MergeSort(SqList* &l, int(&a)[2]) { 35 MSort(l->v,l->v,1, l->len,a); 36 } 37 38 /**************************非递归法归并排序****************************/ 39 /*将SR[]中相邻长度为s的子序列两两归并到TR[] * /*/ 40 void MergePass(int *SR, int *TR, int k, int len, int(&a)[2]) { 41 int i = 1, j; 42 a[1]++; 43 while (i <= len - 2 * k + 1) { 44 //两两归并 45 Merge(SR, TR, i, i + k - 1, len); 46 i = i + 2 * k; 47 } 48 if (i < len - k + 1)//归并最后两个序列 49 Merge(SR, TR, i, i + k - 1, len); 50 else//最后只剩下单个序列 51 for (j = i; j <= len; ++j) 52 TR[j] = SR[j]; 53 } 54 //归并排序 55 void MergeSort2(SqList* &l, int(&a)[2]) { 56 int *TR = new int[l->len]; 57 int k = 1; 58 while (k < l->len) { 59 a[0]++; 60 MergePass(l->v, TR, k, l->len, a); 61 k = k * 2;//加长子序列 62 MergePass(TR, l->v, k, l->len, a); 63 k = 2 * k;//子序列加长 64 } 65 }
快速排序:
快速排序(Quick Sort)的基本思想是:通过一趟排序将待排记录分割成独立的两部分,其中一部分记录的关键字均比另一部分记录的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序的目的。
算法思想:
从待排序记录中选择一个记录为枢纽,设为K,将其余大于K的记录移动至K的后面,小于K的移动至前面,此过程称为一趟快速排序。当然就是对接下来的两个字表进行相同的操作,直到子表的长度不超过1
算法时间复杂度:
O(Knlog2n),K为常数因子,且在所有O(nlogn)复杂度中,快排的K值最小
1 /**************************快速排序****************************/ 2 /* 交换顺序表L中子表的记录,使枢轴记录到位,并返回其所在位置 */ 3 /* 此时在它之前(后)的记录均不大(小)于它。 */ 4 //即处于枢轴的数的左边比其小,右边比其大 5 int Partiton(SqList* &l, int pot, int len, int(&a)[2]) { 6 int pkey; 7 pkey = l->v[pot];//假设第一个位置的数就是枢轴的位置 8 while (pot < len) { 9 a[0]++; 10 while (pot < len&&l->v[len] >= pkey)//确保右边大于关键数 11 len--; 12 a[1]++; 13 swap(l->v[pot], l->v[len]);//将比关键数小的数放置到关键数的左边 14 a[0]++; 15 while (pot < len&&l->v[pot] <= pkey)//确保左边的 数小于关键数 16 pot++; 17 a[1]++; 18 swap(l->v[pot], l->v[len]);//将比关键数大的数放置到关键数的右边 19 } 20 return pot;//返回关键数的位置 21 } 22 23 /* 对顺序表L中的子序列L->r[low..high]作快速排序 */ 24 void QSort(SqList* &l, int pot, int len, int(&a)[2]) { 25 int pivot; 26 if (pot < len) { 27 pivot = Partiton(l, pot, len, a);/* 将L->r[low..high]一分为二,算出枢轴值pivot */ 28 QSort(l, pot, pivot - 1, a);/* 对低子表递归排序 */ 29 QSort(l, pivot + 1, len, a);/* 对高子表递归排序 */ 30 } 31 } 32 void QuickSort(SqList* &l, int(&a)[2]) { 33 QSort(l, 1, l->len, a); 34 } 35 36 /**************************改进快速排序****************************/ 37 //快速排序的优化 38 int Partiton1(SqList* &l, int pot, int len, int(&a)[2]) { 39 int pkey; 40 41 /////改进之处,之间选中间作为关键数的位置 42 int m = pot + (len - pot) / 2; 43 a[0]++; 44 if (l->v[pot] > l->v[len]) { 45 swap(l->v[pot], l->v[len]);/* 交换左端与右端数据,保证左端较小 */ 46 a[1]++; 47 } 48 a[0]++; 49 if (l->v[m] > l->v[len]) { 50 swap(l->v[m], l->v[len]);/* 交换中间与右端数据,保证中间较小 */ 51 a[1]++; 52 } 53 a[0]++; 54 if (l->v[m] < l->v[pot]) { 55 swap(l->v[m], l->v[pot]);/* 交换中间与左端数据,保证左端较小 */ 56 a[1]++; 57 } 58 ///// 59 pkey = l->v[pot];//假设第一个位置的数就是枢轴的位置 60 l->v[0] = pkey;//保留 61 while (pot < len) {/*从表的两端交替地向中间扫描 */ 62 a[0]++; 63 while (pot < len&&l->v[len] >= pkey)//确保右边大于关键数 64 --len; 65 l->v[pot] = l->v[len]; 66 a[0]++; 67 while (pot < len&&l->v[pot] <= pkey)//确保左边的 数小于关键数 68 ++pot; 69 l->v[len] = l->v[pot]; 70 } 71 l->v[pot] = l->v[0]; 72 return pot;//返回关键数的位置 73 } 74 75 void QSort1(SqList* &l, int pot, int len, int(&a)[2]) { 76 int pivot; 77 if ((len - pot) > MAX_LENGTH_INSERT_SORT) { 78 while (pot < len) { 79 pivot = Partiton1(l, pot, len, a);/* 将L->r[low..high]一分为二,算出枢轴值pivot */ 80 QSort1(l, pot, pivot - 1, a);/* 对低子表递归排序 */ 81 pot = pivot + 1;//尾递归 82 } 83 } 84 else 85 InsertSort(l,a); 86 } 87 88 void QuickSort1(SqList* &l, int(&a)[2]) { 89 QSort1(l, 1, l->len, a); 90 }
总结:
从算法的简单性来看,我们将7种算法分为两类:
·简单算法:冒泡、简单选择、直接插入。
·改进算法:希尔、堆、归并、快速。
完整代码:
1 #include "000库函数.h" 2 3 #define MAX_LENGTH_INSERT_SORT 7 /* 用于快速排序时判断是否选用插入排序阙值 */ 4 #define MAXSIZE 100 /* 用于要排序数组个数最大值,可根据需要修改 */ 5 #define N 9 6 struct SqList 7 { 8 int v[MAXSIZE+1];//用于存储要排序的数组,r[0]作为哨兵或临时存放变量 9 int len;//记录顺序表的长度 10 }; 11 12 //数据交换 13 void swap(int &i, int &j) { 14 int temp; 15 temp = i; 16 i = j; 17 j = temp; 18 } 19 20 /**************************简单排序****************************/ 21 //初级冒泡排序,就是一个一个与后面对比 22 void BubbleSort0(SqList* &l,int (&a)[2]) { 23 for (int i = 1; i < l->len; ++i) { 24 for (int j = i + 1; j <= l->len; ++j) { 25 a[0]++; 26 if (l->v[i] > l->v[j]){ 27 swap(l->v[i], l->v[j]); 28 a[1]++; 29 } 30 } 31 } 32 } 33 34 /**************************冒泡排序****************************/ 35 //冒泡排序,两两比较排序 36 void BubbleSort(SqList* &l, int(&a)[2]) { 37 for (int i = 1; i < l->len; ++i) { 38 for (int j = l->len - 1; j >= i; --j) { 39 a[0]++; 40 if (l->v[j + 1] < l->v[j]) { 41 swap(l->v[j + 1], l->v[j]); 42 a[1]++; 43 } 44 } 45 } 46 } 47 48 /**************************改进冒泡排序****************************/ 49 //改进冒泡排序,加入哨兵模式 50 void BubbleSort2(SqList* &l, int(&a)[2]) { 51 bool flag = true;//哨兵 52 for (int i = 1; flag&&i < l->len; ++i) { 53 flag = false; 54 for (int j = l->len - 1; j >= i; --j) { 55 a[0]++; 56 if (l->v[j + 1] < l->v[j]) { 57 a[1]++; 58 swap(l->v[j + 1], l->v[j]); 59 flag = true;//数据交换后才需要遍历 60 } 61 } 62 } 63 } 64 65 /**************************选择排序****************************/ 66 //选择排序,就是依次找出未排序中的最小值放入前面 67 void SelectSort(SqList* &l, int(&a)[2]) { 68 int i, j, min; 69 for (i = 1; i < l->len; ++i) { 70 min = i; 71 for (j = i + 1; j < l->len; ++j) { 72 a[0]++; 73 if (l->v[min] > l->v[j]) 74 min = j; 75 } 76 if (i != min) { 77 swap(l->v[min], l->v[i]); 78 a[1]++; 79 } 80 } 81 } 82 83 /**************************插值排序****************************/ 84 //插值排序,就是从前向后两两比较,若第二个数为小的数,然后将该数记住, 85 //从此位置从后向前比较该数,并将比他大的数向后挪,直到找到存放该数的位置 86 //此算法需要一个辅助的临时空间存放: 87 88 void InsertSort(SqList* &l, int(&a)[2]) { 89 int i, j; 90 for (int i = 2; i < l->len; ++i) { 91 a[0]++; 92 if (l->v[i] < l->v[i - 1]) {//找到了前比后大的数 93 l->v[0] = l->v[i];//设置哨兵 94 for (j = i - 1; l->v[0] < l->v[j]; --j) {//返回比较 95 a[1]++; 96 l->v[j + 1] = l->v[j];//大的数向后挪 97 } 98 l->v[j+1] = l->v[0]; 99 } 100 } 101 } 102 103 /**************************希尔排序****************************/ 104 //希尔排序 105 //与插值排序很像,不过是间隔比较 106 void ShellSort(SqList* &l, int(&a)[2]) { 107 int gap = l->len; 108 int i, j; 109 do { 110 gap = gap / 3 + 1;//间隔大小 111 for (i = gap + 1; i < l->len; ++i) { 112 a[0]++; 113 if (l->v[i] < l->v[i - gap]) { 114 l->v[0] = l->v[i]; 115 for (j = i - gap; j > 0 && l->v[0] < l->v[j]; j -= gap) {//向前去排序 116 l->v[j + gap] = l->v[j];//大数向后移; 117 a[1]++; 118 } 119 l->v[j + gap] = l->v[0];//插入 120 } 121 } 122 } while (gap > 1); 123 } 124 125 /**************************************堆排序*****************************/ 126 //调整为大根堆 127 void HeapAdjust(SqList* &l, int n, int len) { 128 int temp = l->v[n]; 129 for (int i = 2 * n; i <= len; i *= 2) {//沿着关键字中较大的孩子结构向在筛选 130 if (i < len&&l->v[i] < l->v[i + 1])++i;//i为关键字中较大的记录的下标 131 if (temp >= l->v[i])break;//找到插入的位置了 132 l->v[n] = l->v[i];// 133 n = i; 134 } 135 l->v[n] = temp;//进行插入 136 } 137 138 //堆排序 139 void HeapSort(SqList* &l, int(&a)[2]) { 140 for (int i = l->len / 2; i > 0; --i) {//把数组中的数据变为一个大堆 141 a[0]++; 142 HeapAdjust(l, i, l->len); 143 } 144 for (int i = l->len; i > 1; --i) { 145 a[1]++; 146 swap(l->v[1], l->v[i]);/* 将堆顶记录和当前未经排序子序列的最后一个记录交换 */ 147 HeapAdjust(l, 1, i - 1);/* 将L->r[1..i-1]重新调整为大根堆 */ 148 } 149 } 150 151 /**************************递归法归并+排序****************************/ 152 /* 将有序的SR[i..m]和SR[m+1..n]归并为有序的TR[i..n] */ 153 //归并 154 void Merge(int *SR, int *TR, int pot, int m, int len) { 155 int i,j,k; 156 for (i = m + 1, k = pot; pot <= m && i <= len; ++k) {/*将SR中记录由小到大地并入TR * /*/ 157 if (SR[pot] < SR[i])TR[k] = SR[pot++]; 158 else TR[k] = SR[i++]; 159 } 160 if (pot <= m) 161 for (j = 0; j <= m; ++j) 162 TR[k + j] = SR[pot + j]; /* 将剩余的SR[i..m]复制到TR */ 163 if (i <= len) 164 for (j = 0; j <= len - i; ++j) 165 TR[k + j] = SR[i + j];/* 将剩余的SR[j..n]复制到TR */ 166 } 167 //进行递归 168 void MSort(int *SR,int*TR1,int pot, int len, int(&a)[2]) { 169 int TR2[MAXSIZE + 1]; 170 if (len == pot) 171 TR1[pot] = SR[pot]; 172 else { 173 int m = (len + pot) / 2; //将原数组平分两个数组 174 a[0]++; 175 MSort(SR, TR2, pot, m, a); /* 递归地将SR[pot..m]归并为有序的TR2[pot..m] */ 176 MSort(SR, TR2, m + 1, len, a); /* 递归地将SR[m+1..len]归并为有序的TR2[m+1..len] */ 177 Merge(TR2, TR1, pot, m, len); /* 将TR2[pot..m]和TR2[m+1..t]归并到TR1[pot..len] */ 178 a[1]++; 179 } 180 181 } 182 183 //递归法归并排序 184 void MergeSort(SqList* &l, int(&a)[2]) { 185 MSort(l->v,l->v,1, l->len,a); 186 } 187 188 /**************************非递归法归并排序****************************/ 189 /*将SR[]中相邻长度为s的子序列两两归并到TR[] * /*/ 190 void MergePass(int *SR, int *TR, int k, int len, int(&a)[2]) { 191 int i = 1, j; 192 a[1]++; 193 while (i <= len - 2 * k + 1) { 194 //两两归并 195 Merge(SR, TR, i, i + k - 1, len); 196 i = i + 2 * k; 197 } 198 if (i < len - k + 1)//归并最后两个序列 199 Merge(SR, TR, i, i + k - 1, len); 200 else//最后只剩下单个序列 201 for (j = i; j <= len; ++j) 202 TR[j] = SR[j]; 203 } 204 //归并排序 205 void MergeSort2(SqList* &l, int(&a)[2]) { 206 int *TR = new int[l->len]; 207 int k = 1; 208 while (k < l->len) { 209 a[0]++; 210 MergePass(l->v, TR, k, l->len, a); 211 k = k * 2;//加长子序列 212 MergePass(TR, l->v, k, l->len, a); 213 k = 2 * k;//子序列加长 214 } 215 } 216 217 /**************************快速排序****************************/ 218 /* 交换顺序表L中子表的记录,使枢轴记录到位,并返回其所在位置 */ 219 /* 此时在它之前(后)的记录均不大(小)于它。 */ 220 //即处于枢轴的数的左边比其小,右边比其大 221 int Partiton(SqList* &l, int pot, int len, int(&a)[2]) { 222 int pkey; 223 pkey = l->v[pot];//假设第一个位置的数就是枢轴的位置 224 while (pot < len) { 225 a[0]++; 226 while (pot < len&&l->v[len] >= pkey)//确保右边大于关键数 227 len--; 228 a[1]++; 229 swap(l->v[pot], l->v[len]);//将比关键数小的数放置到关键数的左边 230 a[0]++; 231 while (pot < len&&l->v[pot] <= pkey)//确保左边的 数小于关键数 232 pot++; 233 a[1]++; 234 swap(l->v[pot], l->v[len]);//将比关键数大的数放置到关键数的右边 235 } 236 return pot;//返回关键数的位置 237 } 238 239 /* 对顺序表L中的子序列L->r[low..high]作快速排序 */ 240 void QSort(SqList* &l, int pot, int len, int(&a)[2]) { 241 int pivot; 242 if (pot < len) { 243 pivot = Partiton(l, pot, len, a);/* 将L->r[low..high]一分为二,算出枢轴值pivot */ 244 QSort(l, pot, pivot - 1, a);/* 对低子表递归排序 */ 245 QSort(l, pivot + 1, len, a);/* 对高子表递归排序 */ 246 } 247 } 248 void QuickSort(SqList* &l, int(&a)[2]) { 249 QSort(l, 1, l->len, a); 250 } 251 252 /**************************改进快速排序****************************/ 253 //快速排序的优化 254 int Partiton1(SqList* &l, int pot, int len, int(&a)[2]) { 255 int pkey; 256 257 /////改进之处,之间选中间作为关键数的位置 258 int m = pot + (len - pot) / 2; 259 a[0]++; 260 if (l->v[pot] > l->v[len]) { 261 swap(l->v[pot], l->v[len]);/* 交换左端与右端数据,保证左端较小 */ 262 a[1]++; 263 } 264 a[0]++; 265 if (l->v[m] > l->v[len]) { 266 swap(l->v[m], l->v[len]);/* 交换中间与右端数据,保证中间较小 */ 267 a[1]++; 268 } 269 a[0]++; 270 if (l->v[m] < l->v[pot]) { 271 swap(l->v[m], l->v[pot]);/* 交换中间与左端数据,保证左端较小 */ 272 a[1]++; 273 } 274 ///// 275 pkey = l->v[pot];//假设第一个位置的数就是枢轴的位置 276 l->v[0] = pkey;//保留 277 while (pot < len) {/*从表的两端交替地向中间扫描 */ 278 a[0]++; 279 while (pot < len&&l->v[len] >= pkey)//确保右边大于关键数 280 --len; 281 l->v[pot] = l->v[len]; 282 a[0]++; 283 while (pot < len&&l->v[pot] <= pkey)//确保左边的 数小于关键数 284 ++pot; 285 l->v[len] = l->v[pot]; 286 } 287 l->v[pot] = l->v[0]; 288 return pot;//返回关键数的位置 289 } 290 291 void QSort1(SqList* &l, int pot, int len, int(&a)[2]) { 292 int pivot; 293 if ((len - pot) > MAX_LENGTH_INSERT_SORT) { 294 while (pot < len) { 295 pivot = Partiton1(l, pot, len, a);/* 将L->r[low..high]一分为二,算出枢轴值pivot */ 296 QSort1(l, pot, pivot - 1, a);/* 对低子表递归排序 */ 297 pot = pivot + 1;//尾递归 298 } 299 } 300 else 301 InsertSort(l,a); 302 } 303 304 void QuickSort1(SqList* &l, int(&a)[2]) { 305 QSort1(l, 1, l->len, a); 306 } 307 308 309 /*************************************************************/ 310 int T036(){ 311 /* int d[N]={9,1,5,8,3,7,4,6,2}; */ 312 int d[N] = { 50,10,90,30,70,40,80,60,20 }; 313 /* int d[N]={9,8,7,6,5,4,3,2,1}; */ 314 int count[2] = { 0,0 };//记录比较次数和交换次数 315 SqList *l0, *l1, *l2, *l3, *l4, *l5, *l6, *l7, *l8, *l9, *l10; 316 317 l0 = new SqList; 318 for (int i = 0; i < N; i++) 319 l0->v[i + 1] = d[i]; 320 l0->len = N; 321 322 l1 = l2 = l3 = l4 = l5 = l6 = l7 = l8 = l9 = l10 = l0; 323 printf("排序前:\n"); 324 for(int i=1;i<=N;++i) 325 cout << l0->v[i] << " "; 326 cout << endl; 327 328 printf("初级冒泡排序:\n"); 329 BubbleSort0(l0,count); 330 for (int i = 1; i <= N; ++i) 331 cout << l0->v[i] << " "; 332 cout << endl; 333 cout << "比较次数: " << count[0] << " ; 交换次数: " << count[1] << endl; 334 count[0] = count[1] = 0;//归0 335 336 printf("冒泡排序:\n"); 337 BubbleSort(l1,count); 338 for (int i = 1; i <= N; ++i) 339 cout << l1->v[i] << " "; 340 cout << endl; 341 cout << "比较次数: " << count[0] << " ; 交换次数: " << count[1] << endl; 342 count[0] = count[1] = 0;//归0 343 344 printf("改进冒泡排序:\n"); 345 BubbleSort2(l2,count); 346 for (int i = 1; i <= N; ++i) 347 cout << l2->v[i] << " "; 348 cout << endl; 349 cout << "比较次数: " << count[0] << " ; 交换次数: " << count[1] << endl; 350 count[0] = count[1] = 0;//归0 351 352 printf("选择排序:\n"); 353 SelectSort(l3,count); 354 for (int i = 1; i <= N; ++i) 355 cout << l3->v[i] << " "; 356 cout << endl; 357 cout << "比较次数: " << count[0] << " ; 交换次数: " << count[1] << endl; 358 count[0] = count[1] = 0;//归0 359 360 printf("直接插入排序:\n"); 361 InsertSort(l4, count); 362 for (int i = 1; i <= N; ++i) 363 cout << l4->v[i] << " "; 364 cout << endl; 365 cout << "比较次数: " << count[0] << " ; 交换次数: " << count[1] << endl; 366 count[0] = count[1] = 0;//归0 367 368 printf("希尔排序:\n"); 369 ShellSort(l5, count); 370 for (int i = 1; i <= N; ++i) 371 cout << l5->v[i] << " "; 372 cout << endl; 373 cout << "比较次数: " << count[0] << " ; 交换次数: " << count[1] << endl; 374 count[0] = count[1] = 0;//归0 375 376 printf("堆排序:\n"); 377 HeapSort(l6, count); 378 for (int i = 1; i <= N; ++i) 379 cout << l6->v[i] << " "; 380 cout << endl; 381 cout << "比较次数: " << count[0] << " ; 交换次数: " << count[1] << endl; 382 count[0] = count[1] = 0;//归0 383 384 printf("归并排序(递归):\n"); 385 MergeSort(l7, count); 386 for (int i = 1; i <= N; ++i) 387 cout << l7->v[i] << " "; 388 cout << endl; 389 cout << "比较次数: " << count[0] << " ; 交换次数: " << count[1] << endl; 390 count[0] = count[1] = 0;//归0 391 392 printf("归并排序(非递归):\n"); 393 MergeSort2(l8, count); 394 for (int i = 1; i <= N; ++i) 395 cout << l8->v[i] << " "; 396 cout << endl; 397 cout << "比较次数: " << count[0] << " ; 交换次数: " << count[1] << endl; 398 count[0] = count[1] = 0;//归0 399 400 printf("快速排序:\n"); 401 QuickSort(l9, count); 402 for (int i = 1; i <= N; ++i) 403 cout << l9->v[i] << " "; 404 cout << endl; 405 cout << "比较次数: " << count[0] << " ; 交换次数: " << count[1] << endl; 406 count[0] = count[1] = 0;//归0 407 408 printf("改进快速排序:\n"); 409 QuickSort1(l10, count); 410 for (int i = 1; i <= N; ++i) 411 cout << l10->v[i] << " "; 412 cout << endl; 413 cout << "比较次数: " << count[0] << " ; 交换次数: " << count[1] << endl; 414 count[0] = count[1] = 0;//归0 415 416 417 /*大数据排序*/ 418 /* 419 srand(time(0)); 420 int Max=10000; 421 int d[10000]; 422 int i; 423 SqList l0; 424 for(i=0;i<Max;i++) 425 d[i]=rand()%Max+1; 426 for(i=0;i<Max;i++) 427 l0.r[i+1]=d[i]; 428 l0.length=Max; 429 MergeSort(l0); 430 print(l0); 431 */ 432 return 0; 433 }