经典排序算法总结
排序算法 | 平均时间复杂度 | 空间复杂度 | 稳定性 |
---|---|---|---|
冒泡排序 | O(n^2) | O(1) | 稳定 |
选择排序 | O(n^2) | O(1) | 不稳定 |
插入排序 | O(n^2) | O(1) | 稳定 |
希尔排序 | 和增量有关 | O(1) | 不稳定 |
二路归并排序 | O(n*log2n) | O(n) | 稳定 |
快速排序 | O(n*log2n) | O(log2n) | 不稳定 |
堆排序 | O(n*log2n) | O(1) | 不稳定 |
基数排序 | O(d(n+r))//d趟分配,r个队列 | O( r ) | 稳定 |
一、插入排序
1.直接插入排序
为了实现对待排序表L[1……n]的排序,可先将L[1]视为已经排好序的子序列,将L[2]到L[n]依次插入到前面已经排好序的子序列中,执行n-1次就能得到一个有序的表。
直接插入排序中,每一趟排序就能确定一个元素的正确位置。
void InsertSort(ElemType A[],int n){
int i,j;
for(i=2;i<=n;i++){
if(A[i]<A[i-1]){
A[0]=A[i];//复制为哨兵,不存放元素
for(j=i-1;A[0]<A[j];--j){
A[j+1]=A[j];//逐个元素向后挪位
A[j+1]=A[0];//将待排序元素复制到插入位置
}
}
}
2.折半插入排序
直接插入排序是将未排序队列的首位从已排序队列的尾位置依此往前对比找到对应有序的位置。折半插入排序是运用了折半查找的办法依此找未排序队列中元素的对应有序位置。
3.希尔排序
希尔排序即通过设置步长,即“增量”,将待排序列表分割成若干子表,对各个子表分别进行直接排序,将待排序序列改造为“基本有序”序列后,再对全体记录进行一次直接插入排序。
优化了直接插入排序对基本无序的排序表和数据量大的排序表不友好的问题。又称为缩小增量排序。
空间效率:由于只使用了常数个辅助单元,因而空间复杂度为O(1)。
稳定性:当相同关键字的记录被划分到不同子表时,会改变它们之间的相对次序,因此希尔排序是一种不稳定的排序方法。
适用性:希尔排序算法只适用于线性表为顺序存储的情况。
二、交换排序
1.冒泡排序
在冒泡排序中,每一趟排序找到当前未排序队列中的最小(最大)元素,即关键字最小的元素像气泡一样一个一个往上冒。找最小(最大)元素的方法为从后往前(或从前往后)两两比较相邻元素的值。若为逆序则进行交换。
2.快速排序
快速排序的基本思想基于分治法。在待排序表中任取一个元素作为枢轴(或基准),通过一趟排序将待排序表分为独立的两部分,比枢轴小的元素在左子表,比枢轴元素大的在右子表。再分别对左子表右子表进行相同操作。这样每一趟快速排序总能确定枢轴元素的最终位置。
快速排序的空间复杂度位O(log2n),因为需要一个递归工作栈。
通过low,high两个指针实现划分算法
int Partition(ElemType A[],int low,int high){
ElemType pivot=A[low];
while(low<high){
while(low<high&&A[high]>=pivot)
--high;
A[low]=A[high];
while(low<high&&A[low]<=pivot)
++low;
A[high]=A[low];
}
A[low]=pivot;
return low;
}
void QuickSort(ElemType A[],int low,int high){
if(low<high){
int pivotpos=Partition(A,low,high);//划分
QuickSort(A,low,povotpos-1);//依此对子表进行递归划分
QuickSort(A,pivotpos+1,high);
}
}
三、选择排序
1.简单选择排序
简单选择排序和冒泡排序有相同之处也有不同之处。相同之处在于选择排序和冒泡排序每一轮总能确定一个待排序表中的最小关键字元素。区别在于交换方式上,冒泡排序是通过两两对比,选择排序是按顺序找出最小关键字元素。
2.堆排序
大根堆:最大元素放在根节点,且其任一非根结点的值小于或等于其双亲结点值。
小根堆:最小元素放在根结点,任一非根节点的值大于或等于其双亲结点值。
首先将无序序列构造成完全二叉树,然后按从左到右,从下到上的顺序进行调整,使每个元素都满足大根堆(小根堆)的特性。
四、归并排序
每次排序将两个或两个以上的有序表组合成一个新的有序表。
在二路归并排序中,初始待排序表有n个记录时,可将其视为n个有序的子表,然后两两归并,得到┍ n/2 ┑(n/2向上取整)。继续两两归并,直到合并成一个长度为n的有序表为止。
合并时通过两个指针分别指向子序列A和B的起始元素,将较小的元素放入数组C中,并将指针向后移动一位。依此进行比较,直到其中一个序列的元素全部排完,将另一个序列的剩余元素存放进序列C中。
二路归并排序的空间复杂度为O(n),因为需要一个辅助数组。
五、基数排序
基数排序不基于比较和移动,是一种借助多关键字排序思想对单逻辑关键字进行排序的方法。通常有最高位优先法和最低位优先法。
基数排序的原理是,将关键字按位数切割成不同的数字,然后将数字分别按顺序进行排序。
基数排序的空间复杂度为O( r ),r为队列数。例如四位数的基数排序需要四个队列,分别对应个十百千四位。