将一系列给定数字插入一个初始为空的小顶堆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
最小堆的基本操作,可以用数组就直接实现,在这里为为了复习直接用了完整的方式。
但是在这过程中发现一个现象,题目是让一个一个把输入插入到最小堆中。
而我是直接先把所有数据按输入顺序放入数组里,然后再重新调整各节点满足最小堆。结果发现这两种方式形成的最小堆有结构的细微差异,叶子节点的位置会有所不同,这样再输出路径就会出现与题目不符合的情况。因此还是要按照题目的意思来。
//注意:这个方法与原题答案顺序不同
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define MAXDATA -10001 //岗哨
typedef struct Hnode * Heap;
struct Hnode{
int *data;
int size;
int capacity;
};
typedef Heap MINheap;
MINheap creatMinheap(){ //最小堆节点的创建
MINheap h = (MINheap)malloc(sizeof(struct Hnode));
h->data = (int*)malloc(sizeof(int)*1001);
h->size = 0;
h->capacity = 1000;
h->data[0] = MAXDATA;
return h;
}
void percDown(MINheap h, int p){//调整节点
int head = h->data[p];
int parent,child;
for (parent = p;parent*2 <= h->size;parent = child){
child = parent*2;
if ((child!=h->size)&&(h->data[child]>h->data[child+1])){
child++;
}
if (h->data[child]>=head) break;
else h->data[parent] = h->data[child];
}
h->data[parent] = head;
}
MINheap buildMinHeap(MINheap h){
int i;
for (i=h->size/2; i>0; i--){
percDown(h,i);
}
return h;
}
MINheap readMinHeap(MINheap h ,int N){//按顺序读取数
int x;
for (int i = 1; i <= N; ++i){
scanf("%d",&x);
getchar();
h->data[i] = x;
h->size++;
}
//读取完所有数
//最小堆调整
buildMinHeap(h);//读完后进行调整以满足最小堆条件
return h;
}
void printLoad(MINheap h, int i){//输出路径
for (;i>0;i=i/2){
printf("%d", h->data[i]);
if (i!=1) printf(" ");
}
printf("\n");
}
int main(){
int N,M;
scanf("%d %d",&N,&M);
printf("%d %d\n",N,M);
MINheap h = creatMinheap();
h = readMinHeap(h,N);//读取
h = buildMinHeap(h);//调整
int p;
while(M--){
scanf("%d",&p);
getchar();
printLoad(h,p);//输出
}
return 0;
}
正确的答案:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdbool.h>
#define MINDATA -10001;//岗哨
int a[1001],size;//size是当前数组里的个数
void insert(int x){//最小堆的插入
int i;
if (size == 1000) return;
//这里注意向上比较的时候一定要和插入值x比较而不是a[i]比较
for ( i = ++size; a[i/2]>x; i=i/2){
// i=++size size的值增加 同时把增加后的值给了变量i
a[i] = a[i/2];
}
a[i] = x;
}
int main(){
int N,M;
a[0] = MINDATA;//岗哨赋值
scanf("%d %d",&N,&M);
int x;
for (int i = 1; i <= N; ++i){//插入
scanf("%d",&x);
insert(x);
}
int p;
for (int i = 0; i < M; ++i){
scanf("%d",&p);
printf("%d", a[p]);//为了格式 先输出一个数
for (int i = p/2; i > 0; i=i/2){
printf(" %d",a[i]);
}
printf("\n");
}
return 0;
}