堆中的路径(浙大PTA数据结构与算法题目集-编程题7-5)

· 题目内容

题目描述:

将一系列给定数字插入一个初始为空的最小堆 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,父节点为 $\lfloor \frac{i}{2} \rfloor$(如果有的话)
  •  [1, \lfloor \frac{n}{2} \rfloor] 为分支节点,其他为叶子结点。
  • 除根结点外,完全二叉树的任一结点要么有右兄弟,要么是最后一个结点。

所以对于大根堆的第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即可

完整代码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值