数据结构与算法Python版 归并排序与快速排序


一、归并排序

归并排序Merge Sort

  • 归并排序的思路是将数据表持续分裂为两半,对两半分别进行归并排序。属于递归算法,体现了分治策略在排序中的应用
    • 递归的基本结束条件是:数据表仅有1个数据项,自然是排好序的;
    • 缩小规模:将数据表分裂为两半,规模减为原来的二分之一;
    • 调用自身:将两半分别调用自身排序,然后将分别排好序的两半进行归并,得到排好序的数据表
      在这里插入图片描述
def merge_sort(lst):
    # 数据项数量等于1,直接返回
    if len(lst) <= 1:
        return lst
    # 数据项数量大于1,分裂为两半
    mid = len(lst) // 2
    left_lst = merge_sort(lst[:mid])
    right_lst = merge_sort(lst[mid:])

    # 对结果进行合并:每次比对左右半部第1个数据项,小的入列
    result_lst = []
    while left_lst and right_lst:
        if left_lst[0] <= right_lst[0]:
            result_lst.append(left_lst.pop(0))
        else:
            result_lst.append(right_lst.pop(0))

    # 左右半部剩余项入列
    result_lst.extend(left_lst if left_lst else right_lst)
    return result_lst


test_list = [18, 48, 85, 67, 44, 36, 53, 32, 3, 5]
print(merge_sort(test_list))


### 输出结果
[3, 5, 18, 32, 36, 44, 48, 53, 67, 85]

归并排序-算法分析

  • 将归并排序分为两个过程来分析:分裂和归并
    • 分裂的过程,借鉴二分查找中的分析结果,是对数复杂度,时间复杂度为O(log n)
    • 归并的过程,相对于分裂的每个部分,其所有数据项都会被比较和放置一次,所以是线性复杂度,其时间复杂度是O(n)
  • 综合考虑,每次分裂的部分都进行一次O(n)的数据项归并,总的时间复杂度是O(nlog n)
  • 归并排序算法使用了额外1倍的存储空间用于归并

二、快速排序

快速排序Quick Sort

  • 快速排序的思路是依据一个“中值”数据项来把数据表分为两半:小于中值的一半和大于中值的一半,然后分别进行快速排序。也属于递归算法。
    • 基本结束条件:数据表仅有1个数据项
    • 缩小规模:根据“中值”,将数据表分裂分为两半,最好情况是相等规模的两半
    • 调用自身:将两半分别调用自身进行排序
  • 分裂过程
    • 假设将表中第1项作为“中值”
    • 设置左右标(left/rightmark),左标向右移动,右标向左移动
    • 左标一直向右移动,碰到比中值大的就停止;右标一直向左移动,碰到比中值小的就停止;然后把左右标所指的数据项交换
    • 重复上面步骤,直到左标移到右标的右侧,停止移动。这时,将中值和右标所指位置数据项交换
    • 分裂完成,左半部比中值小,右半部比中值大

示例:第1趟分裂过程
在这里插入图片描述

def quick_sort(lst, first, last):
    if first < last:  # 基本结束条件
        pivot_value = lst[first]
        left_mark = first + 1
        right_mark = last

        done = False
        while not done:
            # 左标向右移动
            while left_mark <= right_mark and lst[left_mark] <= pivot_value:
                left_mark += 1
            # 右标向左移动
            while lst[right_mark] >= pivot_value and right_mark >= left_mark:
                right_mark -= 1
            # 直到左标移到右标的右侧
            if right_mark < left_mark:
                done = True
            else:
                # 左右标所指的数据项交换
                lst[left_mark], lst[right_mark] = lst[right_mark], lst[left_mark]
        # 中值与右标所指数据项交换
        lst[first], lst[right_mark] = lst[right_mark], lst[first]

        quick_sort(lst, first, right_mark - 1)
        quick_sort(lst, right_mark + 1, last)

test_list = [18, 48, 85, 67, 44, 36, 53, 32, 3, 5]
quick_sort(test_list, 0, len(test_list) - 1)
print(test_list)


### 输出结果
[3, 5, 18, 32, 36, 44, 48, 53, 67, 85]

快速排序-算法分析

  • 快速排序过程分为两部分:分裂和移动。如果分裂总能把数据表分为相等的两部分,那么就是O(log n)的复杂度;而移动需要将每项都与中值进行比对,还是O(n)。综合起来就是O(nlog n)
  • 与归并排序算法不同,快速排序不需要额外的存储空间
  • 快速排序的关键是“中值”的选取,如果分裂时造成左或右部分始终没有数据,时间复杂度退化到O(n^2)。此时,加上递归调用开销,性能比冒泡排序还差
  • 中值取值的方法:三数取中法,九数取中法,随机取中法等

三、小结

在解决实际问题时,不要只考虑算法理论的时间复杂度,需要综合考虑算法的运行环境要求、处理数据对象的特性等因素,来选择合适的算法。


您正在阅读的是《数据结构与算法Python版》专栏!关注不迷路~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值