活动地址:CSDN21天学习挑战赛
题目描述
将一系列给定数字依次插入一个初始为空的小顶堆H[]。随后对任意给定的下标i,打印从H[i]到根结点的路径。
输入格式:
每组测试第1行包含2个正整数N和M(≤1000),分别是插入元素的个数、以及需要打印的路径条数。下一行给出区间[-10000, 10000]内的N个要被插入一个初始为空的小顶堆的整数。最后一行给出M个下标。
输出格式:
对输入中给出的每个下标i,在一行中输出从H[i]到根结点的路径上的数据。数字间以1个空格分隔,行末不得有多余空格。
输入样例:
5 3
46 23 26 24 10
5 4 3
输出样例:
24 23 10
46 23 10
26 10
代码长度限制 16 KB 时间限制 400 ms 内存限制 64 MB
解题代码
#include<stdio.h>
void headAdjust(int *num,int len){
//调整堆,每次插入一个值都对堆自下向上进行一个调整。
int k=len;
num[0]=num[k];
for(int i=len/2;num[i]>num[0];i/=2){
num[k]=num[i];
k=i;
}
num[k]=num[0];
}
int main()
{
int num[1005];
int len=0,n,m,inNum;
while(scanf("%d%d",&n,&m)!=EOF){
for(int i=1;i<=n;i++){
scanf("%d",&inNum);
num[++len]=inNum;
headAdjust(num,len);
}
for(int i=1;i<=m;i++){
scanf("%d",&inNum);
while(inNum>=1){
if(inNum==1)
printf("%d",num[inNum]);
else
printf("%d ",num[inNum]);
inNum/=2;
}
if(i!=m)//输出注意格式
printf("\n");
}
}
}
解题思路
就是一个简单的插入小顶堆,每次插入一个值的时候都与其上面的结点进行比较交换,小的放上面,大的放下面,然后一直交换到根节点就行。
输出的时候一直除2就行,就是要注意输出格式,这是PTA的题目,对格式的要求还是比较强的。
小顶堆就是将一个一个数组看作是一颗完全二叉树,即编号为i的结点左右子树分别是结点编号2i和2i+1。然后每个结点的需要满足结点i的值<=结点2i的值&&结点i的值<结点2i+1的值,满足上述条件才能成为小顶堆,结点值大于左右结点的为大顶堆。
用例解析
初始数组num,样例为46 23 26 24 10
下标 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|
值 |
当我们输入第一个值46时,数组内没数据,直接存放
下标 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|
值 | 46 |
当我们输入值23时,先将其存放在下标为2的位置
下标 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|
值 | 46 | 23 |
按照顶堆的规则,i的左右子树分别为2i和2i+1,也就是如下图所示
但是此时不符合小顶堆的规则,小顶堆应当符合num[i]<=num[2*i]&&num[i]<=num[2*i+1],即结点的值要小于左右结点值(大顶堆结点值要大于左右结点值),所以要进行交换。因为我们是从数组末尾插入结点,我们的插入结点要自下往上和他的根节点进行比较,如果根节点比他大则进行交换。得到如下结果。
下标 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|
值 | 23 | 46 |
其他结点 也同样,插入之后自下向上比较替换即可。