目录
1.6.7 快速排序——非递归版(排序可复用挖坑法、左右指针法、前后指针法)
后记:●由于作者水平有限,文章难免存在谬误之处,敬请读者斧正,俚语成篇,恳望指教!
详细实现链接:
<排序>《数据结构(C语言版)》
<堆及堆排序>《数据结构(C语言版)》
1.分析实现逻辑,学习不同实现思想及写法:
1.1插入排序(直接插入):
- 时间复杂度:O(N^2)
- 最坏情况:逆序O(N^2)
- 最好情况:全部顺序 O(N)
空间复杂度:O(1)
稳定
1.2 希尔排序:
- 时间复杂度:O(N*logN)~ O(N^2)
- 最好情况:O(N^1.3)
- 最坏情况:O(N^2)
- 空间复杂度:O(1)
- 不稳定
在直接插入排序的基础上优化:
- 1.先进行预排序(分组排序),让数据接近有序
- 2.再直接插入排序,
多组间隔为gap的预排序,gap由大变小,
- gap越大(以升序举例),大的数可以越快的到后面,小的数可以越快的到前面。但预排后越不接近有序
- gap越小,越接近有序
时间复杂度:O(N*logN)~O(N^2)
- gap很大时,下面预排序为O(N)
- gap很小时,数据已接近有序,近似为O(N)
1.3 堆排序:
- 时间复杂度:O(N*logN)
- 最好情况:O(N*logN)
- 最坏情况:O(N*logN)
- 空间复杂度:O(1)
- 不稳定
给一组数据(假设存储在动态数组里),若要进行堆排序(若打印为升序),且采用向下调整,那么就把这组数据建大堆,再进行调整!然后根据下标遍历数组,即可打印为升序!
- 大堆:堆顶数据是最大的。
- 小堆:堆顶元素是最小的。
建堆:
- 倒数第一个非叶子结点(即叶子父亲的结点)开始调整。
- 建堆的时间复杂度:O(N)
调整方法:
- 向上调整法:
- 向下调整算法:
- 前提:左右子树必须是小堆。
- 最多调整高度次:logN次
这里注意:
调整分为:向上调整法、向下调整法。
建堆分为:建大堆、建小堆。
注意:
若将最后的数据按照升序打印,若采用向下调整法,则选择建成大堆(只保证了父结点与子节点的相对大小,相邻没关系,这里只是建堆,而非调整后的最终形态),再从倒数第一个非叶子结点(即叶子父亲的结点)开始调整,最终调整后,再每次输出堆顶元素,打印(因为存储在数组,遍历下标打印,类似于层序遍历的操作)的就是升序!
其实:有4种情况:
(1)建小堆,向上调整,最终为大堆-----用于堆排降序(可以实现,但多此一举!)
(2)建大堆,向上调整,最终为小堆-----用于堆排升序
(3)建小堆,向下调整,最终为大堆-----用于堆排降序(可以实现,但多此一举!)
(4)建大堆,向下调整,最终为小堆-----用于堆排升序
在进行堆排序时,若想数据为升序,则建大堆,采用向下调整法。当把最大的换到最后,就不再将其作为接下来的调整对象。
由于向上调整法的时间复杂度比向下调整法的时间复杂度高,因此一般采用向下调整法。
时间复杂度:
关于堆的选择题快速判断:
(1)建小堆,向上调整,最终为大堆-----用于堆排降序(可以实现,但多此一举!)
(2)建大堆,向上调整,最终为小堆-----用于堆排升序
(3)建小堆,向下调整,最终为大堆-----用于堆排降序(可以实现,但多此一举!)
(4)建大堆,向下调整,最终为小堆-----用于堆排升序
1.4 直接选择排序:
- 时间复杂度:O(N^2)
- 最好情况:O(N^2)
- 最坏情况:O(N^2)
- 空间复杂度:O(1)
- 不稳定
1.5 冒泡排序:
- 时间复杂度:O(N^2)
- 最好情况:O(N)
- 最坏情况:O(N^2)
- 空间复杂度:O(1)
- 稳定
1.6 快速排序:
- 时间复杂度:O(N*logN)
- 最好情况:O(N*logN)
- 最坏情况:O(N^2)
- 空间复杂度:O(logN)~ O(N)
- 不稳定
1.6.1 单趟时间复杂度:O(N)
1.6.2 快速排序——挖坑法:
时间复杂度:
1.6.3 快速排序——挖坑法优化(三数取中)
时间复杂度:
1.6.4 快速排序——挖坑法优化(三数取中+小区间优化)
时间复杂度:
1.6.5 快速排序——左右指针法
时间复杂度:
1.6.6 快速排序——前后指针法
时间复杂度:
1.6.7 快速排序——非递归版(排序可复用挖坑法、左右指针法、前后指针法)
时间复杂度:
1.7 归并排序
- 时间复杂度:O(N*logN)
- 最好情况:O(N*logN)
- 最坏情况:O(N*logN)
- 空间复杂度:O(N)
- 稳定
1.7.1 归并排序——递归版:
时间复杂度:
1.7.2 归并排序——非递归版:
时间复杂度:
1.8计数排序
时间复杂度:O(range+N)
空间复杂度:O(range)
2. 完整源码:
Sort.h:
#pragma once #include<stdio.h> #include<stdlib.h> void PrintArray(int* a, int n); //打印 void InsertSort(int* a, int n); //插入排序 void ShellSort(int* a, int n); //希尔排序 void HeapSort(int* a, int n); //堆排序 void SelectSort(int* a, int n); //选择排序 void BubbleSort(int* a, int n); //冒泡排序 //void QuickSort(int* a, int n); //快速排序——写法1:挖坑法(单趟分析版) void QuickSort(int* a, int left, int right); //快速排序——写法1:挖坑法(实现排序版) void MergeSort(int* a, int n); //归并排序 void QuickSortNonR(int* a, int n); //快速排序(非递归版) void MergeSortNonR(int* a, int n); //归并排序(非递归版) void CountSort(int* a, int n); //计数排序
Sort.c:
#include"Sort.h" #include"Stack.h" //这里采用升序讲解 //打印 void PrintArray(int* a, int n) { for (int i = 0; i < n; i++) { printf("%d ", a[i]); } printf("\n"); } /// //插入排序 //时间复杂度:O(N^2) //最坏情况:逆序 //最好情况:全部顺序 O(N) void InsertSort(int* a, int n) { //假设[0,end]有序,将end+1位置的值插入进去,让[0,end+1]有序 for (int i = 0; i < n - 1; i++) { int end = i; int tmp = a[end + 1]; while (end >= 0) { if (a[end] > tmp) //升序 //if (a[end] < tmp) //降序 { a[end + 1] = a[end]; --end; } else { break; } } a[end + 1] = tmp; } } // //希尔排序 //在直接插入排序的基础上优化 //1.先进行预排序(分组排序),让数据接近有序 //2.再直接插入排序, //多组间隔为gap的预排序,gap由大变小, //gap越大(以升序举例),大的数可以越快的到后面,小的数可以越快的到前面。但预排后越不接近有序 //gap越小,越接近有序 //时间复杂度:O(logN*N)或者O(log3N*N) //gap很大时,下面预排序为O(N) //gap很小时,数据已接近有序,近似为ON) //平均时间复杂度:O(N^1.3) void ShellSort(int* a, int n) { int gap = n; //可以自行设置,但不会给固定的值 //把间隔为gap的多组数据同时排序(注意:理解这个多组的含义) //end最终位置:n-gap-1 //gap的设置方式之一: while(gap > 1) { //gap=gap/2; //一定会保证最后一次为1 //运行了logN次 gap = gap / 3 + 1; //但要保证最后一次为1 //运行了log3N次 //gap>1时都是预排序,排序后接近有序 //gap==1时,就是直接插入排序 for (int i = 0; i < n - gap; i++) { int end = i; int tmp = a[end + gap]; while (end >= 0) { if (a[end] > tmp) { a[end + gap] = a[end]; end -= gap; } else { break; } } a[end + gap] = tmp; } } } / //堆排序 void Swap(int* p1, int* p2) { int tmp = *p1; *p1 = *p2; *p2 = tmp; } //(1)建小堆,向上调整,最终为大堆---- - 用于堆排降序 //(2)建大堆,向上调整,最终为小堆---- - 用于堆排升序 //(3)建小堆,向下调整,最终为大堆---- - 用于堆排降序(可以实现,多此一举) //(4)建大堆,向下调整,最终为小堆---- - 用于堆排升序 //注:由于向上调整法的时间复杂度比向下调整法的时间复杂度高,因此一般采用向下调整法。 //堆排序,采用数组存储,操作下标。但逻辑要是二叉树 //把数据建成堆 //1.堆排序调整-向下调整法 void AdjustDwon(int* a, int n, int root) { int parent = root; int child = parent * 2 + 1; // 默认是左孩子 //1.建大堆,再调整 while (child < n) { // 1、选出左右孩子中大的那一个 if (child + 1 < n && a[child + 1] > a[child]) { child += 1; } if (a[child] > a[parent]) { Swap(&a[child], &a[parent]); parent = child; child = parent * 2 + 1; } else { break; } } 2.建小堆,再调整 //while (child < n) //{ // // 1、选出左右孩子中小的那一个 // if (child + 1 < n && a[child + 1] < a[child]) // { // child += 1; // } // if (a[child] < a[parent]) // { // Swap(&a[child], &a[parent]); // parent = child; // child = parent * 2 + 1; // } // else // { // break; // } //} } //2.堆排序调整-向上调整法 void AdjustUp(int* a, int child) { int parent = (child - 1) / 2; //1.建大堆,再调整 while (child > 0) { if (a[child] > a[parent]) { Swap(&a[child], &a[parent]); child = parent; parent = (child - 1) / 2; } else { break; } } 2.建小堆,再调整 //while (child > 0) //{ // if (a[child] < a[parent]) // { // Swap(&a[child], &a[parent]); // child = parent; // parent = (child - 1) / 2; // } // else // { // break; // } //} } //堆排序(建堆) // 升序,建小堆?还是大堆? -> 大堆 // 建堆只是第一步,并非最终形态,还要进行调整(一般选择向下调整) // 整体时间复杂度O(N*logN) void HeapSort(int* a, int n) { // 建堆 时间复杂度:O(N) //调整 1.向上调整法 //for (int i = 1; i < n; ++i) //{ // AdjustUp(a, i); //} //2.向下调整法 for (int i = (n - 1 - 1) / 2; i >= 0; --i) { AdjustDwon(a, n, i); } // 排升序,采用向下调整法,那么,建大堆还是小堆? -->建大堆 int end = n - 1; while (end > 0) { Swap(&a[0], &a[end]); AdjustDwon(a, end, 0); --end; } } / //直接选择排序,时间复杂度O(N*N) // 很差,因为最好情况也是O(N*N) // N // N-2 // N-4 // ... void SelectSort(int* a, int n) { int begin = 0, end = n - 1; while (begin < end) { int mini = begin, maxi = begin; for (int i = begin; i <= end; ++i) { if (a[i] < a[mini]) { mini = i; } if (a[i] > a[maxi]) { maxi = i; } } Swap(&a[begin], &a[mini]); // 如果begin跟maxi重叠,需要修正一下maxi的位置 if (begin == maxi) { maxi = mini; } Swap(&a[maxi], &a[end]); ++begin; --end; } } / //冒泡排序 // 时间复杂度:O(N*N) // 最好情况:O(N) // N-1 // N-2 // ... // 跟直接插入排序相比?谁更好 -> 直接插排序入更好 void BubbleSort(int* a, int n) { //写法1: for (int j = 0; j < n; ++j) { int exchange = 0; for (int i = 1; i < n - j; ++i) { if (a[i - 1] > a[i]) { Swap(&a[i - 1], &a[i]); exchange = 1; } } if (exchange == 0) { break; } } 写法2: //int end = n; //while (end > 0) //{ // for (int i = 1; i < end; ++i) // { // if (a[i - 1] > a[i]) // { // Swap(&a[i - 1], &a[i]); // } // } // --end; //} } / 快速排序——写法1:挖坑法(单趟分析版) 单趟排序时间复杂度:O(N) //void QuickSort(int* a, int n) //{ // int begin = 0, end = n - 1; // int pivot = begin; // int key = a[begin]; //指定了第一次关键字为begin // // while (begin < end) // { // //右边找小,放到左边 // while (begin < end && a[end] >= key) // { // --end; // } // //小的放到左边的坑里,自己形成新的坑位 // a[pivot] = a[end]; // pivot = end; // //左边找大 // while (begin < end && a[begin] <= key) // { // ++begin; // } // //大的放到左边的坑里,自己形成新的坑位 // a[pivot] = a[begin]; // pivot = begin; // // } // pivot = begin; // a[pivot] = key; //} 快速排序——写法1:挖坑法(实现排序版) 时间复杂度: 最坏情况:有序-->O O(N^2) ,性能相当于插入排序。 解决:“三数取中法”,使其取得key不是最大或最小 //void QuickSort(int* a, int left, int right) //{ // if (left >= right) // return; // int begin = left, end = right; // int pivot = begin; // int key = a[begin]; //指定了第一次关键字为begin // // while (begin < end) //单趟排序 // { // //右边找小,放到左边 // while (begin < end && a[end] >= key) // { // --end; // } // //小的放到左边的坑里,自己形成新的坑位 // a[pivot] = a[end]; // pivot = end; // //左边找大 // while (begin < end && a[begin] <= key) // { // ++begin; // } // //大的放到左边的坑里,自己形成新的坑位 // a[pivot] = a[begin]; // pivot = begin; // // } // pivot = begin; // a[pivot] = key; // // // [left, right] // // [left, pivot-1] pivot [pivot+1, right] // // 左子区间和右子区间有序,我们就有序了,如果让他们有序呢? 分治递归 // QuickSort(a, left, pivot - 1); // QuickSort(a, pivot + 1, right); //} // 快速排序——写法2:挖坑法优化--->(三数取中,解决原始数据有序情况) 三数取中 //int GetMidIndex(int* a, int left, int right) //{ // int mid = (left + right) >> 1; // if (a[left] < a[mid]) // { // if (a[mid] < a[right]) // { // return mid; // } // else if (a[left] > a[right]) // { // return left; // } // else // { // return right; // } // } // else // a[left] > a[mid] // { // if (a[mid] > a[right]) // { // return mid; // } // else if (a[left] < a[right]) // { // return left; // } // else // { // return right; // } // } //} //void QuickSort(int* a, int left, int right) //{ // if (left >= right) // return; // int keyIndex = GetMidIndex(a, left, right); //三数取中,解决原始数据有序的性能下降 // Swap(&a[left], &a[keyIndex]); // // int begin = left, end = right; // int pivot = begin; // int key = a[begin]; // // while (begin < end) //单趟排序 O(N) // { // // 右边找小,放到左边 // while (begin < end && a[end] >= key) // --end; // // // 小的放到左边的坑里,自己形成新的坑位 // a[pivot] = a[end]; // pivot = end; // // // 左边找大 // while (begin < end && a[begin] <= key) // ++begin; // // // 大的放到左边的坑里,自己形成新的坑位 // a[pivot] = a[begin]; // pivot = begin; // } // // pivot = begin; // a[pivot] = key; // // // [left, right] // // [left, pivot-1] pivot [pivot+1, right] // // 左子区间和右子区间有序,我们就有序了,如果让他们有序呢? 分治递归 // QuickSort(a, left, pivot - 1); // QuickSort(a, pivot + 1, right); //} 快速排序——写法3:挖坑法优化--->(三数取中,解决原始数据有序情况),再根据处理量分情况排序(小区间优化) 三数取中 //int GetMidIndex(int* a, int left, int right) //{ // int mid = (left + right) >> 1; // if (a[left] < a[mid]) // { // if (a[mid] < a[right]) // { // return mid; // } // else if (a[left] > a[right]) // { // return left; // } // else // { // return right; // } // } // else // a[left] > a[mid] // { // if (a[mid] > a[right]) // { // return mid; // } // else if (a[left] < a[right]) // { // return left; // } // else // { // return right; // } // } //} //void QuickSort(int* a, int left, int right) //{ // if (left >= right) // return; // int keyIndex = GetMidIndex(a, left, right); // Swap(&a[left], &a[keyIndex]); // // int begin = left, end = right; // int pivot = begin; // int key = a[begin]; // // // O(N) // while (begin < end) // { // // 右边找小,放到左边 // while (begin < end && a[end] >= key) // --end; // // // 小的放到左边的坑里,自己形成新的坑位 // a[pivot] = a[end]; // pivot = end; // // // 左边找大 // while (begin < end && a[begin] <= key) // ++begin; // // // 大的放到左边的坑里,自己形成新的坑位 // a[pivot] = a[begin]; // pivot = begin; // } // // pivot = begin; // a[pivot] = key; // // // 小区间优化 // //这个值会随原始数据量改变,而官方给13左右 // if (pivot - 1 - left > 10) //官方给13左右 // { // QuickSort(a, left, pivot - 1); // } // else // { // //InsertSort(int* a, int n) // InsertSort(a + left, pivot - 1 - left + 1); //left不一定从0开始,传数据个数 // } // // if (right - (pivot + 1) > 10) // { // QuickSort(a, pivot + 1, right); // } // else // { // InsertSort(a + pivot + 1, right - (pivot + 1) + 1); // } //} 快速排序——写法4:左右指针法:找小找大交换 三数取中 //int GetMidIndex(int* a, int left, int right) //{ // int mid = (left + right) >> 1; // if (a[left] < a[mid]) // { // if (a[mid] < a[right]) // { // return mid; // } // else if (a[left] > a[right]) // { // return left; // } // else // { // return right; // } // } // else // a[left] > a[mid] // { // if (a[mid] > a[right]) // { // return mid; // } // else if (a[left] < a[right]) // { // return left; // } // else // { // return right; // } // } //} 左右指针法:找小找大交换 //void QuickSort(int* a, int left, int right) //{ // if (left >= right) // return; // // int index = GetMidIndex(a, left, right); // Swap(&a[left], &a[index]); // // int begin = left, end = right; // int keyi = begin; // // while (begin < end) // { // // 找小 // while (begin < end && a[end] >= a[keyi]) // { // --end; // } // // // 找大 // while (begin < end && a[begin] <= a[keyi]) // { // ++begin; // } // // Swap(&a[begin], &a[end]); // } // // Swap(&a[begin], &a[keyi]); // int key = begin; // // // 小区间优化 // //这个值会随原始数据量改变,而官方给13左右 // if (key - 1 - left > 10) //官方给13左右 // { // QuickSort(a, left, key - 1); // } // else // { // //InsertSort(int* a, int n) // InsertSort(a + left, key - 1 - left + 1); //left不一定从0开始,传数据个数 // } // // if (right - (key + 1) > 10) // { // QuickSort(a, key + 1, right); // } // else // { // InsertSort(a + key + 1, right - (key + 1) + 1); // } //} //快速排序——写法5:前后指针法:cur找小,每次找到比keyi小额值,就++prev,然后交换prev与cur的位置交换 //1.会有自己与自己交换的情况,2.也有异步交换,3.最后实现左边全为小于key,右边全为大于key // 三数取中 int GetMidIndex(int* a, int left, int right) { int mid = (left + right) >> 1; if (a[left] < a[mid]) { if (a[mid] < a[right]) { return mid; } else if (a[left] > a[right]) { return left; } else { return right; } } else // a[left] > a[mid] { if (a[mid] > a[right]) { return mid; } else if (a[left] < a[right]) { return left; } else { return right; } } } //前后指针法 void QuickSort(int* a, int left, int right) { if (left >= right) return; int index = GetMidIndex(a, left, right); Swap(&a[left], &a[index]); int keyi = left; int prev = left, cur = left + 1; while (cur <= right) { if (a[cur] < a[keyi] && ++prev != cur) //++prev != cur:优化自己与自己交换的情况 { Swap(&a[prev], &a[cur]); } ++cur; } Swap(&a[keyi], &a[prev]); int key = prev; // 小区间优化 //这个值会随原始数据量改变,而官方给13左右 if (key - 1 - left > 10) //官方给13左右 { QuickSort(a, left, key - 1); } else { //InsertSort(int* a, int n) InsertSort(a + left, key - 1 - left + 1); //left不一定从0开始,传数据个数 } if (right - (key + 1) > 10) { QuickSort(a, key + 1, right); } else { InsertSort(a + key + 1, right - (key + 1) + 1); } } //递归的缺陷:若递归的深度太深,程序没错,但是栈的空间不够用,会导致溢出 //递归改非递归:方法1:(简单的)可以直接改循环 2.(复杂一点)使用数据结构的栈进行模拟递归过程 //快速排序——非递归版 //(复杂一点)使用数据结构的栈进行模拟递归过程:不会再有栈溢出问题,也会有空间消耗 //但数据结构中的栈是malloc出来的,开辟在堆(操作系统对内存的划分),而堆的空间比栈大 void QuickSortNonR(int* a, int n) { ST st; StackInit(&st); StackPush(&st, n - 1); StackPush(&st, 0); while (!StackEmpty(&st)) { int left = StackTop(&st); StackPop(&st); int right = StackTop(&st); StackPop(&st); //调用单趟排序 int index = GetMidIndex(a, left, right); //Swap(&a[left], &a[index]); int keyi = left; int prev = left, cur = left + 1; while (cur <= right) { if (a[cur] < a[keyi] && ++prev != cur) //++prev != cur:优化自己与自己交换的情况 { Swap(&a[prev], &a[cur]); } ++cur; } Swap(&a[keyi], &a[prev]); int keyIndex = prev; //栈里面的就需要单趟分割排序 //[left,keyIndex-1] keyIndex [keyIndex+1,right] if (keyIndex + 1 < right) { StackPush(&st, right); StackPush(&st, keyIndex+1); } if (left < keyIndex - 1) { StackPush(&st, keyIndex - 1); StackPush(&st, left); } } StackDestory(&st); } /// //归并排序 //1.假设左半区间、右半区间有序,依次对比取小的放到新的临时数组 //2.若左半区间、右半区间没有序,则递归进行 void _MergeSort(int* a, int left, int right, int* tmp) { if (left >= right) return; int mid = (left + right) >> 1; //假设 [left, mid] [mid+1, right]有序,那么我们就可以归并了 _MergeSort(a, left, mid, tmp); _MergeSort(a, mid + 1, right, tmp); // 归并 int begin1 = left, end1 = mid; int begin2 = mid + 1, end2 = right; int index = left; while (begin1 <= end1 && begin2 <= end2) { if (a[begin1] < a[begin2]) { tmp[index++] = a[begin1++]; } else { tmp[index++] = a[begin2++]; } } while (begin1 <= end1) { tmp[index++] = a[begin1++]; } while (begin2 <= end2) { tmp[index++] = a[begin2++]; } // 拷贝回去 for (int i = left; i <= right; ++i) { a[i] = tmp[i]; } } void MergeSort(int* a, int n) { int* tmp = (int*)malloc(sizeof(int) * n); //空间复杂度为O(N) _MergeSort(a, 0, n - 1, tmp); free(tmp); } //归并排序——非递归版 //(复杂一点)使用数据结构的栈进行模拟递归过程 //归并排序的非递归:先相邻两两归并,再四四归并,依次扩大 void MergeSortNonR(int* a, int n) { int* tmp = (int*)malloc(sizeof(int) * n); int gap = 1; //每组数据个数 while (gap < n) { for (int i = 0; i < n; i += 2 * gap) { //[i,i+gap-1] [i+gap,i+2*gap-1] // 归并 int begin1 = i, end1 = i + gap - 1; int begin2 = i + gap, end2 = i + 2 * gap - 1; //归并过程中右半区间可能就不存在 if (begin2 >= n) break; //归并过程中右半区间算多了,修正 if (end2 >= n) { end2 = n - 1; } int index = i; while (begin1 <= end1 && begin2 <= end2) { if (a[begin1] < a[begin2]) { tmp[index++] = a[begin1++]; } else { tmp[index++] = a[begin2++]; } } while (begin1 <= end1) { tmp[index++] = a[begin1++]; } while (begin2 <= end2) { tmp[index++] = a[begin2++]; } // 拷贝回去 for (int j = i; j < end2; ++j) { a[j] = tmp[j]; } } gap *= 2; } free(tmp); } //归并排序,也叫外排序,还可以对文件中数据进行排序 //假设10G的数据放到硬盘中,要排序可能内存不够,假设有1G内存可以用,如何排序? //依次读文件,每次读1G到内存中放到一个数组,用快排对其进行排序,再写到一个文件,2G、4G、8G归并需要借助在磁盘归并 //磁盘只能依次读数据 //非比较排序 // //基数排序(桶排序)实际中运用少,只对整数排序 // //计数排序 //统计数据个数,不进行比较 //时间复杂度:O(range+N) //空间复杂度:O(range) //说明计数排序适用于范围集中的数据 //使用相对映射,也可对负数进行排序 //字符串、浮点数等等不行 void CountSort(int* a, int n) { int min = a[0], max = a[0]; for (int i = 1; i < n; ++i) { if (a[i] < min) min = a[i]; if (a[i] > max) max = a[i]; } int range = max - min + 1; int* countA = (int*)malloc(sizeof(int) * range); assert(countA); memset(countA, 0, sizeof(int) * range); //计数 for (int i = 0; i < n; ++i) countA[a[i] - min]++; //排序 int j = 0; for (int i = 0; i < range; ++i) { while (countA[i]--) { a[j++] = i + min; } } }
Test.c:
#include"Sort.h" #include"Stack.h" void TestInsertSort() { int a[] = { 3, 5, 2, 7, 8, 6, 1, 9, 4, 0 }; PrintArray(a, sizeof(a) / sizeof(int)); InsertSort(a, sizeof(a) / sizeof(int)); PrintArray(a, sizeof(a) / sizeof(int)); } void TestShellSort() { int a[] = { 3, 5, 2, 7, 8, 6, 1, 9, 4, 0 }; PrintArray(a, sizeof(a) / sizeof(int)); ShellSort(a, sizeof(a) / sizeof(int)); PrintArray(a, sizeof(a) / sizeof(int)); } void TestHeapSort() { int a[] = { 3, 5, 2, 7, 8, 6, 1, 9, 4, 0 }; PrintArray(a, sizeof(a) / sizeof(int)); HeapSort(a, sizeof(a) / sizeof(int)); PrintArray(a, sizeof(a) / sizeof(int)); } void TestSelectSort() { int a[] = { 9, 3, 5, 2, 7, 8, 6, -1, 9, 4, 0 }; PrintArray(a, sizeof(a) / sizeof(int)); SelectSort(a, sizeof(a) / sizeof(int)); PrintArray(a, sizeof(a) / sizeof(int)); } void TestBubbleSort() { int a[] = { 9, 3, 5, 2, 7, 8, 6, -1, 9, 4, 0 }; PrintArray(a, sizeof(a) / sizeof(int)); BubbleSort(a, sizeof(a) / sizeof(int)); PrintArray(a, sizeof(a) / sizeof(int)); } void TestQuickSort() { int a[] = { 6, 3, 5, 2, 7, 8, 9, 4, 1 }; //int a[] = { 49, 38, 65, 97, 76, 13, 27, 49}; //int a[] = { 49, 38, 65, 97, 76, 13, 27, 49, 13, 27, 49 }; PrintArray(a, sizeof(a) / sizeof(int)); //QuickSort(a, sizeof(a) / sizeof(int) ); //快速排序——写法1:挖坑法(单趟分析版) QuickSort(a, 0, sizeof(a) / sizeof(int)-1); //快速排序——写法1:挖坑法(实现排序版) PrintArray(a, sizeof(a) / sizeof(int)); } void TestMergeSort() { //int a[] = { 10, 6, 7 ,1, 3, 9, 4, 2 }; int a[] = { 49, 38, 65, 97, 76, 13, 27, 49, 13, 27, 49 }; PrintArray(a, sizeof(a) / sizeof(int)); MergeSort(a, sizeof(a) / sizeof(int)); PrintArray(a, sizeof(a) / sizeof(int)); } void TestQuickSortNonR() { //int a[] = { 6, 3, 5, 2, 7, 8, 9, 4, 1 }; //int a[] = { 49, 38, 65, 97, 76, 13, 27, 49}; int a[] = { 49, 38, 65, 97, 76, 13, 27, 49, 13, 27, 49 }; PrintArray(a, sizeof(a) / sizeof(int)); QuickSortNonR(a, sizeof(a) / sizeof(int)); PrintArray(a, sizeof(a) / sizeof(int)); } void TestMergeSortNonR() { //int a[] = { 10, 6, 7 ,1, 3, 9, 4, 2 }; int a[] = { 49, 38, 65, 97, 76, 13, 27, 49, 13, 27, 49 }; PrintArray(a, sizeof(a) / sizeof(int)); MergeSortNonR(a, sizeof(a) / sizeof(int)); PrintArray(a, sizeof(a) / sizeof(int)); } void TestCountSort() { //int a[] = { 10, 6, 7 ,1, 3, 9, 4, 2 }; int a[] = { 49, 38, 65, 97, 76, 13, 27, 49, 13, 27, 49 }; PrintArray(a, sizeof(a) / sizeof(int)); CountSort(a, sizeof(a) / sizeof(int)); PrintArray(a, sizeof(a) / sizeof(int)); } int main() { //TestInsertSort(); //TestShellSort(); //TestHeapSort(); //TestSelectSort(); //TestBubbleSort(); //TestQuickSort(); //TestMergeSort(); //TestQuickSortNonR(); //TestMergeSortNonR(); TestCountSort(); return 0; }
注:
由于这里的快速排序——非递归版,使用数据结构的栈进行模拟递归过程。因此要附用栈的实现代码。这里将栈的代码给出,使用时包含头文件,再复用即可!
Stack.h:
#pragma once #include <stdio.h> #include <stdbool.h> #include <assert.h> #include <stdlib.h> typedef char STDataType; typedef struct Stack { STDataType* a; //通过数组实现栈的结构 int top; int capacity; }ST; //初始化 void StackInit(ST* ps); //释放内存、销毁空间 void StackDestory(ST* ps); // 入栈 void StackPush(ST* ps, STDataType x); // 出栈 void StackPop(ST* ps); //取栈顶数据 STDataType StackTop(ST* ps); //栈的大小 int StackSize(ST* ps); //判空 bool StackEmpty(ST* ps);
Stack.c:
#include"Stack.h" //初始化 void StackInit(ST* ps) { assert(ps); ps->a = (STDataType*)malloc(sizeof(STDataType) * 4); if (ps->a == NULL) { printf("malloc fail!\n"); exit(-1); } ps->capacity = 4; ps->top = 0; //这使得top最终指向的是栈顶的后一个位置。若top=-1,则最终指向的是栈顶。 } //释放内存、销毁空间 void StackDestory(ST* ps) { assert(ps); free(ps->a); ps->a = NULL; ps->top = ps->capacity = 0; } // 入栈 void StackPush(ST* ps, STDataType x) { assert(ps); // 满了->增容 if (ps->top == ps->capacity) { STDataType* tmp = (STDataType*)realloc(ps->a, ps->capacity * 2 * sizeof(STDataType)); if (tmp == NULL) { printf("realloc fail!\n"); exit(-1); } else { ps->a = tmp; ps->capacity *= 2; } } ps->a[ps->top] = x; ps->top++; } // 出栈 void StackPop(ST* ps) { assert(ps); // 栈空了,再调用Pop,就会直接中止程序报错 assert(ps->top > 0); //ps->a[ps->top - 1] = 0; //置为0只考虑了int型等,若为char、double等就不适用了。 ps->top--; } //取栈顶数据 STDataType StackTop(ST* ps) { assert(ps); // 栈空了,再调用Top,就会直接中止程序报错 assert(ps->top > 0); return ps->a[ps->top - 1]; } //求栈大小 int StackSize(ST* ps) { assert(ps); return ps->top; } //判空 bool StackEmpty(ST* ps) { assert(ps); return ps->top == 0; } //判断括号是否匹配算法 bool isValid(char* s) { ST st; StackInit(&st); while (*s != '\0') { switch (*s) { case '{': case '[': case '(': { StackPush(&st, *s); ++s; break; } case '}': case ']': case ')': { if (StackEmpty(&st)) { StackDestory(&st); return false; } char top = StackTop(&st); StackPop(&st); // 不匹配 if ((*s == '}' && top != '{') || (*s == ']' && top != '[') || (*s == ')' && top != '(')) { StackDestory(&st); return false; } else // 匹配 { ++s; } break; } default: break; } } bool ret = StackEmpty(&st); StackDestory(&st); return ret; }
3. 各大排序性能对比分析:
1. 堆排序性能对比测试:
测试代码:
#pragma once #include<stdio.h> #include<stdlib.h> / //堆排序对比测试: void Swap(int* p1, int* p2) { int tmp = *p1; *p1 = *p2; *p2 = tmp; } //(1)建小堆,向上调整,最终为大堆---- - 用于堆排降序 //1.堆排序调整-向下调整法 void AdjustDwon1(int* a, int n, int root) { int parent = root; int child = parent * 2 + 1; // 默认是左孩子 //建小堆,再调整 while (child < n) { // 1、选出左右孩子中小的那一个 if (child + 1 < n && a[child + 1] < a[child]) { child += 1; } if (a[child] < a[parent]) { Swap(&a[child], &a[parent]); parent = child; child = parent * 2 + 1; } else { break; } } } //2.堆排序调整-向上调整法 void AdjustUp1(int* a, int child) { int parent = (child - 1) / 2; //建小堆,再调整 while (child > 0) { if (a[child] < a[parent]) { Swap(&a[child], &a[parent]); child = parent; parent = (child - 1) / 2; } else { break; } } } //堆排序(建堆) void HeapSort1(int* a, int n) { //建堆 调整 //向上调整法 for (int i = 1; i < n; ++i) { AdjustUp1(a, i); } int end = n - 1; while (end > 0) { Swap(&a[0], &a[end]); AdjustDwon1(a, end, 0); --end; } } //(2)建大堆,向上调整,最终为小堆---- - 用于堆排升序 //1.堆排序调整-向下调整法 void AdjustDwon2(int* a, int n, int root) { int parent = root; int child = parent * 2 + 1; // 默认是左孩子 //建大堆,再调整 while (child < n) { // 1、选出左右孩子中大的那一个 if (child + 1 < n && a[child + 1] > a[child]) { child += 1; } if (a[child] > a[parent]) { Swap(&a[child], &a[parent]); parent = child; child = parent * 2 + 1; } else { break; } } } //2.堆排序调整-向上调整法 void AdjustUp2(int* a, int child) { int parent = (child - 1) / 2; //1.建大堆,再调整 while (child > 0) { if (a[child] > a[parent]) { Swap(&a[child], &a[parent]); child = parent; parent = (child - 1) / 2; } else { break; } } } //堆排序(建堆) void HeapSort2(int* a, int n) { // 建堆 调整 //向上调整法 for (int i = 1; i < n; ++i) { AdjustUp2(a, i); } int end = n - 1; while (end > 0) { Swap(&a[0], &a[end]); AdjustDwon2(a, end, 0); --end; } } //(3)建小堆,向下调整,最终为大堆---- - 用于堆排降序(可以实现,多此一举) //1.堆排序调整-向下调整法 void AdjustDwon3(int* a, int n, int root) { int parent = root; int child = parent * 2 + 1; // 默认是左孩子 //建小堆,再调整 while (child < n) { // 1、选出左右孩子中小的那一个 if (child + 1 < n && a[child + 1] < a[child]) { child += 1; } if (a[child] < a[parent]) { Swap(&a[child], &a[parent]); parent = child; child = parent * 2 + 1; } else { break; } } } //2.堆排序调整-向上调整法 void AdjustUp3(int* a, int child) { int parent = (child - 1) / 2; //建小堆,再调整 while (child > 0) { if (a[child] < a[parent]) { Swap(&a[child], &a[parent]); child = parent; parent = (child - 1) / 2; } else { break; } } } //堆排序(建堆) void HeapSort3(int* a, int n) { // 建堆 调整 //向下调整法 for (int i = (n - 1 - 1) / 2; i >= 0; --i) { AdjustDwon3(a, n, i); } int end = n - 1; while (end > 0) { Swap(&a[0], &a[end]); AdjustDwon3(a, end, 0); --end; } } //(4)建大堆,向下调整,最终为小堆---- - 用于堆排升序 //1.堆排序调整-向下调整法 void AdjustDwon4(int* a, int n, int root) { int parent = root; int child = parent * 2 + 1; // 默认是左孩子 //建大堆,再调整 while (child < n) { // 1、选出左右孩子中大的那一个 if (child + 1 < n && a[child + 1] > a[child]) { child += 1; } if (a[child] > a[parent]) { Swap(&a[child], &a[parent]); parent = child; child = parent * 2 + 1; } else { break; } } } //2.堆排序调整-向上调整法 void AdjustUp4(int* a, int child) { int parent = (child - 1) / 2; //建大堆,再调整 while (child > 0) { if (a[child] > a[parent]) { Swap(&a[child], &a[parent]); child = parent; parent = (child - 1) / 2; } else { break; } } } //堆排序(建堆) void HeapSort4(int* a, int n) { // 建堆 调整 //向下调整法 for (int i = (n - 1 - 1) / 2; i >= 0; --i) { AdjustDwon4(a, n, i); } int end = n - 1; while (end > 0) { Swap(&a[0], &a[end]); AdjustDwon4(a, end, 0); --end; } } void TestOP() { srand(time(0)); const int N = 100000; int* a1 = (int*)malloc(sizeof(int) * N); int* a2 = (int*)malloc(sizeof(int) * N); int* a3 = (int*)malloc(sizeof(int) * N); int* a4 = (int*)malloc(sizeof(int) * N); for (int i = 0; i < N; ++i) { a1[i] = rand(); //a1[i] = i; a2[i] = a1[i]; a3[i] = a1[i]; a4[i] = a1[i]; } int begin1 = clock(); HeapSort1(a1, N); int end1 = clock(); int begin2 = clock(); HeapSort2(a2, N); int end2 = clock(); int begin3 = clock(); HeapSort3(a3, N); int end3 = clock(); int begin4 = clock(); HeapSort4(a4, N); int end4 = clock(); printf("建小堆-向上调整-降序:%d\n", end1 - begin1); printf("建大堆-向上调整-升序:%d\n", end2 - begin2); printf("建小堆-向下调整-降序:%d\n", end3 - begin3); printf("建大堆-向下调整-升序:%d\n", end4 - begin4); free(a1); free(a2); free(a3); free(a4); } int main() { TestOP(); return 0; }
2.快速排序(递归版)性能对比测试:
3.各大排序测试对比代码:
#pragma once #include<stdio.h> #include<stdlib.h> #include<assert.h> #include"Stack.h" / //堆排序对比测试: void Swap(int* p1, int* p2) { int tmp = *p1; *p1 = *p2; *p2 = tmp; } //(1)建小堆,向上调整,最终为大堆---- - 用于堆排降序 //1.堆排序调整-向下调整法 void AdjustDwon1(int* a, int n, int root) { int parent = root; int child = parent * 2 + 1; // 默认是左孩子 //建小堆,再调整 while (child < n) { // 1、选出左右孩子中小的那一个 if (child + 1 < n && a[child + 1] < a[child]) { child += 1; } if (a[child] < a[parent]) { Swap(&a[child], &a[parent]); parent = child; child = parent * 2 + 1; } else { break; } } } //2.堆排序调整-向上调整法 void AdjustUp1(int* a, int child) { int parent = (child - 1) / 2; //建小堆,再调整 while (child > 0) { if (a[child] < a[parent]) { Swap(&a[child], &a[parent]); child = parent; parent = (child - 1) / 2; } else { break; } } } //堆排序(建堆) void HeapSort1(int* a, int n) { //建堆 调整 //向上调整法 for (int i = 1; i < n; ++i) { AdjustUp1(a, i); } int end = n - 1; while (end > 0) { Swap(&a[0], &a[end]); AdjustDwon1(a, end, 0); --end; } } //(2)建大堆,向上调整,最终为小堆---- - 用于堆排升序 //1.堆排序调整-向下调整法 void AdjustDwon2(int* a, int n, int root) { int parent = root; int child = parent * 2 + 1; // 默认是左孩子 //建大堆,再调整 while (child < n) { // 1、选出左右孩子中大的那一个 if (child + 1 < n && a[child + 1] > a[child]) { child += 1; } if (a[child] > a[parent]) { Swap(&a[child], &a[parent]); parent = child; child = parent * 2 + 1; } else { break; } } } //2.堆排序调整-向上调整法 void AdjustUp2(int* a, int child) { int parent = (child - 1) / 2; //1.建大堆,再调整 while (child > 0) { if (a[child] > a[parent]) { Swap(&a[child], &a[parent]); child = parent; parent = (child - 1) / 2; } else { break; } } } //堆排序(建堆) void HeapSort2(int* a, int n) { // 建堆 调整 //向上调整法 for (int i = 1; i < n; ++i) { AdjustUp2(a, i); } int end = n - 1; while (end > 0) { Swap(&a[0], &a[end]); AdjustDwon2(a, end, 0); --end; } } //(3)建小堆,向下调整,最终为大堆---- - 用于堆排降序(可以实现,多此一举) //1.堆排序调整-向下调整法 void AdjustDwon3(int* a, int n, int root) { int parent = root; int child = parent * 2 + 1; // 默认是左孩子 //建小堆,再调整 while (child < n) { // 1、选出左右孩子中小的那一个 if (child + 1 < n && a[child + 1] < a[child]) { child += 1; } if (a[child] < a[parent]) { Swap(&a[child], &a[parent]); parent = child; child = parent * 2 + 1; } else { break; } } } //2.堆排序调整-向上调整法 void AdjustUp3(int* a, int child) { int parent = (child - 1) / 2; //建小堆,再调整 while (child > 0) { if (a[child] < a[parent]) { Swap(&a[child], &a[parent]); child = parent; parent = (child - 1) / 2; } else { break; } } } //堆排序(建堆) void HeapSort3(int* a, int n) { // 建堆 调整 //向下调整法 for (int i = (n - 1 - 1) / 2; i >= 0; --i) { AdjustDwon3(a, n, i); } int end = n - 1; while (end > 0) { Swap(&a[0], &a[end]); AdjustDwon3(a, end, 0); --end; } } //(4)建大堆,向下调整,最终为小堆---- - 用于堆排升序 //1.堆排序调整-向下调整法 void AdjustDwon4(int* a, int n, int root) { int parent = root; int child = parent * 2 + 1; // 默认是左孩子 //建大堆,再调整 while (child < n) { // 1、选出左右孩子中大的那一个 if (child + 1 < n && a[child + 1] > a[child]) { child += 1; } if (a[child] > a[parent]) { Swap(&a[child], &a[parent]); parent = child; child = parent * 2 + 1; } else { break; } } } //2.堆排序调整-向上调整法 void AdjustUp4(int* a, int child) { int parent = (child - 1) / 2; //建大堆,再调整 while (child > 0) { if (a[child] > a[parent]) { Swap(&a[child], &a[parent]); child = parent; parent = (child - 1) / 2; } else { break; } } } //堆排序(建堆) void HeapSort4(int* a, int n) { // 建堆 调整 //向下调整法 for (int i = (n - 1 - 1) / 2; i >= 0; --i) { AdjustDwon4(a, n, i); } int end = n - 1; while (end > 0) { Swap(&a[0], &a[end]); AdjustDwon4(a, end, 0); --end; } } //插入排序 //时间复杂度:O(N^2) //最坏情况:逆序 //最好情况:全部顺序 O(N) void InsertSort(int* a, int n) { //假设[0,end]有序,将end+1位置的值插入进去,让[0,end+1]有序 for (int i = 0; i < n - 1; i++) { int end = i; int tmp = a[end + 1]; while (end >= 0) { if (a[end] > tmp) //升序 //if (a[end] < tmp) //降序 { a[end + 1] = a[end]; --end; } else { break; } } a[end + 1] = tmp; } } //快速排序——写法1:挖坑法(实现排序版) void QuickSort1(int* a, int left, int right) { if (left >= right) return; int begin = left, end = right; int pivot = begin; int key = a[begin]; //指定了第一次关键字为begin while (begin < end) //单趟排序 { //右边找小,放到左边 while (begin < end && a[end] >= key) { --end; } //小的放到左边的坑里,自己形成新的坑位 a[pivot] = a[end]; pivot = end; //左边找大 while (begin < end && a[begin] <= key) { ++begin; } //大的放到左边的坑里,自己形成新的坑位 a[pivot] = a[begin]; pivot = begin; } pivot = begin; a[pivot] = key; // [left, right] // [left, pivot-1] pivot [pivot+1, right] // 左子区间和右子区间有序,我们就有序了,如果让他们有序呢? 分治递归 QuickSort1(a, left, pivot - 1); QuickSort1(a, pivot + 1, right); } //快速排序——写法2:挖坑法优化--->(三数取中,解决原始数据有序情况),再根据处理量分情况排序(小区间优化) // 三数取中 int GetMidIndex(int* a, int left, int right) { int mid = (left + right) >> 1; if (a[left] < a[mid]) { if (a[mid] < a[right]) { return mid; } else if (a[left] > a[right]) { return left; } else { return right; } } else // a[left] > a[mid] { if (a[mid] > a[right]) { return mid; } else if (a[left] < a[right]) { return left; } else { return right; } } } void QuickSort2(int* a, int left, int right) { if (left >= right) return; int keyIndex = GetMidIndex(a, left, right); Swap(&a[left], &a[keyIndex]); int begin = left, end = right; int pivot = begin; int key = a[begin]; // O(N) while (begin < end) { // 右边找小,放到左边 while (begin < end && a[end] >= key) --end; // 小的放到左边的坑里,自己形成新的坑位 a[pivot] = a[end]; pivot = end; // 左边找大 while (begin < end && a[begin] <= key) ++begin; // 大的放到左边的坑里,自己形成新的坑位 a[pivot] = a[begin]; pivot = begin; } pivot = begin; a[pivot] = key; // 小区间优化 //这个值会随原始数据量改变,而官方给13左右 if (pivot - 1 - left > 10) //官方给13左右 { QuickSort2(a, left, pivot - 1); } else { //InsertSort(int* a, int n) InsertSort(a + left, pivot - 1 - left + 1); //left不一定从0开始,传数据个数 } if (right - (pivot + 1) > 10) { QuickSort2(a, pivot + 1, right); } else { InsertSort(a + pivot + 1, right - (pivot + 1) + 1); } } //快速排序——写法3:左右指针法:找小找大交换 void QuickSort3(int* a, int left, int right) { if (left >= right) return; int index = GetMidIndex(a, left, right); Swap(&a[left], &a[index]); int begin = left, end = right; int keyi = begin; while (begin < end) { // 找小 while (begin < end && a[end] >= a[keyi]) { --end; } // 找大 while (begin < end && a[begin] <= a[keyi]) { ++begin; } Swap(&a[begin], &a[end]); } Swap(&a[begin], &a[keyi]); int key = begin; // 小区间优化 //这个值会随原始数据量改变,而官方给13左右 if (key - 1 - left > 10) //官方给13左右 { QuickSort3(a, left, key - 1); } else { //InsertSort(int* a, int n) InsertSort(a + left, key - 1 - left + 1); //left不一定从0开始,传数据个数 } if (right - (key + 1) > 10) { QuickSort3(a, key + 1, right); } else { InsertSort(a + key + 1, right - (key + 1) + 1); } } //快速排序——写法4:前后指针法:cur找小,每次找到比keyi小额值,就++prev,然后交换prev与cur的位置交换 //1.会有自己与自己交换的情况,2.也有异步交换,3.最后实现左边全为小于key,右边全为大于key void QuickSort4(int* a, int left, int right) { if (left >= right) return; int index = GetMidIndex(a, left, right); Swap(&a[left], &a[index]); int keyi = left; int prev = left, cur = left + 1; while (cur <= right) { if (a[cur] < a[keyi] && ++prev != cur) //++prev != cur:优化自己与自己交换的情况 { Swap(&a[prev], &a[cur]); } ++cur; } Swap(&a[keyi], &a[prev]); int key = prev; // 小区间优化 //这个值会随原始数据量改变,而官方给13左右 if (key - 1 - left > 10) //官方给13左右 { QuickSort4(a, left, key - 1); } else { //InsertSort(int* a, int n) InsertSort(a + left, key - 1 - left + 1); //left不一定从0开始,传数据个数 } if (right - (key + 1) > 10) { QuickSort4(a, key + 1, right); } else { InsertSort(a + key + 1, right - (key + 1) + 1); } } //快速排序——非递归版 //(复杂一点)使用数据结构的栈进行模拟递归过程:不会再有栈溢出问题,也会有空间消耗 //但数据结构中的栈是malloc出来的,开辟在堆(操作系统对内存的划分),而堆的空间比栈大 void QuickSortNonR(int* a, int n) { ST st; StackInit(&st); StackPush(&st, n - 1); StackPush(&st, 0); while (!StackEmpty(&st)) { int left = StackTop(&st); StackPop(&st); int right = StackTop(&st); StackPop(&st); //调用单趟排序 int index = GetMidIndex(a, left, right); //Swap(&a[left], &a[index]); int keyi = left; int prev = left, cur = left + 1; while (cur <= right) { if (a[cur] < a[keyi] && ++prev != cur) //++prev != cur:优化自己与自己交换的情况 { Swap(&a[prev], &a[cur]); } ++cur; } Swap(&a[keyi], &a[prev]); int keyIndex = prev; //栈里面的就需要单趟分割排序 //[left,keyIndex-1] keyIndex [keyIndex+1,right] if (keyIndex + 1 < right) { StackPush(&st, right); StackPush(&st, keyIndex + 1); } if (left < keyIndex - 1) { StackPush(&st, keyIndex - 1); StackPush(&st, left); } } StackDestory(&st); } void ShellSort(int* a, int n) { int gap = n; //可以自行设置,但不会给固定的值 //把间隔为gap的多组数据同时排序(注意:理解这个多组的含义) //end最终位置:n-gap-1 //gap的设置方式之一: while (gap > 1) { //gap=gap/2; //一定会保证最后一次为1 //运行了logN次 gap = gap / 3 + 1; //但要保证最后一次为1 //运行了log3N次 //gap>1时都是预排序,排序后接近有序 //gap==1时,就是直接插入排序 for (int i = 0; i < n - gap; i++) { int end = i; int tmp = a[end + gap]; while (end >= 0) { if (a[end] > tmp) { a[end + gap] = a[end]; end -= gap; } else { break; } } a[end + gap] = tmp; } } } void BubbleSort(int* a, int n) { //写法1: for (int j = 0; j < n; ++j) { int exchange = 0; for (int i = 1; i < n - j; ++i) { if (a[i - 1] > a[i]) { Swap(&a[i - 1], &a[i]); exchange = 1; } } if (exchange == 0) { break; } } 写法2: //int end = n; //while (end > 0) //{ // for (int i = 1; i < end; ++i) // { // if (a[i - 1] > a[i]) // { // Swap(&a[i - 1], &a[i]); // } // } // --end; //} } void _MergeSort(int* a, int left, int right, int* tmp) { if (left >= right) return; int mid = (left + right) >> 1; //假设 [left, mid] [mid+1, right]有序,那么我们就可以归并了 _MergeSort(a, left, mid, tmp); _MergeSort(a, mid + 1, right, tmp); // 归并 int begin1 = left, end1 = mid; int begin2 = mid + 1, end2 = right; int index = left; while (begin1 <= end1 && begin2 <= end2) { if (a[begin1] < a[begin2]) { tmp[index++] = a[begin1++]; } else { tmp[index++] = a[begin2++]; } } while (begin1 <= end1) { tmp[index++] = a[begin1++]; } while (begin2 <= end2) { tmp[index++] = a[begin2++]; } // 拷贝回去 for (int i = left; i <= right; ++i) { a[i] = tmp[i]; } } void MergeSort(int* a, int n) { int* tmp = (int*)malloc(sizeof(int) * n); //空间复杂度为O(N) _MergeSort(a, 0, n - 1, tmp); free(tmp); } void MergeSortNonR(int* a, int n) { int* tmp = (int*)malloc(sizeof(int) * n); int gap = 1; //每组数据个数 while (gap < n) { for (int i = 0; i < n; i += 2 * gap) { //[i,i+gap-1] [i+gap,i+2*gap-1] // 归并 int begin1 = i, end1 = i + gap - 1; int begin2 = i + gap, end2 = i + 2 * gap - 1; //归并过程中右半区间可能就不存在 if (begin2 >= n) break; //归并过程中右半区间算多了,修正 if (end2 >= n) { end2 = n - 1; } int index = i; while (begin1 <= end1 && begin2 <= end2) { if (a[begin1] < a[begin2]) { tmp[index++] = a[begin1++]; } else { tmp[index++] = a[begin2++]; } } while (begin1 <= end1) { tmp[index++] = a[begin1++]; } while (begin2 <= end2) { tmp[index++] = a[begin2++]; } // 拷贝回去 for (int j = i; j < end2; ++j) { a[j] = tmp[j]; } } gap *= 2; } free(tmp); } void CountSort(int* a, int n) { int min = a[0], max = a[0]; for (int i = 1; i < n; ++i) { if (a[i] < min) min = a[i]; if (a[i] > max) max = a[i]; } int range = max - min + 1; int* countA = (int*)malloc(sizeof(int) * range); assert(countA); memset(countA, 0, sizeof(int) * range); //计数 for (int i = 0; i < n; ++i) countA[a[i] - min]++; //排序 int j = 0; for (int i = 0; i < range; ++i) { while (countA[i]--) { a[j++] = i + min; } } } void TestOP() { srand(time(0)); const int N = 1000000; int* a1 = (int*)malloc(sizeof(int) * N); int* a2 = (int*)malloc(sizeof(int) * N); int* a3 = (int*)malloc(sizeof(int) * N); int* a4 = (int*)malloc(sizeof(int) * N); int* a5 = (int*)malloc(sizeof(int) * N); int* a6 = (int*)malloc(sizeof(int) * N); int* a7 = (int*)malloc(sizeof(int) * N); int* a8 = (int*)malloc(sizeof(int) * N); int* a9 = (int*)malloc(sizeof(int) * N); int* a10 = (int*)malloc(sizeof(int) * N); int* a11 = (int*)malloc(sizeof(int) * N); int* a12 = (int*)malloc(sizeof(int) * N); int* a13 = (int*)malloc(sizeof(int) * N); int* a14 = (int*)malloc(sizeof(int) * N); for (int i = 0; i < N; ++i) { a1[i] = rand(); //a1[i] = i; a2[i] = a1[i]; a3[i] = a1[i]; a4[i] = a1[i]; a5[i] = a1[i]; a6[i] = a1[i]; a7[i] = a1[i]; a8[i] = a1[i]; a9[i] = a1[i]; a10[i] = a1[i]; a11[i] = a1[i]; a12[i] = a1[i]; a13[i] = a1[i]; a14[i] = a1[i]; } int begin1 = clock(); //HeapSort1(a1, N); int end1 = clock(); int begin2 = clock(); //HeapSort2(a2, N); int end2 = clock(); int begin3 = clock(); //HeapSort3(a3, N); int end3 = clock(); int begin4 = clock(); //HeapSort4(a4, N); int end4 = clock(); int begin5 = clock(); QuickSort1(a5, 0, N - 1); int end5 = clock(); int begin6 = clock(); QuickSort2(a6, 0, N - 1); int end6 = clock(); int begin7 = clock(); QuickSort3(a7, 0, N - 1); int end7 = clock(); int begin8 = clock(); QuickSort4(a8, 0, N - 1); int end8 = clock(); int begin9 = clock(); QuickSortNonR(a9, N); int end9 = clock(); int begin10 = clock(); //ShellSort(a10, N); int end10 = clock(); int begin11 = clock(); //BubbleSort(a11, N); int end11 = clock(); int begin12 = clock(); //MergeSort(a12, N); int end12 = clock(); int begin13= clock(); //MergeSortNonR(a13, N); int end13 = clock(); int begin14 = clock(); //CountSort(a14, N); int end14 = clock(); printf("给定%d个随机数进行排序,测试对比各大排序性能(单位:ms):\n", N); printf("建小堆-向上调整-降序:\t%d\n", end1 - begin1); printf("建大堆-向上调整-升序:\t%d\n", end2 - begin2); printf("建小堆-向下调整-降序:\t%d\n", end3 - begin3); printf("建大堆-向下调整-升序:\t%d\n", end4 - begin4); printf("快速排序-挖坑法: \t%d\n", end5 - begin5); printf("快速排序-挖坑法优化:\t%d\n", end6 - begin6); printf("快速排序-左右指针法:\t%d\n", end7 - begin7); printf("快速排序-前后指针法:\t%d\n", end8 - begin8); printf("快速排序-非递归: \t%d\n", end9 - begin9); printf("希尔排序: \t%d\n", end10 - begin10); printf("冒泡排序: \t%d\n", end11 - begin11); printf("归并排序: \t%d\n", end12 - begin12); printf("归并排序-非递归: \t%d\n", end13 - begin13); printf("计数排序: \t%d\n", end14 - begin14); free(a1); free(a2); free(a3); free(a4); free(a5); free(a6); free(a7); free(a8); free(a9); free(a10); free(a11); free(a12); free(a13); free(a14); } int main() { TestOP(); return 0; }