今天来写一下先序遍历,中序遍历和后序遍历的相互求法。
首先我们要知道先序遍历,中序遍历和后序遍历各自遍历的顺序:
1.先序遍历(根左右的顺序):①访问根节点
②先序遍历左子树
③先序遍历右子树
2.中序遍历(左根右的顺序):①中序遍历左子树
②访问根节点
③中序遍历右子树
3.中序遍历(左右根的顺序):①后序遍历左子树
②后序遍历右子树
③访问根节点
我们要知道,根据先序遍历和后序遍历无法唯一确定一棵二叉树,所以无法根据先序遍历和后序遍历输出中序遍历。这是因为先序和后序遍历不能确定根节点的具体位置。例如,考虑两棵形状不同但具有相同先序和后序遍历的树:
A A
/ \
B B
这两棵树的先序遍历都是AB,后序遍历都是BA,但它们的中序遍历分别是BA和AB,所以我们不能仅根据先序和后序遍历来确定中序遍历。
然而,如果我们知道这棵树的叶子节点或者所有的内部节点都有两个子节点(即满二叉树),那么我们可以使用先序和后序遍历来重建这棵树。但在一般情况下,我们需要中序遍历与先序或后序遍历配合才能唯一确定一棵二叉树。所以在这里我们只进行用中序遍历和后序遍历求先序遍历以及用先序遍历和中序遍历求后序遍历的讨论。
一、已知中序遍历和后序遍历,求先序遍历
例:
中序遍历:4, 2, 5, 1, 3
后序遍历:4, 5, 2, 3, 1
首先要知道,在后序遍历中,最后一个元素总是根节点。所以在这个例子中,1就是根节点。
然后,在中序遍历结果中找到 1,它将中序遍历结果分为两部分:左子树的中序遍历结果4, 2, 5和右子树的中序遍历结果 3。
接下来,需要确定左子树和右子树的后序遍历结果。在后序遍历中,左子树的元素总是在右子树元素的前面。所以在这个例子中,左子树的后序遍历结果是 4, 5, 2,右子树的后序遍历结果是 3。
现在,可以递归地对左子树和右子树进行相同的操作。对于左子树,可以看到2是根节点,4是左子节点,5是右子节点。对于右子树,3就是一个叶节点。
所以,这个二叉树的先序遍历结果就是:1(根节点),2(左子树的根节点),4(左子树的左子节点),5(左子树的右子节点),3(右子节点)。
可以画出该二叉树:
所以,由先序遍历的规则,可以得到先序遍历的顺序为:1,2,4,5,3
代码如下
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 定义二叉树节点结构体
typedef struct Node {
int data; // 数据域
struct Node *left; // 左指针域
struct Node *right; // 右指针域
} Node;
// 根据后序遍历和中序遍历构建二叉树,并返回根节点指针
Node *buildTree(int *postOrder, int *inOrder, int len) {
// 如果输入为空或长度为零,返回空指针
if (postOrder == NULL || inOrder == NULL || len <= 0) {
return NULL;
}
// 创建一个新的节点,将后序遍历的最后一个元素赋值给它的数据域
Node *root = (Node *)malloc(sizeof(Node));
root->data = postOrder[len - 1];
// 在中序遍历中找到根节点的位置
int pos = 0;
while (inOrder[pos] != root->data) {
pos++;
}
// 递归地调用函数,分别传入后序遍历和中序遍历的左子树部分和右子树部分
root->left = buildTree(postOrder, inOrder, pos);
root->right = buildTree(postOrder + pos, inOrder + pos + 1, len - pos - 1);
// 返回根节点指针
return root;
}
// 先序遍历打印二叉树
void preOrder(Node *root) {
// 如果节点为空,返回
if (root == NULL) {
return;
}
// 打印当前节点的数据域
printf("%d ", root->data);
// 先序遍历左子树
preOrder(root->left);
// 先序遍历右子树
preOrder(root->right);
}
// 测试代码
int main() {
// 定义后序遍历和中序遍历的整数数组
int postOrder[] = {4, 5, 2, 3, 1};
int inOrder[] = {4, 2, 5, 1, 3};
// 构建二叉树,并得到根节点指针
Node *root = buildTree(postOrder, inOrder, sizeof(postOrder) / sizeof(int));
// 先序遍历打印二叉树
printf("先序遍历结果为:\n");
preOrder(root);
printf("\n");
// 释放内存
free(root);
return 0;
}
运行结果如下
先序遍历结果为:
1 2 4 5 3
二、已知先序遍历和中序遍历,求后序遍历
例:
先序遍历:1, 2, 4, 5, 3
中序遍历:4, 2, 5, 1, 3
首先要知道,在先序遍历中,第一个元素总是根节点。所以在这个例子中,1就是根节点。
然后,在中序遍历结果中找到 1,它将中序遍历结果分为两部分:左子树的中序遍历结果 4, 2, 5和右子树的中序遍历结果 3。
接下来,需要确定左子树和右子树的先序遍历结果。在先序遍历中,左子树的元素总是在右子树元素的前面。所以在这个例子中,左子树的先序遍历结果是 2, 4, 5 ,右子树的先序遍历结果是 3 。
现在,可以递归地对左子树和右子树进行相同的操作。对于左子树,可以看到2是根节点,4是左子节点,5是右子节点。对于右子树,3就是一个叶节点。
所以,这个二叉树的后序遍历结果就是:4(左子树的左子节点),5(左子树的右子节点),2(左子树的根节点),3(右子节点),1(根节点)。
可以画出该二叉树:
所以,由后序遍历的规则,可以得到后序遍历的顺序为:4,5,2,3,1
代码如下
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 定义二叉树节点结构体
typedef struct Node {
int data; // 数据域
struct Node *left; // 左指针域
struct Node *right; // 右指针域
} Node;
// 根据先序遍历和中序遍历构建二叉树,并返回根节点指针
Node *buildTree(int *preOrder, int *inOrder, int len) {
// 如果输入为空或长度为零,返回空指针
if (preOrder == NULL || inOrder == NULL || len <= 0) {
return NULL;
}
// 创建一个新的节点,将先序遍历的第一个元素赋值给它的数据域
Node *root = (Node *)malloc(sizeof(Node));
root->data = preOrder[0];
// 在中序遍历中找到根节点的位置
int pos = 0;
while (inOrder[pos] != root->data) {
pos++;
}
// 递归地调用函数,分别传入先序遍历和中序遍历的左子树部分和右子树部分
root->left = buildTree(preOrder + 1, inOrder, pos);
root->right = buildTree(preOrder + pos + 1, inOrder + pos + 1, len - pos - 1);
// 返回根节点指针
return root;
}
// 后序遍历打印二叉树
void postOrder(Node *root) {
// 如果节点为空,返回
if (root == NULL) {
return;
}
// 后序遍历左子树
postOrder(root->left);
// 后序遍历右子树
postOrder(root->right);
// 打印当前节点的数据域
printf("%d ", root->data);
}
// 测试代码
int main() {
// 定义先序遍历和中序遍历的整数数组
int preOrder[] = {1, 2, 4, 5, 3};
int inOrder[] = {4, 2, 5, 1, 3};
// 构建二叉树,并得到根节点指针
Node *root = buildTree(preOrder, inOrder, sizeof(preOrder) / sizeof(int));
// 后序遍历打印二叉树
printf("后序遍历结果为:\n");
postOrder(root);
printf("\n");
// 释放内存
free(root);
return 0;
}
运行结果如下
后序遍历结果为:
4 5 2 3 1
这就是我对已知中序遍历和后序遍历求先序遍历与已知先序遍历和中序遍历求后序遍历的理解,欢迎大家指出我的错误,以便我后续进行改正。