P1 What is 堆?
堆是一个完全二叉树!
如果对于一个完全二叉树,父节点的值一定不大于或不小于子节点的值(本文使用不大于),那么这个树就是一个堆
补充: 堆的性质
对于一个堆,每一个父与其子集合组成的子树为堆,且,每一个父与其子集合组成的子树为堆的完全二叉树是堆,即它们是等价的,我把这样的子树称之为三点子树
补充: 完全二叉树的性质
节点 n n n的儿子的下标: n ∗ 2 + 1 n*2+1 n∗2+1 与 n ∗ 2 + 2 n*2+2 n∗2+2
节点 n n n的父亲: ⌊ ( n − 1 ) / 2 ⌋ \lfloor(n-1)/2\rfloor ⌊(n−1)/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); //交换
}
}
完结撒花