03-树1 树的同构 (25分)
给定两棵树T1和T2。如果T1可以通过若干次左右孩子互换就变成T2,则我们称两棵树是“同构”的。例如图1给出的两棵树就是同构的,因为我们把其中一棵树的结点A、B、G的左右孩子互换后,就得到另外一棵树。而图2就不是同构的。
现给定两棵树,请你判断它们是否是同构的。
输入格式:
输入给出2棵二叉树树的信息。对于每棵树,首先在一行中给出一个非负整数N (≤10),即该树的结点数(此时假设结点从0到N−1编号);随后N行,第i行对应编号第i个结点,给出该结点中存储的1个英文大写字母、其左孩子结点的编号、右孩子结点的编号。如果孩子结点为空,则在相应位置上给出“-”。给出的数据间用一个空格分隔。注意:题目保证每个结点中存储的字母是不同的。
输出格式:
如果两棵树是同构的,输出“Yes”,否则输出“No”。
输入样例1(对应图1):
8
A 1 2
B 3 4
C 5 -
D - -
E 6 -
G 7 -
F - -
H - -
8
G - 4
B 7 6
F - -
A 5 1
H - -
C 0 -
D - -
E 2 -
输出样例1:
Yes
输入样例2(对应图2):
8
B 5 7
F - -
A 0 3
C 6 -
H - -
D - -
G 4 -
E 1 -
8
D 6 -
B 5 -
E - -
H - -
C 0 2
G - 3
F - -
A 1 4
输出样例2:
No
代码长度限制
16 KB
时间限制
400 ms
内存限制
64 MB
主要思路:
先要理解这句话:对于每棵树,首先在一行中给出一个非负整数N (≤10),即该树的结点数(此时假设结点从0到N−1编号);随后N行,第i行对应编号第i个结点
这句话的意思是:第i行节点编号是i,但建树时可不一定按0~n-1从左到右从上到下来建,而要根据节点关系来
难点主要分为两大部分:
(一)建树:
用一个大数组来建树,大数组的下标i对应节点i,在过程中找到根节点,扫描树时利用根节点先序遍历
(1)先初始化大数组,将每个节点存储的character设置为EMPTY,状态设施为true
(2)再遍历输入,其中处理后面子节点有讲究,应该先按字符读取,然后在判断,如果是数字,就改为数字后存入节点,否则说明子节点为空,并将子节点状态定义为false
(3)遍历大数组,其中节点character不为EMPTY且状态不为false的就是根节点
(二)判断:递归三部曲:
(1)参数和返回值
参数:两棵树各自根节点;返回值:以当前根节点是否满足题意
(2)结束条件:
遍历到空节点
(3)遍历顺序:
前序遍历,其中的单层递归逻辑:先判断当前两个根节点是否相等,在看各自左孩子与各自右孩子是否相等,如果相等直接分别递归,如果不相等,就将第一棵子树左孩子传入下一层递归与第二棵子树右孩子比较;第一棵子树右孩子传入下一层递归与第二棵子树左孩子比较
第一次写错误:
(1)建树时不仅要如果左右孩子是空,不仅要将左右孩子地址设置为NO,左右孩子存储的character也要设置为EMPTY
代码实现:
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#define MAX 20
#define NO -1
#define EMPTY '|'
typedef struct {
char character;
int leftAddress;
int rightAddress;
int id;
}Node;
Node tree1[MAX], tree2[MAX];
int nodeNum1, nodeNum2;
int buildTree(Node* tree, int* nodeNum) {
scanf("%d", nodeNum);
getchar(); //读取换行符
for(int i = 0; i < MAX; i++) { //初始化即将存储数的数组
tree[i].id = true; //先将每个节点的状态都定义为true,即假设都是根节点
tree[i].character = EMPTY; //将每节点存储字符位置都定义为EMPTY
}
for(int i = 0; i < *nodeNum; i++) {
char tmpLeft, tmpRight;
scanf("%c %c %c", &tree[i].character, &tmpLeft, &tmpRight);
//按字符-空格-字符-空格-字符读取
getchar();
if(tmpLeft >= '0' && tmpLeft <= '9') {
tree[i].leftAddress = tmpLeft - '0';
// printf("left = %d\n", tree[i].leftAddress);
int left = tree[i].leftAddress;
tree[left].id = false;
}
else {
tree[i].leftAddress = NO;
tree[tree[i].leftAddress].character = EMPTY;
}
if(tmpRight >= '0' && tmpRight <= '9') {
tree[i].rightAddress = tmpRight - '0';
int right = tree[i].rightAddress;
tree[right].id = false;
}
else {
tree[i].rightAddress = NO;
tree[tree[i].rightAddress].character = EMPTY;
}
}
// for(int i = 0; i < MAX; i++) {
// if(tree[i].character != EMPTY) {
// printf("%c %d %d %d\n", tree[i].character, tree[i].leftAddress, tree[i].rightAddress, tree[i].id);
// }
// }
// printf("-----------------\n");
int root;
for(int i = 0; i < MAX; i++) {
if(tree[i].id && tree[i].character != EMPTY) {
root = i;
break;
}
}
return root;
}
int depth = 0;
bool judge(int root1, int root2) {
// printf("depth = %d\n", depth++);
// printf("nodeNum1 = %d nodeNum2 = %d\n", nodeNum1, nodeNum2);
if(nodeNum1 != nodeNum2) return false;
if(nodeNum1 == 0 || nodeNum2 == 0) return true;
char tree1cur = tree1[root1].character;
char tree2cur = tree2[root2].character;
// printf("tree1cur = %c tree2cur = %c\n", tree1cur, tree2cur);
if(tree1cur == EMPTY && tree2cur == EMPTY) return true;
char tree1LeftNode = tree1[tree1[root1].leftAddress].character;
char tree1RightNode = tree1[tree1[root1].rightAddress].character;
char tree2LeftNode = tree2[tree2[root2].leftAddress].character;
char tree2RightNode = tree2[tree2[root2].rightAddress].character;
bool leftSubtree;
bool rightSubtree;
if(tree1cur == tree2cur) {
// printf("tree1LeftNode = %c ", tree1LeftNode);
// printf("tree2LeftNode = %c\n", tree2LeftNode);
// printf("tree1RightNode = %c ", tree1RightNode);
// printf("tree2RightNode = %c\n", tree2RightNode);
// printf("-----------------------\n");
if(tree1LeftNode == tree2LeftNode && tree1RightNode == tree2RightNode) {
leftSubtree = judge(tree1[root1].leftAddress, tree2[root2].leftAddress);
if(!leftSubtree) return false;
rightSubtree = judge(tree1[root1].rightAddress, tree2[root2].rightAddress);
if(!rightSubtree) return false;
}
else {
leftSubtree = judge(tree1[root1].rightAddress, tree2[root2].leftAddress);
if(!leftSubtree) return false;
rightSubtree = judge(tree1[root1].leftAddress, tree2[root2].rightAddress);
if(!rightSubtree) return false;
}
return true;
}
return false;
}
int main(void) {
int root1 = buildTree(tree1, &nodeNum1);
// printf("%d\n", root1);
// printf("===================\n");
int root2 = buildTree(tree2, &nodeNum2);
// printf("%d\n", root2);
if(judge(root1, root2)) printf("Yes");
else printf("No");
return 0;
}
补充:
(1)打印每次递归输出检查的时候要以第二棵二叉树进行前序遍历,因为第一棵已经交换过左右孩子了不太好看
如下是Example1的输出:
depth = 0
tree1cur = A tree2cur = A
tree1LeftNode = B tree2LeftNode = C
tree1RightNode = C tree2RightNode = B
-----------------------
depth = 1
tree1cur = C tree2cur = C
tree1LeftNode = G tree2LeftNode = G
tree1RightNode = | tree2RightNode = |
-----------------------
depth = 2
tree1cur = G tree2cur = G
tree1LeftNode = H tree2LeftNode = |
tree1RightNode = | tree2RightNode = H
-----------------------
depth = 3
tree1cur = | tree2cur = | //从这里看第二棵二叉树就很明显,左子树遍历完了,在遍历右子树
depth = 4
tree1cur = H tree2cur = H
tree1LeftNode = | tree2LeftNode = |
tree1RightNode = | tree2RightNode = |
-----------------------
depth = 5
tree1cur = | tree2cur = |
depth = 6
tree1cur = | tree2cur = |
depth = 7
tree1cur = | tree2cur = |
depth = 8
tree1cur = B tree2cur = B //到这里可以看出根节点左子树遍历完开始遍历右子树了
tree1LeftNode = D tree2LeftNode = E
tree1RightNode = E tree2RightNode = D
-----------------------
depth = 9
tree1cur = E tree2cur = E
tree1LeftNode = F tree2LeftNode = F
tree1RightNode = | tree2RightNode = |
-----------------------
depth = 10
tree1cur = F tree2cur = F
tree1LeftNode = | tree2LeftNode = |
tree1RightNode = | tree2RightNode = |
-----------------------
depth = 11
tree1cur = | tree2cur = |
depth = 12
tree1cur = | tree2cur = |
depth = 13
tree1cur = | tree2cur = |
depth = 14
tree1cur = D tree2cur = D
tree1LeftNode = | tree2LeftNode = |
tree1RightNode = | tree2RightNode = |
-----------------------
depth = 15
tree1cur = | tree2cur = |
depth = 16
tree1cur = | tree2cur = |
Yes
题目详情:03-树2 List Leaves
Given a tree, you are supposed to list all the leaves in the order of top down, and left to right.
Input Specification:
Each input file contains one test case. For each case, the first line gives a positive integer N (≤10) which is the total number of nodes in the tree -- and hence the nodes are numbered from 0 to N−1. Then N lines follow, each corresponds to a node, and gives the indices of the left and right children of the node. If the child does not exist, a "-" will be put at the position. Any pair of children are separated by a space.
Output Specification:
For each test case, print in one line all the leaves' indices in the order of top down, and left to right. There must be exactly one space between any adjacent numbers, and no extra space at the end of the line.
Sample Input:
8
1 -
- -
0 -
2 7
- -
- -
5 -
4 6
Sample Output:
4 1 5
代码长度限制
16 KB
时间限制
400 ms
内存限制
64 MB
简单翻译:
就是给一把二叉树节点,其中要按从左往右,从上往下顺序依次输出所有叶子节点
主要思路:
这题主要思路其实还好,也就是分为两部分,一部分是建树,与上一题一样,另一部分就是层序遍历,不过这里的队列数据结构是用c语言循环队列实现的,如果是特别要求逐层操作,往往考虑层序遍历,层序遍历顺序:
(1)从队列中取出一个元素
(2)访问该元素所指节点
(3)若该元素所指节点左右孩子非空,则将其左右孩子顺序入队
代码实现:
#include <stdio.h>
#include <stdbool.h>
#define MAX 11
#define EMPTY '|'
#define NO -1
#define MAX_QUEUE_SIZE 10
typedef struct { //实现循环队列
int data[MAX_QUEUE_SIZE];
int front;
int rear;
int count;
} CircularQueue;
void initialize_queue(CircularQueue* queue) {
queue->front = 0;
queue->rear = -1;
queue->count = 0;
}
int is_full(CircularQueue* queue) {
return (queue->count == MAX_QUEUE_SIZE);
}
int is_empty(CircularQueue* queue) {
return (queue->count == 0);
}
int enqueue(CircularQueue* queue, int data) {
if(is_full(queue)) {
return 0;
}
queue->rear = (queue->rear + 1) % MAX_QUEUE_SIZE;
queue->data[queue->rear] = data;
queue->count++;
return 1;
}
int dequeue(CircularQueue* queue) {
if(is_empty(queue)) {
return -1;
}
int data = queue->data[queue->front];
queue->front = (queue->front + 1) % MAX_QUEUE_SIZE;
queue->count--;
return data;
}
/*----------------------------------------------------------*/
int count = 0;
typedef struct {
int leftChild;
int rightChild;
bool isRoot;
}Node;
Node tree[MAX];
int buildTree() {
for(int i = 0; i < MAX; i++) {
tree[i].isRoot = true;
}
int nodeNum;
scanf("%d", &nodeNum);
getchar();
for(int i = 0; i < nodeNum; i++) {
char tmpLeft, tmpRight;
scanf("%c %c", &tmpLeft, &tmpRight);
getchar();
if(tmpLeft >= '0' && tmpLeft <= '9') {
tree[i].leftChild = tmpLeft - '0';
tree[tree[i].leftChild].isRoot = false;
} else {
tree[i].leftChild = NO;
}
if(tmpRight >= '0' && tmpRight <= '9') {
tree[i].rightChild = tmpRight - '0';
tree[tree[i].rightChild].isRoot = false;
} else {
tree[i].rightChild = NO;
}
if(tree[i].leftChild == NO && tree[i].rightChild == NO) count++;
}
int root = 0;
for(int i = 0; i < nodeNum; i++) {
if(tree[i].isRoot) {
root = i;
break;
}
}
return root;
}
void printLeave(int root) {
if(root < 0) return;
CircularQueue myQueue;
initialize_queue(&myQueue);
enqueue(&myQueue, root);
while(!is_empty(&myQueue)) {
int head = dequeue(&myQueue);
// printf("head = %d ", head);
// printf("leftChild = %d ", tree[head].leftChild);
// printf("rightChild = %d\n", tree[head].rightChild);
// printf("----------------------\n");
if(tree[head].leftChild < 0 && tree[head].rightChild < 0) {
count--;
if(count) printf("%d ", head);
else printf("%d", head);
}
if(tree[head].leftChild >= 0) enqueue(&myQueue, tree[head].leftChild);
if(tree[head].rightChild >= 0) enqueue(&myQueue, tree[head].rightChild);
}
}
int main() {
int root = buildTree();
//printf("%d", root);
int depthOrder = 1;
printLeave(root);
return 0;
}
补充:c语言实现循环队列
(1)实现循环队列原因:
一般队列会出现假溢出
(2)实现循环队列难点:
如何判断当前队列空还是满:
方法一:增设一个变量
方法二:少用一个元素空间
(3)代码实现:
#include <stdio.h>
#include <stdlib.h>
#define MAX_QUEUE_SIZE 10
typedef struct {
int data[MAX_QUEUE_SIZE];
int front;
int rear;
int count;
} CircularQueue;
void initialize_queue(CircularQueue* queue) {
queue->front = 0;
queue->rear = -1;
queue->count = 0;
}
int is_full(CircularQueue* queue) {
return (queue->count == MAX_QUEUE_SIZE);
}
int is_empty(CircularQueue* queue) {
return (queue->count == 0);
}
int enqueue(CircularQueue* queue, int data) {
if(is_full(queue)) {
return 0;
}
queue->rear = (queue->rear + 1) % MAX_QUEUE_SIZE; //因为是循环,所以不是简单加1,还要取余
queue->data[queue->rear] = data;
(queue->count)++;
return 1;
}
int dequeue(CircularQueue* queue) {
if(is_empty(queue)) {
return -1;
}
int data = queue->data[queue->front];
queue->front = (queue->front + 1) % MAX_QUEUE_SIZE;
queue->count--;
return data;
}
int main() {
CircularQueue queue;
initialize_queue(&queue);
enqueue(&queue, 10);
enqueue(&queue, 20);
enqueue(&queue, 30);
enqueue(&queue, 40);
enqueue(&queue, 50);
printf("Queue elements: ");
while(!is_empty(&queue)) {
printf("%d ", dequeue(&queue));
}
return 0;
}
03-树3 Tree Traversals Again
An inorder binary tree traversal can be implemented in a non-recursive way with a stack. For example, suppose that when a 6-node binary tree (with the keys numbered from 1 to 6) is traversed, the stack operations are: push(1); push(2); push(3); pop(); pop(); push(4); pop(); pop(); push(5); push(6); pop(); pop(). Then a unique binary tree (shown in Figure 1) can be generated from this sequence of operations. Your task is to give the postorder traversal sequence of this tree.
Figure 1
Input Specification:
Each input file contains one test case. For each case, the first line contains a positive integer N (≤30) which is the total number of nodes in a tree (and hence the nodes are numbered from 1 to N). Then 2N lines follow, each describes a stack operation in the format: "Push X" where X is the index of the node being pushed onto the stack; or "Pop" meaning to pop one node from the stack.
Output Specification:
For each test case, print the postorder traversal sequence of the corresponding tree in one line. A solution is guaranteed to exist. All the numbers must be separated by exactly one space, and there must be no extra space at the end of the line.
Sample Input:
6
Push 1
Push 2
Push 3
Pop
Pop
Push 4
Pop
Pop
Push 5
Push 6
Pop
Pop
Sample Output:
3 4 2 6 5 1
代码长度限制
16 KB
时间限制
400 ms
内存限制
64 MB
简单翻译:
给出一棵树按一定规律给出的节点顺序,要求复建这棵树并且输出这棵树的后序遍历结果
主要思路:
关键一点在于发现题干中给出的push顺序就是原二叉树的前序遍历结果,pop出的顺序就是原二叉树的中序遍历结果
代码实现:(目前这个代码还有点小问题,不知道是切割字符串时有问题还是哪……)
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#define EMPTY '|'
#define maxNodeNum 100
/*-------构造循环队列数据结构-----------*/
#define MAX_QUEUE_SIZE 100
typedef struct {
int data[MAX_QUEUE_SIZE];
int front;
int rear;
int count;
} CircularQueue;
void initialize_queue(CircularQueue* queue) {
queue->front = 0;
queue->rear = -1;
queue->count = 0;
}
int queue_is_full(CircularQueue* queue) {
return (queue->count == MAX_QUEUE_SIZE);
}
int queue_is_empty(CircularQueue* queue) {
return (queue->count == 0);
}
int enqueue(CircularQueue* queue, int data) {
if(queue_is_full(queue)) {
return 0;
}
queue->rear = (queue->rear + 1) % MAX_QUEUE_SIZE;
queue->data[queue->rear] = data;
queue->count++;
return 1;
}
int dequeue(CircularQueue* queue) {
if(queue_is_empty(queue)) {
return -1;
}
int data = queue->data[queue->front];
queue->front = (queue->front + 1) % MAX_QUEUE_SIZE;
queue->count--;
return data;
}
/*----------------构造循环队列数据结构-------------*/
/*----------构造栈数据结构---------------*/
#define MAX_STACK_SIZE 100
typedef struct {
int data[MAX_STACK_SIZE];
int top;
} Stack;
void initialize_stack(Stack* stack) {
stack->top = -1;
}
bool stack_is_full(Stack* stack) {
return (stack->top == MAX_STACK_SIZE - 1);
}
bool stack_is_empty(Stack* stack) {
return (stack->top == -1);
}
bool stack_push(Stack* stack, int data) {
if (stack_is_full(stack)) {
return false;
}
stack->data[++stack->top] = data;
return true;
}
int stack_pop(Stack* stack) {
if (stack_is_empty(stack)) {
return -1;
}
return stack->data[stack->top--];
}
int stack_peek(Stack* stack) {
if (stack_is_empty(stack)) {
return -1;
}
return stack->data[stack->top];
}
/*-----------构造栈数据结构-----------------*/
int inOrderTraversal[maxNodeNum];
int preOrderTraversal[maxNodeNum];
int inOrderTraversalIndex = 0;
int preOrderTraversalIndex = 0;
int nodeNum;
void buildTwoTraversal() {
Stack tmpStack;
initialize_stack(&tmpStack);
for(int i = 0; i < 2 * nodeNum; i++) {
char line[7];
for(int j = 0; j < 7; j++) line[j] = EMPTY;
char ch;
int j = 0;
if(i < 2 * nodeNum - 1) { //这里比较麻烦的原因是给的输入里最后一行没有换行符
while((ch = getchar()) != '\n') {
line[j++] = ch;
}
} else if(i == 2 * nodeNum - 1) {
scanf("%7s", line);
}
if(line[5] >= '0' && line[5] <= '9') {
stack_push(&tmpStack, line[5] - '0');
preOrderTraversal[preOrderTraversalIndex++] = line[5] - '0';
} else {
int topStack = stack_pop(&tmpStack);
inOrderTraversal[inOrderTraversalIndex++] = topStack;
}
}
// printf("preOrderTraversal = ");
// for(int k = 0; k < nodeNum; k++) {
// printf("%d ", preOrderTraversal[k]);
// }
// putchar('\n');
// printf("------------------\n");
// printf("inOrderTraversal = ");
// for(int k = 0; k < nodeNum; k++) {
// printf("%d ", inOrderTraversal[k]);
// }
return;
}
CircularQueue retqueue;
void postOrder(int preStart, int preEnd, int inStart, int inEnd) {
// printf("preStart = %d preEnd = %d\n", preStart, preEnd);
// printf("inStart = %d inEnd = %d\n", inStart, inEnd);
// printf("----------------------------\n");
int preLen = preEnd - preStart + 1;
int inLen = inEnd - inStart + 1;
if(preLen < 1 || inLen < 1) return;
if(preStart < 0 || preEnd < 0 || inStart < 0 || inEnd < 0) return;
if(preStart >= nodeNum || preEnd >= nodeNum || inStart >= nodeNum || inEnd >= nodeNum) return;
if(preStart == preEnd) {
enqueue(&retqueue, preOrderTraversal[preStart]);
return;
}
int root = preOrderTraversal[preStart];
int cuttingLine;
for(int i = 0; i < nodeNum; i++) {
if(inOrderTraversal[i] == root) {
cuttingLine = i;
break;
}
}
int leftInStart = inStart;
int leftInEnd = cuttingLine - 1;
int leftSubTreeLength = leftInEnd - leftInStart + 1;
int leftPreStart = preStart + 1;
int leftPreEnd = leftPreStart + leftSubTreeLength - 1;
int rightPreStart = leftPreEnd + 1;
int rightPreEnd = preEnd;
int rightInStart = cuttingLine + 1;
int rightInEnd = inEnd;
// printf("cuttline = %d\n", cuttingLine);
// printf("leftPreStart = %d leftPreEnd = %d leftInStart = %d leftInEnd = %d\n", leftPreStart, leftPreEnd, leftInStart, leftInEnd);
// printf("rightPreStart = %d rightPreEnd = %d rightInStart = %d rightInEnd = %d\n", rightPreStart, rightPreEnd, rightInStart, rightInEnd);
// printf("==============================================\n");
postOrder(leftPreStart, leftPreEnd, leftInStart, leftInEnd); //遍历左子树
postOrder(rightPreStart, rightPreEnd, rightInStart, rightInEnd); //遍历右子树
enqueue(&retqueue, root);
}
int main() {
scanf("%d", &nodeNum);
getchar();
buildTwoTraversal();
initialize_queue(&retqueue);
postOrder(0, nodeNum - 1, 0, nodeNum - 1);
while(!queue_is_empty(&retqueue)) {
printf("%d", dequeue(&retqueue));
if(nodeNum-- > 1) printf(" ");
}
return 0;
}
补充:c语言实现堆栈 :
/*----------构造栈数据结构---------------*/
#define MAX_STACK_SIZE 30
typedef struct {
int data[MAX_STACK_SIZE];
int top;
} Stack;
void initialize_stack(Stack* stack) {
stack->top = -1;
}
bool stack_is_full(Stack* stack) {
return (stack->top == MAX_STACK_SIZE - 1);
}
bool stack_is_empty(Stack* stack) {
return (stack->top == -1);
}
bool stack_push(Stack* stack, int data) {
if (stack_is_full(stack)) {
return false;
}
stack->data[++stack->top] = data;
return true;
}
int stack_pop(Stack* stack) {
if (stack_is_empty(stack)) {
return -1;
}
return stack->data[stack->top--];
}
int stack_peek(Stack* stack) {
if (stack_is_empty(stack)) {
return -1;
}
return stack->data[stack->top];
}
/*-----------完成构造栈数据结构-----------------*/