高级排序算法【2】--快速排序、归并排序、堆排序

4、快速排序

  • 从数列中挑出一个元素,称为基准; 
  • 重新排列数列,所有元素比基准小的摆放在基准前面,所有元素比基准大的摆在基准后面; 
  • 在这个分区结束之后,该基准就位于数列的中间位置; 
  • 递归地对基准左右两边的数列进行排序。
快速排序代码——第一步

def quick_sort(data, left, right):
    if left < right:
        mid = partition(data, left, right)
        quick_sort(data, left, mid - 1)
        quick_sort(data, mid + 1, right)

快速排序代码——第二步
def partition(data, left, right):
    left =0;right = len(data)-1
    tmp = data[left]
    while left < right:
        while left < right and data[right] >= tmp:
            right -= 1
        data[left] = data[right]
        while left < right and data[left] <= tmp:
            left += 1
        data[right] = data[left]
    data[left] = tmp  ###很显然,当left ==right时,循环会终止,即实现了左边的数都比tmp小,右边的数都比tmp大
    return left  ##left==right,所以返回left和right是一样的结果

5、归并排序(Merge Sort)

        分治法:很多有用的算法结构上是递归的,为了解决一个特定问题,算法一次或者多次递归调用其自身以解决若干子问题。 这些算法典型地遵循分治法的思想:将原问题分解为几个规模较小但是类似于原问题的子问题,递归求解这些子问题, 然后再合并这些问题的解来建立原问题的解。

现在我们就来看下归并排序是是如何利用分治法解决问题的。

  • 分解:将待排序的 n 个元素分成各包含 n/2 个元素的子序列
  • 解决:使用归并排序递归排序两个子序列
  • 合并:合并两个已经排序的子序列以产生已排序的答案

                     

当数组被完全分隔成只有单个元素的数组时,我们需要把它们合并回去,每次两两合并成一个有序的序列。

                    

一次归并代码
def merge(li, low, mid, high):
    i = low      #这里的low是左半段的第一个元素的索引
    j = mid + 1  #这里设置的mid是左半段的最后一个元素的索引,因此j就是右半段的第一个元素的索引
    ltmp = []    #新增一个列表来存储
    while i <= mid and j <= high:
        if li[i] <= li[j]:
            ltmp.append(li[i])
            i += 1
        else:
            ltmp.append(li[j])
            j += 1
    while i <= mid:
        ltmp.append(li[i])
        i += 1
    while j <= high:
        ltmp.append(li[j])
        j += 1
    li[low:high + 1] = ltmp
递归的调用归并排序
def merge_sort(li, low, high):
    if low < high:
    mid = (low + high) // 2
    mergesort(li, low, mid)
    mergesort(li, mid + 1, high)
    merge(li, low, mid, high)

算法分析:

时间复杂度:总的代价是 cnlg(n)+cn ,忽略常数项可以认为是 O(nlg(n))

空间复杂度:o(n)

6、堆排序

     为什么我要将堆排序放在最后呢,相对来说,堆排序是高级排序算法中相对不好理解的一中排序算法,我个人的看法而已。

这涉及了二叉树的理解,以及堆的理解,利用堆数据结构所设计的一种排序算法,通过每次弹出堆顶元素实现排序。

动画演示:

                

堆排序的过程:

  1. 建立堆
  2. 得到堆顶元素
  3. 去掉堆顶,将堆的最后一个元素放到堆顶,此时可通过一次调整重新使堆有序
  4. 堆顶元素为第二大元素
  5. 重复步骤3,直到堆变空

                                  

## 调整的过程
def sift(data, low, high):
    i = low          #当前父节点
    j = 2 * i + 1    #该父节点的左子节点
    tmp = data[i]
    while j <= high: #左节点在堆里
        if j < high and data[j] < data[j + 1]:  ## 有右节点,且大于左子节点
            j += 1                    ##由于是先判断了的,就是为了找出左右子节点中的较大者
        if tmp < data[j]:        ##判断父节点是否小于子节点中的较大者
            data[i] = data[j]    ##小于的话,就相互交换
            i = j                ##交换之后,交换的子节点成为父节点,此时原子节点处是空着的,让交换后的父节点成为新的子节点,在执行循环
            j = 2 * i + 1        ##到这里,在判断其左子节点是否在堆里,在就继续执行循环,不在就退出
         else:
             break
    data[i] = tmp      ##循环结束后,将最开始的父节点交换到最后循环结束时的那个父节点上
def heap_sort(data):
    n = len(data)
    for i in range(n // 2 - 1, -1, -1):  ##这里的n//2是最后一个元素的父节点,从后往前的建堆
        sift(data, i, n - 1)    ### 循环结束时,一个堆就建好了
    for i in range(n - 1, -1, -1):    ##是为了出数,也可以新开一个列表,来存储堆顶元素,出一个元素后,堆的个数同时减一
        data[0], data[i] = data[i], data[0]   
        sift(data, 0, i - 1)

本来还想记录下希尔排序的,不过用的机会不多,这里也不做介绍了。我觉得掌握前面的几种排序算法,就已经够了。


堆排序、快速排序、归并排序小结:

1)三种排序算法的时间复杂度都是o(nlgn)

2)一般情况下,就运行时间而言:
        快速<归并<堆

3)三种排序的缺点:

  • 快速排序:极端情况下,排序效率低
  • 归并排序:需要额外的内存开销
  • 堆排序:在快的排序算法中相对较慢

这里截张图作为总结,注这里的稳定性主要是说比如我们遇到相同的数时,稳定性好的,能保持先前的状态

               

 

参考资料 :

https://python-data-structures-and-algorithms.readthedocs.io/zh/latest/13_高级排序算法/merge_sort/

https://www.cnblogs.com/onepixel/articles/7674659.html

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

且行且安~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值