归并排序

归并排序

描述:

  归并排序是采用分治法的一个非常典型的应用。归并排序的思想就是先递归分解数组,再合并数组

  合并:

    将数组分解最小之后,然后合并两个有序数组,基本思路是比较两个数组的最前面的数,谁小就先取谁,取了后相应的指针就往后移一位。然后再比较,直至一个数组为空,最后把另一个数组的剩余部分复制过来即可。

分析演示:

时间复杂度:

  最优时间复杂度: O(nlogn)
  最坏时间复杂度: O(nlogn)

描述:

  归并排序只有一种情况, 所以最优和最坏的复杂度是一样的

分析:

  归并排序是由两部分组成的: 拆分, 合并   

  拆分:

    将列表一分为二, 一直拆, 直到被拆分为一个个长度为一的小列表, 每次拆一半, 拆n次, 就会把列表拆分完
    因此: 分析的时间复杂度为 O(log2^n)

  合并:

    合并: 先比较左右两个列表内的元素, 然后进行归位, 也就是合并
    因此: 合并的时间复杂度为 O(n)

  优化:

    O(T) = O(n * log2^n) = O(nlogn)

稳定性: 稳定

  描述:

      因为列表拆分与合并都是由左至右依次执行的, 合并的判断条件是, 如果左列表的元素小于等于右列表的元素, 将元素添加到新列表内, 如果两个元素相等, 左侧的元素先添加到新的列表内, 相对顺序并没有改变

代码:

简洁版:

def merge_sort(Li):
    n = len(Li)
    if n <= 1:
        return Li
    mid = n // 2
    left_Li = merge_sort(Li[:mid])
    right_li = merge_sort(Li[mid:])
    left_pointer, right_pointer = 0, 0
    result = []
    while left_pointer < len(left_Li) and right_pointer < len(right_li):
        if left_Li[left_pointer] <= right_li[right_pointer]:
            result.append(left_Li[left_pointer])
            left_pointer += 1
        else:
            result.append(right_li[right_pointer])
            right_pointer += 1
    result += left_Li[left_pointer:]
    result += right_li[right_pointer:]
    return result
View Code

注释版:

"""先拆分, 后归并,
拆分: 递归调用merge_sort(), 将整个列表拆分成n个小列表, 小列表的长度为1
归并: 当列表拆分完毕之后, 执行下面的代码, 先进行比较然后进行归位, 然后返回函数的引用
"""
import random


def merge_sort(Li):
    # 先判断传递进来的列表的长度, 如果列表的长度小于等于1, 直接返回
    # 列表的长度小于等于1, 分两种情况
    # 1. 起始传递进来的就是一个空列表或列表只有一个元素, 那么将他返回
    # 2. 在merge_sort()函数体内, 将起始的列表进行拆分, 被拆分之后的小列表, 如果长度为一, 则不能再进行拆分, 将小列表返回
    n = len(Li)
    if n <= 1:
        return Li
    mid = n // 2
    # 1. 设置列表的中间下标, 将原来的列表进行切片, 产生两个新的列表: 左列表, 右列表.
    # 2. 分别对这两个列表, 执行merge_sort()函数
    # 3. 其实merge_sort()函数, 是先对列表进行拆分, 拆分完毕之后, 再将挨着的两个列表进行合并

    left_Li = merge_sort(Li[:mid])
    right_li = merge_sort(Li[mid:])

    left_pointer, right_pointer = 0, 0
    # 设置两个指针, 分别指向两个列表
    result = []
    # 设置一个空列表, 将比对之后的元素, 追加到这个列表内

    while left_pointer < len(left_Li) and right_pointer < len(right_li):
        # 遍历两个列表, 如果其中一个列表遍历完毕, 则终止循环
        if left_Li[left_pointer] <= right_li[right_pointer]:
            # 如果左列表的元素, 小于等于右列表的元素, 将元素追加到新列表内
            # 指针向后移动
            result.append(left_Li[left_pointer])
            left_pointer += 1

        else:
            # 如果左列表的元素, 大于右列表的元素, 将右列表的元素追加到新列表内
            # 指针向后移动
            result.append(right_li[right_pointer])
            right_pointer += 1
    # 上面循环结束后, 代表其中一个列表遍历完毕, 将另外列表的内的元素追加到新列表内
    # 然后将新列表返回
    result += left_Li[left_pointer:]
    result += right_li[right_pointer:]
    return result
    # 因为这个函数是递归调用[自己调用自己], 当递归到底的时候, 执行合并, 合并完毕, 返回调用, 然后再次执行合并


if __name__ == "__main__":
    l = list(i for i in range(0, 10000 ))
    print("洗牌之前的列表:" + str(l))
    random.shuffle(l)
    print("洗牌之后的列表:" + str(l))
    print(merge_sort(l))
View Code

注意:

  归并排序相较于其他排序来说,

  归并排序时产生了新的列表, 消耗内存, 效率降低

  其他列表只是在原列表的基础上进行操作的

原文地址:https://www.cnblogs.com/amou/p/9045949.html