题目大意:给出一棵完全二叉树,判断它是否是最大堆或者最小堆。并输出从根节点到所有叶子节点的路径。要求位于左子树的比右子树的叶子节点先输出。
因为是完全二叉树,从下标为0开始计,根节点下标为0,下标为 i 的左子树下标为 2*i + 1,右子树下标为 2 * i +2。最后一个非叶子节点的下标是(N - 2) / 2, N是节点数。输出某一个叶子节点的路径是容易实现的,不断向前递推直到根节点为止。问题主要是如何保证右子树的叶子节点优先输出。这里需要分两种情况来考虑:
记最左边的节点下标为 mostLeft,它是从根节点0开始循环 2 * i + 1得到的最后一个小于N的下标。最右边的节点下标为 mostRight,它是从根节点0开始循环 2 * i + 2得到的最后一个小于N的下标。分成的两种情况就是,mostRight是否等于N-1。
① mostRight == N-1,那么所有的叶子节点都在同一层上,只需要从mostRight开始,遍历到 (N-2)/2+1为止,依次输出路径即可。
② mostRight < N-1,那么还有叶子节点在下一层,就像题目中给的节点数为8 的情况,那么需要先输出从 mostRight 到 (N-2) / 2 + 1的路径,再输出从 N - 1 到 mostLeft 的路径。
AC代码:
#include <vector>
#include <algorithm>
#include <cstdio>
using namespace std;
void printPath(int node, vector<int> &tree)
{
vector<int> path;
path.push_back(node);
while(node > 0)
{
node = (node - 1) / 2;
path.push_back(node);
}
for (int i = path.size() - 1; i >= 0; --i)
{
printf("%d", tree[path[i]]);
if(i > 0) printf(" ");
else printf("\n");
}
}
bool judgeMaxHeap(vector<int> &tree)
{
for (int i = 0; i <= (tree.size() - 1 - 1) / 2; ++i)
{
if(tree[i] < tree[2 * i + 1] || tree[i] < tree[min(2 * i + 2, (int)tree.size() - 1)])
return false;
}
return true;
}
bool judgeMinHeap(vector<int> &tree)
{
for (int i = 0; i <= (tree.size() - 1 - 1) / 2; ++i)
{
if(tree[i] > tree[2 * i + 1] || tree[i] > tree[min(2 * i + 2, (int)tree.size() - 1)])
return false;
}
return true;
}
int main()
{
int N;
scanf("%d", &N);
vector<int> tree(N);
for (int i = 0; i < N; ++i)
{
scanf("%d", &tree[i]);
}
int mostLeft = 0;
while(mostLeft < N)
{
if(mostLeft * 2 + 1 >= N) break;
mostLeft = mostLeft * 2 + 1;
}
int mostRight = 0;
while(mostRight < N)
{
if(mostRight * 2 + 2 >= N) break;
mostRight = mostRight * 2 + 2;
}
if(mostRight == N - 1)
{
for (int i = mostRight; i > (N - 1) / 2; --i)
printPath(i, tree);
}
else
{
for (int i = mostRight; i > (N - 1) / 2; --i)
printPath(i, tree);
for (int i = N - 1; i >= mostLeft; --i)
printPath(i, tree);
}
if(judgeMaxHeap(tree)) printf("Max Heap\n");
else if(judgeMinHeap(tree)) printf("Min Heap\n");
else printf("Not Heap\n");
return 0;
}