In-place sort:不占用额外内存或占用常数内存排序算法:插入排序,冒泡排序,选择排序,快速排序,堆排序
Out-place sort:归并排序,基数排序,计数排序,桶排序
当需要对大量数据进行排序时,选择In-place sort就显示出优点了。
stable sort:插入排序,冒泡排序,归并排序,计数排序,基数排序,桶排序。
unstable sort:选择排序,快速排序,堆排序。
如何对于不稳定的算法进行改进,使得那些不稳定的算法稳定?网上提出,对于每个输入元素加一个index,表示初始时的数组索引,当不稳定的算法排好序后,对于相同的元素对index排序即可。
一、插入排序
特点:不占用额外内存空间,稳定排序
最优复杂度:当输入数组是排好序的数组的时候,复杂度为O(n),而快速排序在这种情况下会产生O(N^2)的复杂度。
最差复杂度:当输入数组为倒序时,复杂度为O(n),插入排序比较适用于“少量元素的数组”。
插入排序的插入过程是否能用二分查找?不能,因为要空出插入新元素的位置。
快速排序(不使用随机化)是否一定比插入排序快?不一定,当输入的数组已经排好序时,插入排序时间比快排快。
二、冒泡排序
特点:stable sort,In-place sort
思想:通过两两交换,像水中的泡泡一样,小的先冒出来,大的后冒出来。
最坏运行时间:O(n^2)
最佳运行时间:O(n^2)(可以改进使得最佳运行时间为O(n))
在算法导论中思考题中问:冒泡排序和插入排序哪个更快?答:插入排序的速度直接是逆序对的个数,而冒泡排序中交换的次数是逆序对的个数,因此冒泡排序的执行时间至少是逆序对的个数,因此插入排序的执行时间至少比冒泡排序快。
******************************************************
改进版的冒泡排序
最佳运行时间:O(n)
最坏运行时间:O(n^2)
其实就是在排序过程中定义一个变量,如果一轮循环下来,没有交换过,则说明已经是排好序的,无需再次排序。
三、选择排序
特点:In-place sort ,unstable sort。
思想:每次找一个最小值。
最佳运行时间:O(n^2)
最坏运行时间:O(n^2)
算法导论中问:为什么伪代码中第三行只有循环n-1次而不是n次?
因为前n-1个确定是最小的n-1个元素,则剩下一个一定是最大的,因此肯定是已经排好序的。
递归式:T(n) = T(n-1) +O(n) =>T(n) = O(n^2)
四、归并排序
特点:Out-place sort,stable sort
思想:运用分治法思想解决排序问题
最坏运行情况:O(nlgn)
最佳运行时间:O(nlgn)
分治法介绍:分治法就是将原问题分解为多个独立的子问题,且这些子问题的形式和原问题相似,只是规模上减少了,求解完子问题后合并结果构成原问题的解。
分治法通常有3步:Divide(分解子问题的步骤)、Conquer(递归解决子问题的步骤)、Combine(子问题解求出来后合并成原问题解的步骤)。
假设Divide需要f(n)时间,Conquer分解为b个子问题,且假设子问题大小为a,combine需要g(n)时间,则递归式为:T(n) = bT(n/a)+f(n)+g(n)
算法导论思考题4-3(参数传递)能够很好的考察对于分治法的理解。
就如归并排序,Divide的步骤为m=(p+q)/2,因此O(1),combine步骤为merge()函数,conquer步骤为分解为两个子问题,子问题大小为n/2,因此:归并排序的递归式:T(n) = 2T(n/2)+O(n)
而求解递归式的三种方法有:
(1)替换法:主要用于验证递归式的复杂度。
(2)递归树:能够大致估算递归式的复杂度,估算完后可以用替换法验证。
(3)主定理:用于解一些常见的递归式。
问:归并排序的缺点是什么?
归并排序是Out-place sort,因此相比快排,需要很多额外的空间。
问:为什么归并排序比快排慢?
虽然渐进复杂度一样,但是归并排序的系数比快排大,也就是说归并排序还需要复制数组。
问:对于归并排序有什么改进?
就是在数组长度为k时,用插入排序,因为插入排序适合对小数组排序。在算法导论思考题2-1中介绍了,复杂度为O(nk+nlg(n/k)),当k=O(lgn)时,复杂度为O(nlgn)
五、快速排序
特性:unstable sort,In-place sort
最坏运行时间:O(n^2),当输入数组已排序时,时间为这个。当然可以通过随机化来改进(shuffe array或者randomized select pivot)使得期望运行时间为O(nlgn).
最佳运行时间:O(nlgn)
快速排序的思想也是分治法。
当输入数组的所有元素都一样时,不管是快速排序还是随机化快速排序的复杂度都是O(n^2),而在算法导论第三版的思考题7-2中通过改变Partition函数,从而改进复杂度为O(n).
注意:只要partition的划分比例是常数的,则快排的效率就是O(nlgn),比如当partition的划分比例是10000:1时,快排的效率还是O(nlgn)
六、堆排序
1964年Williams提出。
特性:unstable sort、In-place sort。
最优时间:O(nlgn)
最差时间:O(nlgn)
此文介绍了堆排序的最优时间和最差时间的证明:http://blog.csdn.net/xiazdong/article/details/8193625
思想:运用了最小堆、最大堆这个数据结构,而堆还能用于构建优先队列。
优先队列:
普通的队列是一种先进先出的数据结构,元素在队列尾追加,而在队列头删除。在优先队列中,元素被赋予优先级,当访问元素时,具有最高优先级的元素最先删除,优先队列具有最高级先出的行为特征。优先队列插入和删除元素的复杂度都是O(lgn)。
堆排序,快速排序,归并排序一样都是时间复杂度为O(nlgn)的集中常见排序算法。
二叉堆是完全二叉树或者是近似完全二叉树。
二叉堆满足两个特性:
1. 父节点的键值总是大于等于(小于等于)任何一个子节点的键值。
2. 每个节点的左子树和右子树都是一个二叉堆(都是最大堆或最小堆)。
当父节点的键值总是大于或等于任何一个子节点的键值时为最大堆。当父节点的键值总是小于或等于任何一个子节点的键值时为最小堆。
堆的存储:
一般都用数组来表示堆,i节点的父节点下标就是(i-1)/2,它的左右子节点下标分别为2*i+1和2*i+2。如第0个节点左右子节点下标为1和2.
插入元素时,需要从下向上调整堆;
删除元素时,只能删除最上边的,需要从上向下调整堆;
七、计数排序
特性:stable sort,out-space sort。
最坏运行时间:O(n+k)
最好运行时间:O(n+k)
当k = O(n)时,计数排序时间为O(n)
计数排序是一个非基于比较的排序算法,它的优势在于对一定范围内的整数排序中,它的复杂度是O(n+k),其中k是整数的范围,快于任何比较算法,但这是一种牺牲空间换取时间的做法,而且当O(k)>O(nlgn)时,其效率反而不如基于比较的排序。
计数排序的基本思想是对于给定的输入序列中的每一个元素x,确定该序列中值小于元素x的元素的个数。一旦有了这个信息,就可以将x直接存放到最终的输出序列的正确位置上。
八、基数排序
stable sort,out-place sort;
九、桶排序
stable sort,out-place sort;
十、折半插入排序
折半插入排序,是在直接插入排序的基础上进行改进,考虑到直接插入排序将很多开销放在比较和数据移动上。比较是不可避免的,有没有什么办法可以减少比较的次数呢?于是想到在前面查找算法中的折半查找算法。
十一、希尔排序
希尔排序,是分组插入排序
最优时间复杂度:O(n)
最差:O(n2)
平均:O(n1.5)
十二、枚举排序
枚举排序,通常也被叫做秩排序,算法基本思想是:对每一个要排序的元素,统计小于它的所有元素的个数,从而得到该元素在整个序列中的位置,时间复杂度为O(n^2)
外部排序的总时间 = 内部排序(产生初始归并段)所需时间+外存信息读取时间+内部归并所需的时间
参考:http://blog.csdn.net/xiazdong/article/details/8462393