排序算法整理

开头附上一个微博发现的有趣干货~6分钟欣赏15种排序算法的视听呈现
~~进入正题~~

排序的稳定性定义

· 指当待排序序列中有两个或两个以上相同的关键字时,排序前和排序后这些关键字的相对位置,如果没有发生变化就是稳定的,否则是不稳定的

排序算法的分类

· 插入类的排序:直接插入排序、折半插入排序、希尔排序
· 交换类的排序:冒泡排序、快速排序
· 选择类的排序:简单选择排序、堆排序
· 归并类的排序:二路归并排序
· 基数类的排序:多关键字排序
(本文中的排序默认均按从小到大排序)

插入类

1.直接插入排序

直接上代码不解释~

1
2
3
4
5
6
7
8
9
10
11
12
voidd InsertSort(int R[],int n){
int i,j,temp;
for(i=1;i<n;i++){
temp=R[i];
j=i-1;
while(R[j]>temp&&j>=0){
R[j+1]=R[j];
j--;
}
R[j+1]=temp;
}
}

时间复杂度:最好情况下内层不执行,O(n);最坏情况下O(n^2);平均时间复杂度O(n^2)
空间复杂度:因为辅助存储空间为常量,所以O(1)

2.折半插入排序

思想与直接插入排序相同,查找插入位置的方法不同。可以从直接插入排序中看出每次都是在一个有序的序列中插入新的关键字,因此可以用折半查找在这个有序序列中查找插入位置。
具体待查找专题补充~

3.希尔排序

· 又称缩小增量排序,其本质仍未插入排序。
· 将待排序列按某种规则分成几个子序列,分别对这几个子序列进行直接插入排序。这个规则的体现即为增量的选取。若增量取1,就是直接插入排序。
· 增量的选取先大后小,每次排序都会使整个序列更有序,最后取1
· 希尔排序是不稳定的,其时间复杂度O(n^2),空间复杂度O(1)

交换类

1.冒泡排序

· 两两比较依次比较,若不有序则交换。
· 冒泡排序算法的结束条件为:在一趟排序过程中没有发生关键字交换。故可设flag以作标识

1
2
3
4
5
6
7
8
9
10
11
12
13
14
void BubbleSort(int R[],int n){
int i,j,flag,temp;
for(i=n-1;i>=1;i--){ //循环n-1次
flag=0;
for(j=1;j<=i;j++)
if(R[j-1]>R[j]){
temp=R[j-1];
R[j-1]=R[j];
R[j]=temp;
flag=1;
}
if(flag==0) return;
}
}

· 时间复杂度:最好情况下,待排序列有序,交换的条件句if始终不成立,交换不发生,此时为O(n);最坏情况下则为O(n^2);平均时间复杂度为O(n^2)
· 空间复杂度:额外辅助空间仅为temp,故为O(1)

2.快速排序

以升序为例:每一趟选择当前所有子序列中的一个关键字(通常是第一个)作为枢轴,将子序列中比枢轴小的移到枢轴前边,比枢轴大的移到枢轴后边;当本趟所有子序列都被枢轴以上述规则划分完毕后会得到新的一组更短的子序列,他们会成为下一趟划分的初始序列集。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void QuickSort(int R[],int low,int high){
int temp,i=low,j=high;
if(low<high){
temp=R[low];
while(i<j){
while(j>i&&R[j]>=temp) j--;
if(i<j){
R[i]=R[j];
i++;
}
while(i<j&&R[i]<temp) i++;
if(i<j){
R[j]=R[i];
j--;
}
}
R[i]=temp;
QuickSort(R,low,i-1);
QuickSort(R,i+1,high);
}
}

· 时间复杂度:待排序列越接近无序,快速排序算法效率越高,最好情况下时间复杂度为O(nlogn);待排序列越接近有序,本算法效率越低,最坏情况下为O(n^2);平均算法复杂度为O(nlogn)
· 空间复杂度:O(logn)。快速排序是递归进行的,递归需要栈辅助,辅助空间比前面几类排序大。

选择类

1.简单选择排序

从头至尾顺序扫描序列,找出最小的一个关键字,和第一个关键字交换,接着从剩下的关键字中继续这种选择和交换,最终使序列有序。

1
2
3
4
5
6
7
8
9
10
11
12
void SelectSort(int R[],int n){
for(int i=0;i<n;i++){
int k=i;
for(int j=i+1;j<n;j++){
if(R[j]<R[k])
k=j;
}
int temp=R[k];
R[k]=R[i];
R[i]=temp;
}
}

· 时间复杂度:O(n^2)
· 空间复杂度:O(1)

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
//对R[low]结点向下调整
void downAdjust(int R[],int low,int high){
int i=low,j=i*2;
while(j<=high){
if(j+1<=high&&R[j+1]>R[j]) j++;
if(R[j]>R[i]){
swap(R[j],R[i]);
i=j;
j=i*2;
}else
break;
}
}
//堆排序
void heapSort(int R[],int n){
for(int i=n/2;i>=1;i--)
downAdjust(R,i,n);
for(int i=n;i>=2;i--){
int temp=R[1];
R[1]=R[i];
R[i]=temp;
downAdjust(R,1,i-1);
}
}

· 时间复杂度:O(nlogn)
· 空间复杂度:O(1)

二路归并排序

可看作分而治之的过程,持续两两归并
(待)
· 时间复杂度:O(nlogn),与初始序列无关,最好最坏情况一致
· 空间复杂度:归并排序需转存整个待排序列,因此为O(n)

基数排序

即多关键字排序,2种实现方式:最高位优先和最低位优先
(待)