归并排序---序子后并 2019-11-23(未经允许,禁止转载)

1.归并排序是什么

归并排序,英文叫merge-sort,从英文名可以容易地看出它的原理:通过merge若干组有序子序列来实现整体的排序

归并排序是分治思想的典型体现。通过将待排序列划分成多组子序列,这些子序列又继续往下各自划分成多组,直到序列长度为1,视为序列有序,可以开始merge;这时不断自下而上地merge这些子序列,直到待排序列的排序完成。若将两个有序序列合并成一个有序序列,称为二路归并

2. 归并核心 --- 序子 + 后并

使用归并排序一个无序序列,说穿了就是2步

序子后并

  • 序子:让无序序列的子序列有序。如果子序列无序,继续利用
    归并排序这个子序列(递归),直到子序列有序(递归出口:子序列长度为1即认为有序)
  • :merge有序子序列

归并的递归思路

先看一下递归实现的自顶向下形式。如图,不断地二分产生子序列,直到子序列长度为1,即到达“80”“30”等这些结点,再不断向上merge子序列


归并排序---序子后并 2019-11-23(未经允许,禁止转载)_第1张图片
归并排序递归形式图示

归并的递推思路(自底向上)

那么同时,我们也可以倒过来,自底向上地归并。即叶子结点起,不断merge叶子结点,将多个叶子结点merge形成一新的叶子结点,一直到只剩一个结点为止。如图所示:


归并排序---序子后并 2019-11-23(未经允许,禁止转载)_第2张图片
自底向上归并图示

如果在自底向上merge的过程中,在最后一组出现子序列不能配对成组的情况,那么先留着这个子序列不做任何处理,因为在后续的merge过程中,总能用到它的。我把这种处理方式叫做【不成组子序列的滞后merge】。如图:

归并排序---序子后并 2019-11-23(未经允许,禁止转载)_第3张图片
不成组子序列的滞后merge

3.归并排序性能分析

3.1 时间复杂度分析

通过递归实现归并排序的过程构成了一棵归并树(例如上面的归并排序递归形式图示),这一点类似快速排序的递归过程。因为在树的每一层总是要近似地进行O(n)次操作,而树的层数为 1 + log n,因此归并排序的时间复杂度为O(nlogn),最好最坏都是O(nlogn)

3.2 空间复杂度分析

自顶向下的递归实现和自底向上的实现所需要的空间复杂度不同,需要具体分析
例如,使用递归形式归并排序一个无序数组,每次递归都要拆分数组,拆分后的数组需要新的空间;另外每次合并数组也需要开辟新的空间,空间复杂度是比较大的

3.3 排序稳定性分析

归并排序是稳定的。因为在merge的时候,我们可以保证如果两个当前元素相等,就把处在前面的序列的元素保存在结果序列的前面,从而保证稳定性

4. 归并排序的实现

4.1 递归形式

递归代码非常好写,找到分段函数就行

class solution:
    def mergeSort(self, l: list):
        length = len(l)
        # 写分段函数
        if length == 1:
            return l
        else:
            # 核心代码
            mid = length / 2
            # 序子
            l1 = self.mergeSort(l[:mid])
            l2 = self.mergeSort(l[mid:])
            # 并
            return self.merge(l1, l2)
              
    def merge(self, l1, l2):
        # 新开辟一个list,因此是非原地修改,会增加空间消耗
        # 当然也可以选择往l1或者l2插入元素来实现原地修改的merge
        result = list()
        # 指针分别指向两个序列的首个元素
        i = j = 0
        length_l1 = len(l1)
        length_l2 = len(l2)
        # 当两个指针都在有效范围内的时候才merge,否则直接拼接
        while i < length_l1 and j < length_l2:
            if l1[i] <= l2[j]:
                result.append(l1[i])
                i += 1
            else:
                result.append(l2[j])
                j += 1
        if i == length_l1:
            result += l2[j:]
        if j == length_l2:
            result += l1[i:]
        return result

4.2 自底向上的归并实现

这篇博客写得很清楚了,不再赘述博客地址

相对递归写法而言,非递归写法要求对整个过程的各个步骤细节有清楚的认识,通常都是更难想更难写的

你可能感兴趣的:(归并排序---序子后并 2019-11-23(未经允许,禁止转载))