一、排序
01 冒泡排序
顾名思义,这种排序像水下的一串泡泡要冒出水面,但只能让大泡泡先冒出,小泡泡才能冒出。通过两两比对(下面的泡泡跟它正上方的比较),依次将两泡泡中较大的那个放在上面。
n个数据,需要排n-1趟,每一趟要前后比较n-1-i次。
则一次排序过后,最大的冒出水面;第二次再从最下面的开始比较,这次第二大的泡泡冒出水面;重复此步骤,直到水下只有两个泡泡,比较它们,这次两个泡泡按大小顺序冒出水面,排序就完成了。
大家学习过程中可以参考b站演示视频,直观清晰。
代码实现:
#include<stdio.h>
#define N 5
int main()
{
int i,j,tmp;
int arr[N]={5,2,3,4,1};
for(i=0;i<N-1;i++)
{
for(j=0;j<N-1-i;j++)
{
if(arr[j]>arr[j+1])
{
tmp=arr[j];
arr[j]=arr[j+1];
arr[j+1]=tmp;
}
}
}
for(i=0;i<N;i++)
{printf("%3d",arr[i]);}
printf("\n");
return 0;
}
分析:明显看到冒泡是需要双层循环的,这意味着时间复杂度最高可达O(n*n),因此只适合数据量较小的情况;不过冒泡也是有优点的,它是一种原地排序,没有占用其他空间,空间复杂度是很小的;同时,每一次都是相邻两者比较、调换,其稳定性较高。
02 选择排序
选择排序即每一次都从无序数列中选出最小(大)的,排在有序部分,直至全部排完。
需要排n-1趟,每趟从i+1比到最后一个元素。
代码实现:
#include<stdio.h>
#define N 5
int main()
{
int i,j,tmp,min=0;
int arr[N]={5,4,1,2,3};
for(i=0;i<N-1;i++)
{
min=i;
for(j=i+1;j<N;j++)
{
if(arr[min]>arr[j])
{
min=j;
}
if(min!=i)
{
tmp=arr[min];
arr[min]=arr[i];
arr[i]=tmp;
}
}
}
for(i=0;i<N;i++)
{
printf("%3d",arr[i]);
}
printf("\n");
return 0;
}
可以看到这里也是双层循环,故时间复杂度最大O(n*n),同样是原地排序,空间复杂度小,由于每次调换不是与相邻元素换,其稳定性也不是太好。
03 插入排序
即每次从无序序列中取一个数,在有序序列中从左向右地进行比较,直到找到该元素应处的位置,把它放在该位置;重复此操作,直至无序序列为0。
这其中也是需要依次拿出数,拿出的数依次去比较,也就是双层循环,时间复杂度最大为O(n*n);没有占用其他空间,空间复杂度O(1)。且插入排序也较稳定(这里还不理解)。
【考虑篇幅,之后的排序不再写代码】
04 归并排序
有点像二分法,不断对数组平分两组,直至每一组都只有一个元素,再把相邻两组比大小组合为一组,e.g.一个元素一组并成两个元素一组,此时相邻两组共有4个元素,比较这4个元素,合并为有序的一个新组,以此类推,直到重新合并为含所有元素的一大组。
时间复杂度O(n*log n),空间复杂度O(n),较稳定排序。
05 希尔排序
希尔排序也是分组,但它是组内分先后的排序,好像上小学班级里分小组,每个组都有1号,2号,3号那种。比如8个人,平分两组(9个人就3组,只不过第三组只1人);每组1号跟1号比,2号跟2号比(田忌赛马之前的老赛制那感觉),把小的放在第一组,大的放在第二组;比完这4个组员,重新分组,这次就是2人一组,还是每组1号跟1号比,2号跟2号比;最后直到每组一个人,再做比较,最终完成。
时间复杂度:O(n*log方 n)-O(n*n)之间,空间复杂度O(n),不稳定排序。
06 快速排序
这种算法像是俩人碰头问题。选定一个基数做对照,左一个指针右一个指针,分别指着最左最右的元素开始与基数比对,把比基数小的放左边,大的放右边。这俩指针要交替着来,一人走一步,如果某一步左指针指着的数就应该在左边,没换到右边去,那左指针就再走一步。
排好一次,在原基数的左右分别重复此步骤,直至全部有序。
时间复杂度O(n*n),空间复杂度O(n),不稳定排序。
07 堆排序
首先要知道大顶堆,即二叉树上任意节点数>其子节点数,要实现它,只要节点与子节点三个数据找最大即可。
于是堆排序的方法就很清晰了,只要构建大顶堆,每次都把最顶上的元素拿出来,天然的就是从大到小的一组数了!!非常奇妙。
时间复杂度O(n*log n),空间复杂度O(1),不稳定排序。
08 计数排序
举实例说明:一串数:3,5,6,3,1,0,8。定义一数组arr[9]={0},遍历该串数,出现3,即给arr[3]加1,出现5即给arr[5]加1......本串数出现两次3,即最后arr[3]=2。
最后将arr[i]的i顺序排出,arr[i]计数几次就排几次相应的i,即完成初始数组的排序。
(这波其实是优劣明显了,数值跨度大的话就来不了了,空间太大太浪费)
时间复杂度O(n+k),空间复杂度O(k),稳定排序。
09 桶排序
一组数:1,3,9,......,100,67,30,划分区间如[0,20),[20,40),......[80-100],先把数分到各个桶里,各桶里进行排序,再从桶里顺序拿出来排成一串。(在桶里咋排就看情况选了)
时间复杂度O(n),空间复杂度O(n+m),不稳定排序。
10 基数排序
类似计数排序,是其进阶版。按个十百千位数字依次计数,记完一次排一次,直到最高位记完排完,此时全部有序。
时间复杂度O(n*k),空间复杂度O(n+k),稳定排序。
一口气总结了这些排序,真是要我老命,做题就另开一个吧。