· 题目内容
题目描述:
将一系列给定数字插入一个初始为空的最小堆 h。随后对任意给定的下标 i,打印从第 i 个结点到根结点的路径。
输入格式
每组测试第 1 行包含 2 个正整数 n 和 m (≤103),分别是插入元素的个数、以及需要打印的路径条数。下一行给出区间 [−104,104] 内的 n 个要被插入一个初始为空的小顶堆的整数。最后一行给出 m 个下标。
输出格式
对输入中给出的每个下标 i,在一行中输出从第 i 个结点到根结点的路径上的数据。数字间以 1 个空格分隔,行末不得有多余空格。
输入样例
5 3
46 23 26 24 10
5 4 3
输出样例:
24 23 10
46 23 10
26 10
· 堆
堆在物理上是一个一维数组,实际在逻辑上是一棵按层序存储的完全二叉树。这棵完全二叉树具有这样的性质:某个结点的值总是不大于或不小于其父结点的值
以大根堆为例,对于所有分支节点(除叶子结点以外的结点)T[i],满足:根结点≥左孩子结点且根结点≥右孩子结点。
完全二叉树的一些性质
对于一棵按层序、以顺序表存储的结点总数为n的完全二叉树,其结点有这样几个性质:
- 第i个结点的左右孩子分别为2i,2i+1,父节点为
(如果有的话)
-
为分支节点,其他为叶子结点。
- 除根结点外,完全二叉树的任一结点要么有右兄弟,要么是最后一个结点。
所以对于大根堆的第i个结点,总有T[i]≥T[2i]且T[i]≥T[2i+1](小根堆条件改为≤即可)
· 算法实现
建堆
首先把所有数据都存到一个顺序表中,自底向上地依次检查所有分支节点是否满足堆的要求。若不满足堆的要求,调整该节点与左右孩子中较大者互换。调整后,可能导致子树不满足堆的要求,需要继续向下调整。(建堆的代码需要理解,每一步都很巧妙)
typedef int* Heap;
typedef int HeapNode;
/* 建堆 */
// 注:这里为了下标和位序统一,h[0]不用来存储堆的元素,
// 注2:根据题目要求应使用插入法建堆
/*插入法建堆*/
Heap buildHeap_Insert(int sz) {
Heap res = malloc(sizeof(HeapNode) * sz+1);
for(int i=1;i<=sz;i++) {
// 读取并插入一个元素
int e;
scanf("%d",&e);
res[i] = e;
// 将新插入后的堆调整为小根堆
int k=i;
// 不断与父节点比较,若小于父节点,上浮
while(k>1 && res[k] < res[k / 2]) {
int tmp = res[k];
res[k] = res[k / 2];
res[k / 2] = tmp;
k/=2;
}// while
}// for
return res;
}
int main() {
int HeapSize,pathNum;
scanf("%d %d",&HeapSize,&pathNum);
if(HeapSize==0) return 0;
Heap h = buildHeap_Insert(HeapSize);
while(pathNum--) {
HeapNode n;
scanf("%d",&n);
while(n) {
if(n==1) printf("%d",h[n]);
else printf("%d ",h[n]);
n/=2;
}
if(pathNum!=0) putchar('\n');
}
}
输出
找路径输出就比较简单了,由完全二叉树性质:父节点位序=孩子结点位序/2(向下取整),根据给出的要输出路径结点下标,每次除以2直到下标为1即可