我的PAT-ADVANCED代码仓
https://github.com/617076674/PAT-ADVANCED
原题链接
https://pintia.cn/problem-sets/994805342720868352/problems/994805359372255232
题目描述
题目翻译
1110 完全二叉树
给定一棵树,你需要判断其是否是一棵完全二叉树。
输入格式:
每个输入文件包含一个测试用例。对每个测试用例,第一行给出一个正整数N(<= 20),代表树的总节点数——节点编号为0 ~ N - 1。接下来的N行,每行对应一个节点,给出其左孩子和右孩子的信息。如果某个孩子不存在,则在相应位置输出"-"。每行的2个孩子节点由一个空格分隔。
输出格式:
对每个测试用例,如果这棵树是一棵完全二叉树,输出“YES”,以及这棵树最后一个节点的索引;如果不是一棵完全二叉树,则输出“NO”,以及这棵树的根结点的索引。单词和数字之间必须由一个空格分隔。
输入样例1:
9
7 8
- -
- -
- -
0 1
2 3
4 5
- -
- -
输出样例1:
YES 8
输入样例2:
8
- -
4 5
0 6
- -
2 3
- 7
- -
- -
输出样例2:
NO 1
知识点
树的静态表示法
二叉树的层序遍历
二叉树的中序遍历
数组形式存储完全二叉树
思路
层序遍历和中序遍历可以完全确定一棵二叉树
对于一棵完全二叉树,其可以用数组来存储,其存储顺序恰好是该树的层序遍历结果。
(1)我们首先对原树进行层序遍历得到其层序遍历结果。
(2)再对原树进行中序遍历得到其中序遍历结果。
(3)对层序遍历结果,将其当作是用数组来存储的完全二叉树,中序遍历之。
(4)比较(2)、(3)两步得到的中序遍历结果,如果相同,则说明原二叉树的确是一棵完全二叉树;否则,原二叉树不是一棵完全二叉树。
注意点
必须采用中序遍历来校对,因为只有层序遍历和中序遍历才可以完全确定一棵二叉树,层序遍历和前序遍历或后序遍历的组合均不能完全确定一棵二叉树。一开始我使用了前序遍历结果来校对,无法通过测试点7,用后序遍历校对应该也会出现类似的问题。
复杂度分析
时间复杂度和空间复杂度均是O(N)。
C++代码
#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
struct node{ //节点结构体,存储其左孩子和右孩子
int lchild, rchild;
};
node Node[20]; //以静态数组的形式存储树的节点信息
vector<int> levelOrder; //原树的层序遍历结果
vector<int> inOrder; //原树的中序遍历结果
vector<int> checkInOrder; //假设是一棵完全二叉树,以数组形式存储(节点编号从0开始),得到的中序遍历结果
int changeToNum(char* input); //处理输入,将输入的编号转换为int型
void levelOrderTraversal(int head); //层序遍历原二叉树
void inOrderTraversal(int head); //中序遍历原二叉树
void checkInOrderTraversal(int index); //假设是一棵完全二叉树,以数组形式存储(节点编号从0开始),中序遍历之
int main(){
int N;
scanf("%d", &N); //读取节点个数信息
char left[3], right[3]; //题目保证节点个数小于等于20,因此字符数不超过2,可以用大小为3的字符数组来保存
bool flag[N]; //标记某节点是否出现在某个节点的孩子节点中
fill(flag, flag + N, true); //初始化为true
for(int i = 0; i < N; i++){
scanf("%s %s", left, right);
if(strcmp(left, "-") == 0){ //如果是"-",该孩子节点记为-1
Node[i].lchild = -1;
}else{
int num = changeToNum(left);
Node[i].lchild = num;
flag[num] = false; //标记编号为num的节点曾经是某个节点的孩子节点
}
if(strcmp(right, "-") == 0){ //如果是"-",该孩子节点记为-1
Node[i].rchild = -1;
}else{
int num = changeToNum(right);
Node[i].rchild = num;
flag[num] = false; //标记编号为num的节点曾经是某个节点的孩子节点
}
}
int head = -1; //寻找头节点
for(int i = 0; i < N; i++){
if(flag[i]){ //头节点一定不会出现在某个节点的孩子节点中
head = i;
}
}
levelOrderTraversal(head); //层序遍历原二叉树
inOrderTraversal(head); //中序遍历原二叉树
checkInOrderTraversal(0); //假设是一棵完全二叉树,以数组形式存储(节点编号从0开始),中序遍历之
if(inOrder == checkInOrder){ //如果中序遍历原二叉树的结果和以数组形式存储后的中序遍历结果相同,说明是一棵完全二叉树
printf("YES %d\n", levelOrder[levelOrder.size() - 1]);
}else{ //否则,不是一棵完全二叉树
printf("NO %d\n", head);
}
return 0;
}
int changeToNum(char* input){
int result = 0;
for(int i = 0; i < strlen(input); i++){
result = result * 10 + input[i] - '0';
}
return result;
}
void levelOrderTraversal(int head){
queue<int> q;
q.push(head);
while(!q.empty()){
int now = q.front();
levelOrder.push_back(now);
q.pop();
if(Node[now].lchild != -1){ //其左孩子不为-1,则左孩子入队
q.push(Node[now].lchild);
}
if(Node[now].rchild != -1){ //其右孩子不为-1,则右孩子入队
q.push(Node[now].rchild);
}
}
}
void inOrderTraversal(int head){
if(head == -1){ //递归终止条件
return;
}
inOrderTraversal(Node[head].lchild);
inOrder.push_back(head);
inOrderTraversal(Node[head].rchild);
}
void checkInOrderTraversal(int index){
if(index >= levelOrder.size()){
return;
}
checkInOrderTraversal(2 * index + 1); //其左孩子编号为2 * index + 1
checkInOrder.push_back(levelOrder[index]);
checkInOrderTraversal(2 * index + 2); //其右孩子编号为2 * index + 2
}
C++解题报告