排序
1、基本概念
1、稳定排序:a == b,a本来在b前面,排序结束a仍然在b前面
2、非稳定排序:a==b,a原本在b前面,排序结束b在a前面
3、原地排序:排序过程中不申请新的空间
4、非原地排序:需要利用额外的数组来辅助排序
5、外部排序:排序的数据很大,不能一次容纳全部的排序记录,在排序过程中还需要访问外部存储器的排序
6、内部排序:待排序数据记录完全存放在内存中所进行的排序过程,适合不太大的元素序列。
2、排序算法
1、选择排序
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 | void Selectsort( int a[], int n) { int i; int j; int k; int min; int temp; for (i = 0;i < n;i++) { min = i; for (j = i + 1;j < n;j++) { if (a[min] > a[j]) { min = j; } } if (i != min) { temp = a[i]; a[i] = a[min]; a[min] = temp; } } } //时间复杂度:O(n的平方) //空间复杂度:O(1) //非稳定排序 //原地排序 |
2、插入排序
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 | void Insertsort( int a[], int n) { int i; int j; int temp; int k; if (a == NULL || n < 2) { return ; } for (i = 0;i < n;i++) { temp = a[i]; k = i-1; //挨个往前找 //找到插入位置 while (k >= 0 && a[k] > temp) { k--; } //腾出位置插进去,要插的位置是k+1; for (j = i;j > k+1; j--) { a[j] = a[j-1]; } a[k+1] = temp; } } //时间复杂度O(n的平方) //空间复杂度:O(1) //稳定排序 //原地排序 |
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 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | void Bubblesort( int a[], int n) { int i; int j; int temp; for (i = 0;i < n;i++) { for (j = 0;j < n-i-1;j++) { if (a[j+1] < a[j]) { temp = a[j+1]; a[j+1] = a[j]; a[j] = temp; } } } } //时间复杂度O(n的平方) //空间复杂度O(1) //稳定排序 //原地排序 //优化,如果一次循环下来一次交换操作都没有,说明此时数组有序 void highBubblesort( int a[], int n) { int i; int j; int temp; int flag = 1; for (i = 0;i < n;i++) { for (j = 0;j < n-i-1;j++) { if (a[j+1] < a[j]) { flag = 0; temp = a[j+1]; a[j+1] = a[j]; a[j] = temp; } } if (flag) { break ; } } } |
4、希尔排序
希尔排序是插入排序的一种变种,无论是插入排序还是冒泡排序,如果数组的最大值刚好在第一位,需要将他挪到正确的位置就需要n-1次移动,也就是说,原数组的一个元素如果距离它正确的位置很远的话,则需要与相邻元素交换很多次才能到达正确的位置,这样相比比较花时间。
希尔排序就是加快速度简单的改进了插入排序,交换不相邻的元素以对数组的局部进行排序。
希尔排序的思想是采用插入排序的方法,先让数组中任意间隔为h的元素有序,刚开始h的大小可以是h = n/2,接着h = n/2,让h一直缩小,当h = 1时,也就是数组中任意间隔1的元素有序,此时的数组就是有序的了。
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 | //希尔排序(插入排序的变种) //没有特别理解插入,所以代码写的不好 void sort( int a[], int n) { int h; int i; int j; int temp; //遍历所有的步长 for (h = n/2; h > 0;h /= 2) { //遍历所有的元素 for (i = h;i < n;i++) { //遍历本组中的所有元素 for (j = i-h; j >= 0;j-=h) //这个地方没太用到插入,可以用插入,但是写的那个插入不太对。 { //如果当前元素大于加上步长后的元素 if (a[j] > a[j+h]) // { //有插入部分,非交换部分<br>void sort1(int a[],int n) { int h; int i; int j; int current; //遍历所有的步长 for (h = n/2; h > 0;h /= 2) { //遍历所有的元素 for (i = h;i < n;i++) { j = i; current = a[i]; while (j - h >= 0 && current < a[j - h]) { a[j] = a[j-h]; j = j-h; } a[j] = current; } } } |
5、归并排序
归并排序是建立在归并操作上的一种有效的排序算法,使用分治法的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列。即使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为2-路归并
把长度为n的输入序列分为长度为n/2的子序列;对着两个子序列分别采用归并排序;将两个排序号的子序列合并成一个最终的排序序列。
6、快速排序
基本思想:通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字比另一部分的关键字小,则可对这两部分记录继续进行排序,以达到整个序列有序
算法描述:快速排序使用分治法来把一个串分成两个串。
从数列中调出一个数,称为基准(prvot)
重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准后面(相同的数可以放在任一边),在这份分区退出后,该基准就处于数列的中间位置,这个称为分区操作。
递归的把小于基准值元素的子数列和大于基准值元素的子数列排序。
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 | void swap( int *x, int *y) { int t; t = *x; *x = *y; *y = t; } void fastsort( int arr[], int start, int end) { if (start >= end) { return ; } int mid = arr[end]; int left = start; int right = end -1; while (left < right) { while (arr[left] < mid && left < right) { left++; } while (arr[right] >= mid && left < right) { right--; } swap(&arr[left],&arr[right]); } if (arr[left] >= arr[end]) { swap(&arr[left],&arr[end]); } else { left++; } if (left) { sort(arr,start,left-1); sort(arr,left+1,end); } } |
7、堆排序
基本思想:利用堆这种数据结构设计的一种算法。堆积是一个近似完全二叉树的结构,并同时满足堆积的性质,即子节点的键值或索引总是小于或大于它的父节点
算法描述
1、将待排排序关键字序列(R1,R2……Rn)构成大顶堆,此堆为初始的无序区
2、将堆顶元素R【1】与最后一个元素R【n】交换,此时得到新的无序区(R1,R2……Rn-1)以及新的有序区(Rn),且满足R1,R2Rn-1<=R【n】
3、由于交换后新的堆顶R【1】可能违反堆的性质,因此需要对当前无序区(R1,R2,Rn-1)调整为新堆,然后在将R【1】和无序区最后一个元素交换,得到新的无序区(R1,R2……Rn-2)和新的有序区(Rn-1,Rn),不断重复此过程直到有序区的元素个数为n-1,排序完成。
8、计数排序
基本思想:计数排序不是基于比较的排序算法,核心在于将输入的数据值转化为键存储的额外开辟的数组空间中。作为一种线性时间复杂度的排序,计数排序要求输入的数据必须是有确定范围的整数
算法描述
1、找出待排序的数组中最大和最小的元素
2、统计数组中每个值为i的元素出现的次数,存入数组c的第i项
3、对所有的计数累加(从c中的第一个元素开始,每一项和前一项相加)
4、反向填充目标数组:将每个元素i放在新数组的第C(i)项,每放一个元素就将C【i】减去1
9、桶排序
基本思想:捅排序是计数排序的升级版,利用了函数的映射关系,高效与否在于这个映射函数的确定。桶排序的工作的原理,假如输入数据服从均匀分布,将数据分到有限数量的桶里,每个桶在分别排序(有可能在使用别的排序算法或者以递归方式继续使用桶排序进行排)
算法描述:
1、设置一个定量的数组当做空桶
2、遍历输入数据,并且把数据数据一个一个放到对应的桶里去
3、对每个不是空的桶进行排序
4、从不是空的桶里把排好序的数据拼接起来
10、基数排序
基本思想:基数排序是按照低位先排序,然后收集;在按照高位排序,然后在收集;以此类推,知道最高位。有时候有些属性是有优先级顺序的,先按低优先级排序,在按照高优先级排序。最后的次序就是高优先级的在前,高优先级相同的低优先级高的在前。
算法描述:
1、取得数组的最大数,并取得位数
2、a为原始数组,从最低位组成radix数组
3、对radix进行计数排序(利用计数排序使用与小范围数的特点)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· .NET10 - 预览版1新功能体验(一)