持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第24天,点击查看活动详情
手把手教学考研大纲范围内树定义,遍历,Huffman,并查集 22考研大纲数据结构要求的是C/C++,笔者以前使用的都是Java,对于C++还很欠缺, 如有什么建议或者不足欢迎大佬评论区或者私信指出 初心是用最简单的语言描述数据结构
Talk is cheap. Show me the code. 理论到处都有,代码加例题自己练习才能真的学会
二叉树根据中序遍历,后序遍历构建二叉树,并输出前序遍历
中序遍历:相对根结点的位置,左面是当前根结点左子树的结点,右面是当前根结点的右子树的结点
后序遍历:相对子树的根结点在相对左右子结点靠后的位置,相对根结点都靠后
思路:
从后序遍历后面找到根结点,
在中序遍历找到根结点,根结点左面就是根结点左子树的结点,右面就是根结点右子树的结点
然后继续在后序遍历找下一个根结点(右子树的根结点),在上面的右子树的结点中找右子树的根结点,
继续这种循环,直到右子树的结点为空,返回上一层
如果上面右子树不存在,那么右子树的结点也是不存在的,直接返回上一层了
左子树也是,在后序遍历找上一个根结点(刚才的右结点都找完了现在才会是左结点),在上面左子树结点找左子树的根结点
继续循环
思路简化:
后序遍历是后面的都是根结点,后序遍历是根结点的从后向前排序
根据后序遍历的根结点,在中序遍历找到根结点位置,
中序遍历中:根结点左面是根结点的左子树的结点,根结点的右面是根结点的右子树的结点
继续找下一个根结点,在右子树结点里找新的根结点,
(如果右子树结点不存在,那么新的根结点就是原根的左结点,就是在左子树结点里找新的根结点)
如果右子树结点或者左子树结点不存在返回上一级
不断循环这个过程
思路图解:
例子:
3
/ \
9 20
/ \
15 7
中序遍历 inorder = [9,3,15,20,7]
后序遍历 postorder = [9,15,7,20,3]
1、初始状态:
2、先从后序遍历中找到根结点,在中序遍历找到根结点,
3、中序遍历中根结点右面是右子树结点
4、中序遍历中根结点右面是右子树结点
5、中序遍历中根结点左面是左子树结点
6、当前根结点的左子树右子树都遍历完了,返回上一层,遍历上一层的左子树结点
7、构建完成
代码+注释
//根据前序遍历和中序遍历构建二叉树,求出后序遍历
#include "iostream"
#include "math.h"
#include "queue"
#define MAXSize 100
using namespace std;
int mid[MAXSize];
int post[MAXSize];
typedef struct TreeNode{ //树的结构体
int data;
struct TreeNode *left;
struct TreeNode *right;
} TreeNode, *Tree;
//创建二叉树 //中序遍历的范围midStart-midEnd 后序遍历的范围postStrat - postEnd
Tree createTree(int midStart, int midEnd, int postStart, int postEnd) { //范围都是包括左边不包括右边(包括起始点不包括终止点)
if (postStart >= postEnd || midStart >= midEnd) { //起始点大于等于终止点,范围即为空,返回空
return NULL;
}
Tree root = new TreeNode;
root->data = post[postEnd - 1]; //后序遍历最后一个就是根结点(后序遍历,遍历顺序为:左结点,右结点,根结点)
int i;
for (i = midStart; i < midEnd; ++i) { //在中序遍历中找到根结点下标 i
if (mid[i] == post[postEnd - 1]) { //i左边范围就是左子树结点,i右面范围就是右子树结点
break; //(中序遍历的顺序:左子树,根结点,右子树) (根结点以左就是左子树结点,以右就是右子树结点)
}
}
//后序遍历 要先找右子结点(后序遍历中,根结点前面是右子树,所以先找右子结点)
//中序遍历中右子树的范围:根结点在中序遍历的位置以右,到这段中序范围的结束
//后序遍历中右子树的范围:根结点在后序遍历中是最后一位,所以右子树后序遍历的结束范围是到当前后序范围终止点的前一位
//根结点的起始点是 终止点向前走 右子树数量个位置 (midEnd - i - 1)
//midend - i 是中序遍历中根结点到末尾的数量(范围不包括末尾) -1 是因为右子树不能包括根结点,要把根结点删除
//也可以理解为,右子树结点的数量为midEnd-(i+1) 后序列遍历的范围是postEnd - 1 向前走右子树结点的数量位
root->right = createTree(i + 1, midEnd, postEnd - 1 - (midEnd - i - 1) , postEnd - 1);
//中序遍历中左子树的范围:中序遍历开始位置 到 根结点位置 i (不包括根结点)
//后序遍历中左子树的范围: 后序遍历起始位置 到 右子树以前 都是左子树的结点范围
root->left = createTree(midStart, i,postStart , postEnd - 1 - (midEnd - i - 1) );
return root;
}
//后序遍历
void preOrderPrintTree (Tree tree) {
if (tree == NULL) return;
cout << tree->data << " ";
preOrderPrintTree(tree->left);
preOrderPrintTree(tree->right);
}
//返回二叉树的深度(递归)
int Depth (Tree tree) {
if (tree == NULL) {
return 0;
}
return max(Depth(tree->left), Depth(tree->right)) + 1; //找左或右结点的最大深度 + 1(加一是加上当前这一层)
//不断循环这种操作
}
//格式化输出二叉树(直接看顺序存储的注释就可以,格式化输出就是找二叉树的规律)
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 << " ";