我是根据b站上python数据结构里写的
网上有很多关于堆排的,我这里就不重复了,写点不一样的。
堆排原理,建堆-》出数——》向下调整(我看算法导论里叫维护堆,差不多一个意思)
堆是一种树,有大堆小堆,大堆就是父节点比子节点大.
也就是说,建完堆,堆顶是最大的,每次取堆顶交换数组最后一个(没排序的最后一个),就能排好。
父节点是(子节点-1)//2
左子节点是2父+1,右子节点是2父+2
太多我就不说了,可以看看其他博客,这里说点别的地方没有的。
步骤
1.建堆
建堆用向下调整也叫堆维护,建立个大堆
就是重第一非叶子节点开始,循环维护向下调整后,数组就是大堆。
数学证明算法导论中有,我还没看。
大概就是,重一开个小堆开始,向下调整,还挺不好理解的暂时相信它是对的。
向下调整的代码
def sift(li,low,high):
i =low #堆顶指针1
j = 2*i+1 # 指针2
tmp = li[low] #需要比较的数
while j<= high: #指针2别出界,出界就停了
if j+1 <=high and li[j+1]>li[j]: #有没有右子节点,有右子节点的话比较,让j指针是大的那个
j = j+1
if tmp < li[j]: # 比较两个指针大小,做出是否交换,和向下
li[i] = li[j]
i = j
j = 2*i+1
else:
li[i] = tmp #发现它是最大的,不用比了,停止,把tmp放在这层
break
else:
li[i] = tmp #循环完了,没有可以比的了,把tmp放在这
两个
就是两个指针比较,当然每一层实际上是三个数比较
左节点和右节点比较,找到大的,变成j,和tmp比较
如果j大把li[j]的值给到i的位置
如果停止了(可能是到底了,也可能是tmp比下面都大了),总之停止了,就把li[i] =tmp,把tmp放在这就行。
建堆代码
def hip_sort(li):
n = len(li)
for i in range((n-1-1)//2,-1,-1):
sift(li , i, n-1) #每次的low就是每个非叶子节点,一直到0,high就是让它别出界就行,到n-1
先找到最后一个叶子节点,就是最后一个数它的父节点,最后一个数下标是数组长度-1,
它的父节点是(n-1-1)//2
python的//是除法并向下取整,就是啥意思,如果是左 ,减一处2后,应该是个整数,那就是它,
如果是右,减一处2,可能带个0.5,因为是向下取证,那还是它
因为python语法,右括号的-1是不包含-1的,到0,注意一下
2.出数并向下调整
for j in range(n-1,0,-1): # 从最后一个数,到倒数第二个(我感觉li[0]没必要和li[0]交换)
li[0],li[j] = li[j],li[0] #交换
sift(li,0,j-1) #向下调整,注意一下调整需要的high是j-1,交换完了,需要调整的数组就少了一个数
写的不太好,变革口诀吧
def sift(li,low,high):
i =low
j = 2*i+1
tmp = li[low]
while j<= high:
if j+1 <=high and li[j+1]>li[j]:
j = j+1
if tmp < li[j]:
li[i] = li[j]
i = j
j = 2*i+1
else:
li[i] = tmp
break
else:
li[i] = tmp
def hip_sort(li):
n = len(li)
for i in range((n-1-1)//2,-1,-1):
sift(li , i, n-1)
for j in range(n-1,0,-1):
li[0],li[j] = li[j],li[0]
sift(li,0,j-1)
建堆出数加调整,最后非叶子节点调到0.
0和堆的最后一个换,再调整。
一个i一个j双指针,i是根j是它子节点
while一循环,防止出界j小于等于high。
如果有右子节点,选出左右最大数下标。
如果子大于tmp,就交换到i位置。
并子变父,生成新子,否则停止tmp放在i。
停了就把tmp放i那。