数据结构与算法 | 归并排序

1 什么叫归并排序?

什么叫归并排序呢?这也是我们排序算法中目前最后一个,后面可能还会有补充!

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

归并排序就是两个大的步骤:

  • 首先进行拆分,直至分到只有1个元素为止。
  • 然后进行合并。两两有序合并,直至全部合并ok!

是不是看着相当easy?但讲道理,代码实现起来非常的difficult!所以今天小编给大家带来的代码实现部分还加入了新的环节,就是代码的拆解,看看究竟代码是如何一步一步进行的!

2 代码实现

2.1 思路

  • 首先将序列进行一分为二 然后再对半分 一直到分成1个元素为止!
  • 然后一左一右设置两个游标left right
  • 如果比较left和right对应的值 如果left小 将元素拿过来 然后游标右移 继续比较和right 如果right小把right拿过来 直至某一个list里全为空 此时再把剩下一个copy过来
  • 注意上述过程是从最小单位开始进行 逐个往上!
  • 肯定要用到递归的思路,而且传参进去应该也不止一个参数 还应该有始末位置![不一定]

2.2 代码

alist = [54,26,93,17,77,31,44,55,20]
sorted_alist = mergeSort(alist)
print(sorted_alist)
9 // 2
4
def merge_sort(alist):
    '''归并排序'''
    
    # 先拆分 - 算法复杂度为常数
    n = len(alist)  
    if n <= 1: # 只有一个元素 返回自身
        return alist
    mid = n // 2
    left_li = merge_sort(alist[:mid]) # 前面一部分
    right_li = merge_sort(alist[mid:]) # 后面一部分
#     merge(left_li, right_li) # 将两个子序列合并为一个新的整体
    '''
    问题:上面两个调用 需要返回值吗?
    区别于快排,这是传入了列表,而不是给定位置了
    left_li:采用归并排序后新的list
    right_li:采用归并排序后新的list
    然后合并两部分
    '''
    
    # 然后排序 - 算法复杂度为:n × 对数
    left_pointer, right_pointer = 0, 0 
    result = []
    n1 = len(left_li)-1
    n2 = len(right_li)-1
    
    while left_pointer <= n1 and right_pointer <= n2:
        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
    
    # 有一种情况是 如果任一为0了 直接把剩下的加到result
#     if left_pointer > n1:
#         result += right_li
#     elif right_pointer > n2:
#         result += left_li 
    # 上面这种情况复杂了 不管三七二十一 直接加上去就好!
    result += left_li[left_pointer:]
    result += right_li[right_pointer:]
    return result
alist = [54,26,93,17,77,31,44,55,20]
print(alist)
sorted_alist = merge_sort(alist)
print(alist) # 原先列表不会改变 返回有序的列表
print(sorted_alist)
[54, 26, 93, 17, 77, 31, 44, 55, 20]
[54, 26, 93, 17, 77, 31, 44, 55, 20]
[17, 20, 26, 31, 44, 54, 55, 77, 93]

2.3 代码拆解

上面递归看着还是比较生涩的,下面通过一步一步的拆解,来看上述过程是如何进行的!

right_li = merge_sort([77,31,44,55,20])
    left_li = merge_sort([77,31])
        left_li = merge_sort([77])
            return [77]
right_li = merge_sort([77,31,44,55,20])
    left_li = merge_sort([77,31])
        left_li = [77]
        right_li = merge_sort([31])
            return [31]
right_li = merge_sort([77,31,44,55,20])
    left_li = merge_sort([77,31])
        left_li = [77]
        right_li = [31]
right_li = merge_sort([77,31,44,55,20])
    left_li = merge_sort([77,31])
        left_li = [77]
        right_li = [31]
        return result = [31, 77]
right_li = merge_sort([77,31,44,55,20])
    left_li = [31, 77]
    right_li = merge_sort([44,55,20])
        left_li = merge_sort([44])
            return [44]
right_li = merge_sort([77,31,44,55,20])
    left_li = [31, 77]
    right_li = merge_sort([44,55,20])
        left_li = [44]
        right_li = merge_sort([55,20])
            left_li = merge_sort([55])
                return [55]
            right_li = merge_sort([20])
                return [20]
            return result = [20,55]
right_li = merge_sort([77,31,44,55,20])
    left_li = [31, 77]
    right_li = merge_sort([44,55,20])
        left_li = [44]
        right_li = [20,55]
        return result = [20,44,55]
right_li = merge_sort([77,31,44,55,20])
    left_li = [31, 77]
    right_li = [20,44,55]
right_li = merge_sort([77,31,44,55,20])
    left_li = [31, 77]
    right_li = [20,44,55]
    return result = [20,31,44,55,77]
right_li = [20,31,44,55,77] # 大功告成!

上面就是右边序列一步一步拆解的过程!结束!

3 算法时间复杂度

  • 最优/最差算法复杂度为: O ( n l o g n ) O(nlogn) O(nlogn) 先两两比较 线性复杂度 然后一分为二 对数 故两者相乘!最优和最差都是这个,因为均是一分为二,特不特殊情况无所谓的。
  • 空间复杂度是比其余大的,因为新生成了一个list,其余的就是在原来上面修改的!所以需要占用更多的空间!

4 算法稳定性

稳定!比如下图:
在这里插入图片描述
由于代码:

if left_li[left_pointer] <= right_li[right_pointer]: # 加上等号保证稳定

所以左边的54首先插入result,然后再是右边的54,所以算法是稳定的!

参考

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值