[排序N大件之]归并排序
终于到归并了吗?归并排序本质上是一个递归的过程,递归到底层,然后按照顺序再向上层归并,这是一个典型运用到分治+递归的思想的排序
算法的核心思想:分治+递归
分治:整个列表是否有序,取决于列表是否局部有序,因此将列表不断分裂成更小的区间,每个区间都依赖于更小的区间的有序,当所有的局部区间有序了,整个列表也就有序了
分治思想的的引入,就是对整个排序算法的不断优化
冒泡——>选择——>插入——谢尔排序——归并排序——快速排序
-
从谢尔排序开始,引入了[整个列表是否有序取决于局部列表是否有序]这一优化思想去优化插入排序
-
到归并排序,采用分治算法,不断的[分裂],最后[归并],有序的局部区间逐渐扩大,使整个列表变成有序
-
快速排序,是更优化的分治思想,它的局部列表不再是随机分的,而是相对于一个'pivot'[基准],在'pivot'左侧的都是比它小的元素,在右侧的都是比它大的元素,就不需要归并排序中对整个列表的归并过程,对空间复杂度有一定的提高?
思路:
-
将列表不断分裂
-
直到分裂成最小
-
然后开始归并
采用递归解法:
-
递归出口:当局部列表[分裂形成]的长度唯一时,就直接返回
-
减小问题规模:不断的列表分裂成两半
-
调用自身:对一半和另一半,进行排序,归并
代码如下:
def mergeSort(nums): # 递归出口 if len(nums) > 1: # 缩小问题规模 mid = len(nums) // 2 left = nums[:mid] right = nums[mid:] # 调用自身,对左右两侧分别进行归并排序:到这里左右两边就是一个已经排序好的了 mergeSort(left) mergeSort(right) # 最后再对左右两个子列表进行归并就可以了 i = j = k = 0 while i < len(left) and j < len(right): if left[i] < right[j]: nums[k] = left[i] i = i + 1 else: nums[k] = right[j] j = j + 1 k = k + 1 while i < len(left): nums[k] = left[i] k = k + 1 i = i + 1 while j < len(right): nums[k] = right[j] k = k + 1 j = j + 1 return nums if __name__ == "__main__": numbers = [3, 7, 5, 4, 8, 9, 6, 2, 1] print(mergeSort(numbers))
时间复杂度:O(nlogn),最好最坏都是O(nlogn)
空间复杂度:O(n),因为需要额外的递归栈空间
在这里先说一下我所学过的排序方法的稳定性吧:
稳定的排序:冒泡,插入,归并
不稳定的排序:选择,谢尔,快速