22计算机408考研—数据结构—二叉树根据前序遍历,中序遍历构建二叉树,并输出后序遍历

160 阅读5分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 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]

1.jpg

先从前序遍历中找到根结点,在中序遍历找到根结点,

2.jpg

根结点左面是左子树结点,根结点右面是右子树结点

然后不断的循环这个操作,在先序遍历中找下一个根结点……

思路动图:

在这里插入图片描述

代码+注释

//根据前序遍历和中序遍历构建二叉树,求出后序遍历
#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

在这里插入图片描述

LeetCode例题:105. 从前序与中序遍历序列构造二叉树

题目链接

在这里插入图片描述