持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第23天,点击查看活动详情
手把手教学考研大纲范围内树定义,遍历,Huffman,并查集 22考研大纲数据结构要求的是C/C++,笔者以前使用的都是Java,对于C++还很欠缺, 如有什么建议或者不足欢迎大佬评论区或者私信指出 初心是用最简单的语言描述数据结构
Talk is cheap. Show me the code. 理论到处都有,代码加例题自己练习才能真的学会
二叉树根据前序遍历,中序遍历构建二叉树,并输出后序遍历
前序遍历:相对子树的根结点在相对左右子结点靠前的位置,相对根结点都靠前
中序遍历:相对根结点的位置,左面是当前根结点左子树的结点,右面是当前根结点的右子树的结点
思路:
从前序遍历前面找到根结点,
在中序遍历找到根结点,根结点左面就是根结点左子树的结点,右面就是根结点右子树的结点
然后继续在前序遍历找下一个根结点(左子树的根结点),在上面的左子树的结点中找左子树的根结点,
继续这种循环,直到左子树的结点为空,返回上一层
如果上面左子树不存在,那么左子树的结点也是不存在的,直接返回上一层了
右子树也是,在前序遍历找下一个根结点(刚才的左结点都找完了现在才会是右结点),在上面右子树结点找右子树的根结点
继续循环
思路简化:
前序遍历是前面的都是根结点,前序遍历是根结点的顺序排列
根据前序遍历的根结点,在中序遍历找到根结点位置,
中序遍历中:根结点左面是根结点的左子树的结点,根结点的右面是根结点的右子树的结点
继续找下一个根结点,在左子树结点里找新的根结点,
(如果左子树结点不存在,那么新的根结点就是原根的右结点,就是在右子树结点里找新的根结点)
如果左子树结点或者右子树结点不存在返回上一级
不断循环这个过程
思路图解:
例子:
1
/ \
2 3
/ \ / \
4 5 6 7
前序遍历的结果是: [1,2,4,5,3,6,7]
中序遍历的结果是: [4,2,5,1,6,3,7]
先从前序遍历中找到根结点,在中序遍历找到根结点,
根结点左面是左子树结点,根结点右面是右子树结点
然后不断的循环这个操作,在先序遍历中找下一个根结点……
思路动图:
代码+注释
//根据前序遍历和中序遍历构建二叉树,求出后序遍历
#include "iostream"
#include "math.h"
#include "queue"
#define MAXSize 100
using namespace std;
int pre[MAXSize];
int mid[MAXSize];
typedef struct TreeNode{
int data;
struct TreeNode *left;
struct TreeNode *right;
} TreeNode, *Tree;
//创建二叉树 (!!!范围包括起始点不包括终止点,含左不含右!!!)
Tree createTree(int preStart, int preEnd, int midStart, int midEnd) {//前序队列从preStart到preEnd 对应着 中序遍历midStart到midEnd
if (preStart >= preEnd || midStart >= midEnd) { //如果起始位置大于等于终止位置,则不存在当前点(前序遍历和中序遍历都一样)
return NULL;
}
Tree root = new TreeNode; //创建当前结点,preStart为根结点(前序遍历就是先访问根结点)
root->data = pre[preStart];
int i;
for (i = midStart; i < midEnd; ++i) { //在中序遍历中找当前结点的根结点
if (mid[i] == pre[preStart]) {
break;
}
}
// i 是当前根结点在中序遍历的下标, i以左就是根的左子树的结点,i以右就是根的右子树的结点
// i-midStart 就是 当前根的左子树的结点数量
//当前树的结点范围:根结点+左子树结点+右子树结点 分左子树和右子树一定要记得这个关系!!!
//左子树的前序遍历的范围:当前前序遍历的 起始点+1 开始向后找 i-midStart个值
// (左子点的范围就是,起始点+1 向后找左子点数量个数)
// 起始点+1 开始是因为当前根结点就是起始点,已经用到了
//左子树的中序遍历范围: midStart 到 i
//(中序遍历的起始点 到 根结点的位置)
root->left = createTree(preStart + 1,preStart + 1 + i - midStart, midStart, i);
//右子树的前序遍历范围:从左子树前序范围终止点 到 当前树的前序范围终止点
//(根据前序遍历的特点(根结点在第一位),当前树子结点范围,除去左子树结点范围,剩下的就是右子树的结点范围)
//右子树的中序遍历范围: i+1 到 当前树中序遍历的终止点
//(i为根结点,i以右是右子树的结点,一直到中序遍历的终止点)
root->right = createTree(preStart + 1 + i - midStart,preEnd, i + 1, midEnd);
return root;
}
//后序遍历
void preOrderPrintTree (Tree tree) {
if (tree == NULL) return;
preOrderPrintTree(tree->left);
preOrderPrintTree(tree->right);
cout << tree->data << " ";
}
//返回二叉树的深度(递归)
int Depth (Tree tree) {
if (tree == NULL) {
return 0;
}
return max(Depth(tree->left), Depth(tree->right)) + 1; //找左或右结点的最大深度 + 1(加一是加上当前这一层)
//不断循环这种操作
}
//格式化输出二叉树(直接看顺序存储的注释就可以,格式化输出就是找二叉树的规律)
// 顺序存储的注释: https://blog.csdn.net/weixin_46285416/article/details/120931768#t14
void treePrint(Tree tree) { //与顺序打印二叉树基本相似(如果结点为空的时候,要创建一个0结点,给0结点的左右结点都附空)
cout << "打印二叉树:\n";
int depth = Depth(tree);
queue<Tree> q;
q.push(tree);
for (int i = 0; i < depth; i++) {
int space = 0;
for (int j = 0; j < depth - 1 - i; j++) {
space = space * 2 + 1;
}
for (int j = 0; j < space; j++) {
cout << " ";
}
for (int j = 0; j < pow(2, i); j++) {
cout << q.front()->data;
if (q.front()->left != NULL) {
q.push(q.front()->left);
} else {
TreeNode *t = new TreeNode;
t->data = 0;
t->left = NULL;
t->right = NULL;
q.push(t);
}
if (q.front()->right != NULL) {
q.push(q.front()->right);
} else {
TreeNode *t = new TreeNode;
t->data = 0;
t->left = NULL;
t->right = NULL;
q.push(t);
}
q.pop();
for (int k = 0; k < space * 2 + 1; k++) {
cout << " ";
}
}
cout << "\n";
}
cout << "\n";
}
int main() {
cout << "请输入树的结点数量" << endl;
int n;
cin >> n;
cout << "请输入前序遍历的结果" << endl;
for (int i = 0; i < n; ++i) {
cin >> pre[i];
}
cout << "请输入中序遍历的结果" << endl;
for (int i = 0; i < n; ++i) {
cin >> mid[i];
}
Tree tree = createTree(0,n, 0, n);
treePrint(tree);
cout << "后序遍历:";
preOrderPrintTree(tree);
return 0;
}
输入样例:
7
1 2 4 5 3 6 7
4 2 5 1 6 3 7