20200330-Timsort算法

1. Timsort算法(PYTHON中sort函数的御用算法)

冒泡排序

第 1 轮:依次比较第 1 和第 2 个,第 2 和第 3 个,…,第 n-1 和第 n 个元素,大的向后移,第 1 大的移到倒数第 1 个位置。
第 2 轮:依次比较第 1 和第 2 个,第 2 和第 3 个,…,第 n-2 和第 n-1 个元素,大的向后移,第 2 大的移到倒数第 2 个位置。
▪ ▪ ▪ ▪ ▪ ▪
第 n-1 轮:比较第 1 和第 2 个,大的向后移,第 n-1 大的移到倒数第 n-1 个位置,所有元素排序完成。
冒泡排序低效的原因之一是它对于含有n个元素的序列,总要进行n - 1轮共计n(n - 1)/2次比较,即使该序列已经完全有序。于是一种改进方法是跟踪每一轮的比较结果,如果在某一轮完成所有两两比较后,发现没有发生任何交换,就说明此时已经完成了排序任务,我们提前收工。下面是这种优化的冒泡算法以及实现代码。
▪ ▪ ▪ ▪ ▪ ▪
直到某轮比较过程中,未发生任何交换,表明排序已完成。

def optimized_bubble_sort(nums):
    n = len(nums)
    for i in range(n-1):
        swap = False
        for j in range(n-1-i):
            if nums[j] > nums[j+1]:
                nums[j], nums[j+1] = nums[j+1], nums[j]
                # 注意此处的实现细节,每轮只有第一次交换才会赋值 swap = True
                if not swap:
                    swap = True
        if not swap:
            break
    return nums

插入排序

第 1 轮:将第 2 个元素插到第 1 个元素的左边或右边,前 2 个元素排序完成。
第 2 轮:将第 3 个元素插到前 2 个元素的合适位置上,前 3 个元素排序完成。
▪ ▪ ▪ ▪ ▪ ▪
第 n-1 轮:将第 n 个元素插到前 n-1 个元素的合适位置上 (依次将其与第 n-1 个,第 n-2 个,…,第 1 个元素相比,若小于则交换位置,大于等于则跳出循环),所有元素排序完成。

def insersion_sort(nums):
    n = len(nums)
    for i in range(n-1):
        for j in range(i+1, 0, -1):
            if nums[j] < nums[j-1]:
                nums[j], nums[j-1] = nums[j-1], nums[j]
            else:
                break
    return nums

选择排序

第 1 轮:从第 1 个元素开始,找到最小元素的指标,将该元素与第 1 个元素交换。
第 2 轮:从第 2 个元素开始,找到最小元素的指标,将该元素与第 2 个元素交换。
▪ ▪ ▪ ▪ ▪ ▪
第 n-1 轮:从第 n-1 个元素开始,找到最小元素的指标,将该元素与第 n-1 个元素交换。

def selection_sort(nums):
    n = len(nums)
    for i in range(n-1):
        min_index = i
        for j in range(i+1, n):
            if nums[j] < nums[min_index]:
                min_index = j
        if min_index != i:
            nums[i], nums[min_index] = nums[min_index], nums[i]
    return nums

归并排序

归并排序的整体思路,是对于一个较大的序列,不断将其细分为较小序列,并递归地调用归并排序对每一小段分别排序,具体步骤和代码如下。
平均分割:把当前序列平均分割为两个子序列。
递归排序:对以上两个子序列,分别递归地用归并排序法排序 (在递归调用过程中,若序列为空或只有一个元素,表明已经完成排序)。
保序集成:将上一步得到的两个有序子列,保序集成到一起,完成整个序列的排序。

def merge(left, right):          # 保序集成算法
    result = []
    while left and right:
        if left[0] < right[0]:
            result.append(left.pop(0))
        else:
            result.append(right.pop(0))
    if left:
        result += left
    if right:
        result += right
    return result

def merge_sort(nums):
    n = len(nums)
    if n <= 1:                   # 若序列为空或只有一个元素,表明已经完成排序
        return nums
    m = n//2                     # 平均分割
    left = nums[:m]
    right = nums[m:]
    left = merge_sort(left)      # 递归排序
    right = merge_sort(right)
    return merge(left, right)    # 保序集成

快速排序

挑选基准:选取序列的最后一个元素作为基准,其值为基准值。
基准分割:对序列重新排序,所有比基准值小的元素放在基准前面,所有比基准值大的元素放在基准后面 (与基准值相等的元素可到任一边),在这个分割结束之后,完成对基准的排序。
递归排序:递归地将基准前面的子序列和基准后面的子序列用此快速排序法排序 (在递归调用过程中,若序列为空或只有一个元素,表明已经完成排序)。
由于其采用基准来划分序列,因而可以省略归并排序中对子列的集成这一步骤,不过相应的代价就是基准的选取方法会对算法性能产生较大影响。

def poor_quick_sort(nums):
    n = len(nums)
    if n <= 1:                # 若序列为空或只有一个元素,表明已经完成排序
        return nums
    less = []
    more = []
    pivot = nums.pop()        # 挑选基准
    for i in nums:            # 基准分割
        if i < pivot:
            less.append(i)
        else:
            more.append(i)
    nums.append(pivot)        # 这一句是为了让传入的 nums 保持不变
    return poor_quick_sort(less) + [pivot] + poor_quick_sort(more) # 递归排序

但是,当使用完全逆序整数序列 list(range(9999, -1, -1)) 对其进行测试时,由于每次所选基准两边元素的数量差都达到最大,也就是遇到了所谓的“最坏状况”,结果程序抛出了 RecursionError,即超过递归深度限制的异常,如下所示。解决这个问题的一种办法就是在第一步随机挑选基准。

Timsort

Timsort 是一种结合了插入排序和归并排序的混合算法。比较复杂,以后有空可以继续看看。

总结

时间复杂度
在这里插入图片描述
空间复杂度
在这里插入图片描述
参考1
参考2

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值