3.排序算法05——堆排序算法

树的定义

 二叉树

 满二叉树与完全二叉树

完全二叉树=满二叉树拿走几个(只能右边拿走,左边必须充满的)

例如:1.在下图的b中删去12号也是完全二叉树

2.在下图的b中删去12,11也是完全二叉树

3.在下图的b中删去9不是完全二叉树

 

 就是横过来读

公式*2:1.已知父节点下标为i,左子节点下标为2*i+1,右子节点下标为2*i+2

2.已知子节点(左右都一样)下标为j,父节点下标为(j-1)//2

 堆的定义:以下单独来看都可称之为堆,把堆比作行政机关的话,上一级的人员的能力值都比下一级(自己控制区域)的人员能力值高,称为大根堆。同理,上一级的人员的能力值都比下一级(自己控制区域)的人员能力值低,称为小根堆

 堆的性质——向下调整性质

 如何理解见下

比如这个省长2的“能力值”不太行,但是他的下面的县都是堆(合理控制的县)所以向下调整2的位置 

 把9,7之间较大的一个挪到省长的位置,然后发现2连县长都当不了,就把8,5中更大的挪到县长位置。

 最后发现2连村长都当不了,所以最终成为村民(即堆的最底层)

堆排序过程:

1.建立堆(“农村包围城市”从乡县开始建)

2.得到堆顶元素为最大元素

3.去掉堆顶,将堆最后一个元素放到堆顶,此时可通过一次调整重新使堆有序

4.堆顶元素为第二大元素

5.重复步骤三,直到堆变空

 但一般为了节省空间复杂度,会将树此时的最大元素直接与最后一个元素交换,然后标记i来确定有序区与树,进而有序区不参与下一轮向下调整。

 算法的代码实现

第一步:写出向下调整函数

def sift(li,low,high):#向下调整函数
    '''
    li:列表
    low:堆的根节点位置
    high:堆的最后一个元素的位置
    
    '''
    i=low #i最开始指向根节点
    j=2*i+1 #j开始是左孩子
    tmp=li[low] #把堆顶封存起来
    while j<=high: #只要j位置有数(i->父亲  j->孩子)
        if j+1<=high and li[j+1]>li[j]: #如果有右孩子并且比较大
            j=j+1 #j指向右孩子,不能左右交换,0->6.5
        if li[j]>tmp:
            li[i]=li[j]
            i=j #往下看一层
            j=2*i+1  #j=2*j+1同。tmp更大,把tmp放到i的位置上
        else: #把tmp放到某一级领导位置上
            break
    li[i]=tmp #把tmp放到某一级领导位置上(不满足while条件,即j超过high说明i是最后一层)
    #while 完->j 指向孩子中大的

第二步 排序算法的完成:

1.建立堆(农村包围城市)

def heap_sort(li):
    n=len(li)
    for i in range((n-2)//2,-1,-1): #i表示建堆的时候调整的部分的根的下标
        sift(li,i,n-1) # 建堆完成了,这里用n-1原因是j超过上面的high放缩为j超过下面的high,更方便求

2.完成234步

def heap_sort(li):
    n=len(li)
    for i in range((n-2)//2,-1,-1): #i表示建堆的时候调整的部分的根的下标
        sift(li,i,n-1) # 建堆完成了,这里用n-1原因是j超过上面的high放缩为j超过下面的high,更方便求
    for i in range (n-1,-1,-1): #i指向当前堆的最后一个元素
        li[0],li[i]=li[i],li[0] 
        sift(li,0,i-1) #i-1是新的high

第三步,整体实现

def sift(li,low,high):#向下调整函数
    '''
    li:列表
    low:堆的根节点位置
    high:堆的最后一个元素的位置
    
    '''
    i=low #i最开始指向根节点
    j=2*i+1 #j开始是左孩子
    tmp=li[low] #把堆顶封存起来
    while j<=high: #只要j位置有数(i->父亲  j->孩子)
        if j+1<=high and li[j+1]>li[j]: #如果有右孩子并且比较大
            j=j+1 #j指向右孩子,不能左右交换,0->6.5
        if li[j]>tmp:
            li[i]=li[j]
            i=j #往下看一层
            j=2*i+1  #j=2*j+1同。tmp更大,把tmp放到i的位置上
        else: #把tmp放到某一级领导位置上
            break
    li[i]=tmp #把tmp放到某一级领导位置上(不满足while条件,即j超过high说明i是最后一层)
    #while 完->j 指向孩子中大的

def heap_sort(li):
    n=len(li)
    for i in range((n-2)//2,-1,-1): #i表示建堆的时候调整的部分的根的下标
        sift(li,i,n-1) # 建堆完成了,这里用n-1原因是j超过上面的high放缩为j超过下面的high,更方便求
    for i in range (n-1,-1,-1): #i指向当前堆的最后一个元素
        li[0],li[i]=li[i],li[0] 
        sift(li,0,i-1) #i-1是新的high
from random import *
from time import time
start_time=time()
a=[i for i in range(10000)]
shuffle(a)
print(a)
heap_sort(a)
end_time=time()
used_time=end_time-start_time
print(a)
print(used_time)

运行结果展示

时间复杂度O(nlogn) 

 另附:python自带的树模块的函数应用

 heapify建堆函数,默认为小根堆

 部分图片参考自b站IT编程界的扛把子的python算法学习视频,本文仅供个人学习使用,特此声明

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值