手撕堆排序

我是根据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那。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值