PAT(甲级)2018年冬季考试 7-4 Heap Paths(非递归与递归解法)
Posted CSU迦叶
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了PAT(甲级)2018年冬季考试 7-4 Heap Paths(非递归与递归解法)相关的知识,希望对你有一定的参考价值。
非递归解法
1. 前置知识:完全二叉树的属性
1.1 从1开始存储,子节点的下标除以二得到的是父节点的下标
1.2 数组的存放顺序刚好是层序遍历顺序
1.3 从1开始存储,节点的下标i和结点总数n如果满足 i*2>n说明该结点是叶子结点
2. 思路:
2.1 从最右边的叶子结点开始,对每个叶子结点找到祖先,并把祖先插入向量的首个位置,直到根节点停止
最右边的叶子节点如何找?未必是下标最大的那个,是层数(使用getLayer函数)更小且下标最大的那个(体现在排序函数cmp中)
2.2 判断首个序列是非递增(最大堆)还是非递减,对于后面的每个序列都判断(使用judgeType函数),看是否和首个序列的结论一致,如果有一个不一致,说明这棵树不是堆
3. 纠偏
按照以上思路提交代码上去,得分26/30,这才反应过来自己的判断是什么堆还一个个序列去判断,也太被带沟里了。改成直接写一个判断最大堆、一个判断最小堆的函数,把整棵树当作一个整体来判断,还避免了遇到等号算谁的这种尴尬问题。于是AC。
代码
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<iostream>
#include<vector>
#include<string>
#include<queue>
#include<map>
#include<cstring>
#include<set>
using namespace std;
const int maxn = 1010;
const int SUP = 100000010;
int n,A[maxn];
struct Node{
vector<int> preVi;
int layer;
int idx;
};
Node node[maxn];
bool cmp(Node a,Node b){
return a.layer!=b.layer?a.layer<b.layer:a.idx>b.idx;
}
int getLayer(int idx){
int k;
for(k=0;k<10;k++){
if(idx>pow(2,k)-1&&idx<=pow(2,k+1)-1)break;
}
return k;
}
bool isMaxHeap(){
for(int i=1;i*2<=n;i++){//对于每一个非叶子结点,判断其和左右节点的大小关系
if(A[i]<A[i*2])return false;
if(i*2+1<=n&&A[i]<A[i*2+1])return false;
}
return true;
}
bool isMinHeap(){
for(int i=1;i*2<=n;i++){
if(A[i]>A[i*2])return false;
if(i*2+1<=n&&A[i]>A[i*2+1])return false;
}
return true;
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&A[i]);
}
int viNum = 0;//向量的个数
for(int i=n;2*i>n;i--){
vector<int> vi;
for(int j=i;j>=1;j/=2){
vi.insert(vi.begin(),A[j]);
}
node[viNum].preVi = vi;
node[viNum].idx = i;
node[viNum].layer = getLayer(i);
viNum++;
}
sort(node,node+viNum,cmp);
for(int i=0;i<viNum;i++){
for(int j=0;j<node[i].preVi.size();j++){
printf("%d",node[i].preVi[j]);
if(j!=node[i].preVi.size()-1)printf(" ");
}
printf("\\n");
}
if(isMaxHeap())printf("Max Heap\\n");
else if(isMinHeap())printf("Min Heap\\n");
else printf("Not Heap\\n");
return 0;
}
递归解法
DFS解法我参考了柳婼仙女的,她说这个是一个先序的镜像问题,也就是先序遍历是父,左子,右子,而这题是要父,右子,左子。
递归的边界是当前结点已经是叶子结点,即满足下标*2>n(这里无需判断右结点了) 。
另外一个收获是,对于最大最小堆的判断,可以用一种否定法的思想,先假设最大和最小堆都成立,对于每一个有父节点的结点,判断其和父节点的关系。代码简洁很多。
AC代码
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<iostream>
#include<queue>
#include<string>
#include<map>
#include<vector>
#include<set>
using namespace std;
const int maxn = 1001;
int n;
int A[maxn];
vector<int> vi;
void DFS(int root){
if(root*2>n){
if(root<=n){
for(int i=0;i<vi.size();i++){
printf("%d%s",vi[i],i==vi.size()-1?"\\n":" ");
}
}
}else{
vi.push_back(A[root*2+1]);
DFS(root*2+1);
vi.pop_back();
vi.push_back(A[root*2]);
DFS(root*2);
vi.pop_back();
}
}
int main(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>A[i];
}
bool isMaxHeap = true,isMinHeap = true;
vi.push_back(A[1]);
DFS(1);
for(int i=2;i<=n;i++){
if(A[i]>A[i/2])isMaxHeap = false;
if(A[i]<A[i/2])isMinHeap = false;
}
if(isMaxHeap)printf("Max Heap\\n");
else{
if(isMinHeap)printf("Min Heap\\n");
else printf("Not Heap\\n");
}
return 0;
}
以上是关于PAT(甲级)2018年冬季考试 7-4 Heap Paths(非递归与递归解法)的主要内容,如果未能解决你的问题,请参考以下文章
PAT(甲级)2018年冬季考试 7-1 Google Recruitment
PAT(甲级)2018年冬季考试 7-2 Decode Registration Card of PAT