二叉堆排序

P1 What is 堆?

堆是一个完全二叉树!

如果对于一个完全二叉树,父节点的值一定不大于或不小于子节点的值(本文使用不大于),那么这个树就是一个堆

补充: 堆的性质

对于一个堆,每一个父与其子集合组成的子树为堆,且,每一个父与其子集合组成的子树为堆的完全二叉树是堆,即它们是等价的,我把这样的子树称之为三点子树

补充: 完全二叉树的性质

节点 n n n的儿子的下标: n ∗ 2 + 1 n*2+1 n2+1 n ∗ 2 + 2 n*2+2 n2+2

节点 n n n的父亲: ⌊ ( n − 1 ) / 2 ⌋ \lfloor(n-1)/2\rfloor (n1)/2

P2 怎样把无序数组array变成堆?

此时我们把 a r r a y array array当成一个完全二叉树,其下标就是树内的编号。

在此介绍操作 h e a p _ u p d t heap\_updt heap_updt,参数为点n的下标:

对于一个父节点 n n n n n n的子节点集合 c h i l d child child,如果 c h i l d m a x > n child_{max}>n childmax>n,则交换 c h i l d m a x child_{max} childmax n n n

h e a p _ u p d a t e heap\_update heap_update操作可以保证这三个节点(可以更少)构成的子树为堆

交换过后再对 n n n进行 h e a p u p d t heap_{updt} heapupdt操作,维护交换影响的三点子树成为堆

代码放出:

inline void heap_updt(int tree[],int len,int ptr)//树数组,树大小,当前节点
{
	if(ptr>len) return;
	int c1=ptr*2+1;//第一个儿子
	int c2=ptr*2+2;//第二个儿子
	int max=ptr;
	if(c1<=len&&tree[c1]>tree[max])
		max=c1;
	if(c2<=len&&tree[c2]>tree[max])
		max=c2;
	if(max!=ptr)//当前子树(当前节点与其儿子组成的树)不是堆,进行heap_updt操作
	{
		swap(tree,ptr,max);
		heap_updt(tree,len,max);//进行递归
	}
}

跳出时就可以保证 n n n达到最底部(即子节点没有比n大的了),可以用递归模拟。

所以只需要找出 a r r a y array array倒数第二层的最后一个有子的节点,即 a r r a y array array内最后一个节点的父节点 F a l a s t Fa_{last} Falast,从 F a l a s t Fa_{last} Falast a r r a y array array根节点

进行逆序遍历进行 h e a p _ u p d t heap\_updt heap_updt操作即能保证每一个三点子树为堆

代码放出:

inline void heap_build(int tree[],int len)
{
	int bg=(len-1)/2;
	for(int i=bg;i>=0;i--)
		heap_updt(tree,len,i);//逆序执行heap_updt
}

此时这个完全二叉树一定是个堆

堆排怎么搞?

前面提到,对于一个堆,父节点的值一定不大于或不小于子节点的值。

所以对于 a r r a y array array来说,目前根节点一定是最大的,第二大的一定是其儿子集合中较大的一个

我们交换 a r r a y array array的根与最后一个节点,并从 a r r a y array array切断最后一个节点(即 a r r a y array array的最后一个值固定了(一定最大),只需考虑前面的就好了)

因为我们只交换了根,影响到以根为根的三点子树,所以只对根进行 h e a p u p d t heap_{updt} heapupdt操作就可以维护保证当前 a r r a y array array的树部分一定是堆。

重复进行当前操作即可。

代码放出:

inline void heap_sort(int tree[],int len)
{
	heap_build(tree,len);//先建堆
	for(int i=len;i>=0;i--)
	{
		heap_updt(tree,i,0);//每次树的大小减一	
		swap(tree,0,i);	//交换
	}
}

完结撒花

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值