常见排序算法总结

本篇博客是《大话数据结构》第9章排序部分的学习笔记,部分图文参考国外博客

排序算法的稳定性:对于值相等的元素,排序前后的相对位置不发生改变,则称该排序算法为稳定的排序。

内排序与外排序:根据在排序过程中待排序的记录是否全部被放置在内存中分为内排序和外排序。

根据排序算法中借助的主要操作,可把内排序分为:插入排序、交换排序、选择排序和归并排序。

根据排序算法的复杂性(非时间复杂度)可将其分为简单排序算法和改进算法。其中简单排序算法包括冒泡排序、简单选择排序、直接插入排序。改进算法包括希尔排序、堆排序、归并排序、快速排序。

1.冒泡排序

基本思想:以递增排序进行说明,若有N个元素需要排序,每趟排序从头开始两两比较相邻元素的大小,将大的那个放到后面,则每趟结束都把最大的那个找了出来。N个元素的话需要进行N-1趟排序,第i趟排序需要进行N-i次比较。

冒泡排序

改进:设立Flag,当某一趟未发生交换时,排序即可终止。

时间复杂度分析:

最好的情况就是序列本身有序,那么进行1趟(n-1)次比较,发现没有数据交换排序即可终止,时间复杂度为O(n)。

最坏的情况就是逆序,比较n-1趟,每趟比较n-i次,比较次数总共为(n-1)+(n-2)+...+4+3+2+1=n(n-1)/2次,移动次数也这么多。时间复杂度为O(n^2)

平均时间复杂度:O(n^2)

2.简单选择排序

由于 冒泡排序的思想是通过不断地交换从而完成最终排序,操作非常频繁。那能不能减少交换次数,只在找到合适的值时才进行交换完成排序呢?这就是选择排序的基本思想。在冒泡和选择排序之间一般选选择排序。

基本思想:每一趟在n-i+1(i=1,2,...,n-1)个记录中选取关键字最小的记录作为有序序列的第i个记录。也就是相当于将输入列表/数组分为两部分:已经排序的子列表和剩余要排序的子列表。不断在未排序的子列表中找到最小的元素,并将其与第一个未排序的元素进行交换。此过程重复进行,直到列表完全排序。需要记录已排序列表的末尾。下图所示中每次未排序列表中的最小值与竖线右边的第一个进元素行交换。

选择排序

时间复杂度:

无论最好还是最坏情况,比较的次数都是一样多的。第i趟排序需要进行n-i次关键字的比较,共比较n-1趟,总次数为n(n-1)/2次。对于交换次数而言,最好的情况下,交换0次。书上说最坏的情况,即降序的情况,交换次数为n-1次(不是的吧,这个如果是降序排序的话,只用交换n/2次向下取整就可以了,因为交换是对称的)。排序时间是比较与交换次数的总和,因此总的时间复杂度依然为O(n^2)。

尽管与冒泡排序同为O(n^2),但简单选择排序的性能还是略优于冒泡排序。

3.直接插入排序

基本思想:就是理扑克牌的思想。基本操作是将一个记录插入到已经排好序的有序表中,从而得到一个新的、记录数增1的有序表。需要设置哨兵暂存当前的值,与已排序好的列表进行比较从而找到哨兵在其中的位置。主循环从i=2开始,默认第一个元素在已排序序列中。

插入排序

时间复杂度:

最好的情况是本身有序,比较的次数为n-1次,不需要移动,时间复杂度为O(n)。

最坏的情况是本身逆序如{6,5,4,3,2},需要比较n-1次,移动......时间复杂度为O(n^2)

4.快速排序

基本思想:通过一趟排序将待排记录分割成独立的两部分,其中一部分记录的关键字均比另一部分记录的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序的目的。

快速排序

主要的步骤如下:

(1)我们首先选择一个元素,称为数组的基准元素(pivot)。
(2)将所有小于基准元素的元素移动到基准元素的左侧;将所有大于基准元素的元素移动到基准元素的右侧。

(3)递归地将上述两个步骤分别应用于比上一个基准元素值更小和更大的元素的每个子数组。

时间复杂度分析:

如果用递归树来描述算法的执行情况,

在最优情况下,Partition每次都划分得很均匀,递归树的深度就位log2(n)向下取整+1,即仅需递归log2(n)次,需要时间为T(n)的话,第一次Partition应该需要对整个数组扫描一遍,做n次比较,然后获得的pivot将数组一分为二,那么各自还需要T(n/2)的时间(注意是最好情况,所以平分两半)。如下图所示,最优情况快排的时间复杂度为O(nlogn)。

在最坏情况下,待排序的序列为正序或者逆序,每次划分只得到一个比上一次划分少一个记录的子序列,另一个为空。O(n^2)。

平均情况下,O(nlogn)。

空间复杂度分析:

就空间复杂度来说,主要是递归造成的栈空间的使用,最好情况,递归树的深度为log2n,其空间复杂度也就为0([logn),最坏情况,需要进行n-1递归调用,其空间复杂度为0(n),平均情况,空间复杂度也为0([logn)。
 

快速排序的优化:

1.关于pivot(枢轴)的选择:随机选取枢轴/三数取中(左端、右端、中间三个数进行排序,取中间的那个数)/九数取中(从数组中分三次取样,每次取三个各取出中值,再从这三个中值中取出一个中值)。

2.优化不必要的交换:将pivot备份到L.r[0]中,采用替换而不是交换的方式进行操作。

3.如果数组很小,快排反而不如直接插入排序来得好。直接插入是简单排序中性能最好的。由于快排用到了递归操作,在大量数据排序时比较快,但是少量数据时反而没那么好。因此加入判断,当high-low不大于某个常数时,就使用直接插入排序。

4.优化递归操作,实施尾递归优化。

 

5.归并排序

基本思想:

归并排序

(1)连续划分未排序列表,直到有N个子列表,其中每个子列表有1个“未排序”元素,N是原始数组中的元素数。
(2)重复合并,即一次将两个子列表合并在一起,生成新的排序子列表,直到所有元素完全合并到一个排序数组中。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章