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年冬季考试

PAT(甲级)2018年冬季考试 7-1 Google Recruitment

PAT(甲级)2018年冬季考试 7-2 Decode Registration Card of PAT

PAT(甲级)2018年冬季考试 7-3 Vertex Coloring

PAT2021年冬季考试甲级,摸鱼游记92分

PAT2021年冬季考试甲级,摸鱼游记92分