排序问题是各种算法问题中比较重要的问题。导论也已排序开篇。
- 插入排序
插入排序的思想:假设数组的前i-1个元素已经排好序,再将第i个元素插入前i个元素中的合适的位置;循环这个过程直到最后一个元素被插入到合适的位置。
设计策略--增量法:在排好的[1,i-1]中加入第i个元素,形成有序的[1,i]。
注意细节:
- 由于1个元素的数组当然是有序的,因此在处理的时候,循环过程从2开始直到数组长度n即可。
- 插入过程中,对于具体要插入的位置的确定。一是可以从i-1,直到0递减和第i个元素比较,并后移,这种方式的插入排序称为直接插入排序。另一个做法是对插入位置,可以按照二分查找的方式,在[0,i-1]区段找到i的合适位置之后,再统一做移动,这种方式的插入排序称为折半插入排序。(注意这两者带来的差异:直接插入排序是稳定的排序,折半插入则不是稳定的)。
- 对已排序数组的假设,也可以假设数组的后半部分为排好序的,从而往前增长排好序的数组。
空间复杂度:O(1),只需要一个交换元素的空间。
时间复杂度:最好情况为已经排好序,这是也需要扫描整个数组,为O(n);最差情况为逆序,这时候每一个内循环都需要和所有的前i-1个元素比较,为O(n^2);
平均情况复杂度为O(n^2),平均的思想是每次循环,平均前i-1个元素中有一半大于第i个,一半小于第i个。
稳定性:
直接插入排序为稳定的排序。稳定的原因在于只会比较相邻的两个元素,从而不会产生逆序。折半则不是稳定的。
原地性:
插入排序(直接,折半等)为原地排序,即在任意时刻,数组中只有常数个元素不在原数组中。每次循环,最多i不再数组中。
应用场景:
由于插入排序的O(n^2)的时间复杂度,因此一般在数组大小n比较小时可以用。n较大时则一般不会使用。
- 归并排序
归并排序的思想:将n个元素的数组分解为两个n/2的数组,分别排序;递归的将两个子数组进行归并排序,直到子数组的长度为1;合并两个已经排好序的子数组。
设计策略--分治法:(分解)将问题分解为小的子问题;(解决)递归解决子问题,如果子问题足够小,则直接解决;(合并)将子问题的解合并为原问题的解。
注意细节:
- 归并排序的关键在于,将两个已排序的子序列合并,因此称为归并排序。
- 对于递归的方式,由于为尾递归,可以用循环来替代,从而提高性能。
复杂度分析:
空间复杂度:O(n),为归并的时候需要暂存的子数组;如果以链表表示,则空间为O(1)。
时间复杂度:最好、最坏、平均时间都为O(nlgn)。
递归树的展开方式。复杂度的计算,树高度,叶节点数。递归式主定理。
稳定性:
归并排序交换发生在两种地方,一是只有两个元素的叶子节点,另一个是在归并的过程中。两个节点时如果相等不交换,则不影响稳定;归并时如果相等,则将下标小的先归并,则也不会影响稳定性。
原地性:
归并过程中,子数组是原数组的拷贝,因此,归并不是原地排序的。
应用场景:
归并排序的最坏时间比插入排序要好,因此一般,如果n比较大时性能比插入好。但是隐含在O中的因子归并比插入大,所以可以使用两者的结合,可以在划分比较小的时候使用插入排序。
- 扩展
插入排序已递归的方式来理解:要排序[1,n],先排序[1,n-1],在插入第n个元素。
二分查找:二分查找思想,时间复杂度为O(lgn)。
两数之和为x:数组中是否有两个元素之和为x,在O(nlgn)时间内的算法。
归并、插入混合使用的时间复杂度分析。
冒泡排序。
一元多项式的值的求解。霍纳规则。
逆序对。插入排序与逆序的关系。归并排序O(nlgn)时间内,求解任意数组的逆序数。