【算法竞赛模板】二叉树专题(含前中后序遍历建树、DFS与BFS等对二叉树的操作)

news/2025/2/23 0:37:50

二叉树专题

  本蒻苟发文,有任何不足的地方欢迎大佬们斧正~(^∀^●)ノシ

一、构建二叉树

  俗话说的好,算法重中之重就是数据存储结构,所以我们先来学一下如何通过给出 中序+前序遍历中序+后序遍历 来构建二叉树
  为什么没有 前序+后序遍历 构建二叉树呢?因为这样构建不了二叉树,或者说不能构建唯一的一个二叉树

① 在中序遍历中对根节点快速定位

  这是两种构建二叉树方法的桥接点,非常重要。一种简单的方法是直接扫描整个中序遍历的结果并找出根节点,但这样做的时间复杂度较高。
  我们可以考虑使用哈希表来帮助我们快速地定位根节点。对于哈希映射中的每个键值对,键(key)表示节点的值,值(value)表示其在中序遍历中的出现位置。这样在中序遍历中查找根节点位置的操作,只需要 O(1)的时间

vector<int> in;
	...		//假设已经读入中序遍历的结果
unordered_map<int, int> pos;
for(int i = 0; i < in.size(); i++)
	pos[in[i]] = i;

② 前序 + 中序 遍历构建二叉树

👉练习题:点这里
在这里插入图片描述
在这里插入图片描述

#include <bits/stdc++.h>
using namespace std;

int n;
vector<int> pre, in;
unordered_map<int, int> pos;

struct TreeNode {
	int val;
	TreeNode *left;
	TreeNode *right;
	TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};

TreeNode* buildTree(int il, int ir, int pl, int pr) {
	if(il > ir) return nullptr;
	int k = pos[pre[pl]];		//根节点的位置
	TreeNode* root = new TreeNode(pre[pl]);
	//中序遍历中「从 左边界 开始到 根节点定位-1」的元素就对应了
	//先序遍历中「从 左边界+1 开始的 左边界+左子树节点数目」的元素
	root->left = buildTree(il, k-1, pl+1, pl+(k-il));
	//中序遍历中「从 根节点定位+1 到 右边界」的元素就对应了
	//先序遍历「从 左边界+1+左子树节点数目 开始到 右边界」的元素
	root->right = buildTree(k+1, ir, pl+1+(k-il), pr);
	return root;
}

int main(){
	int num;
	cin >> n;
	for(int i = 0; i < n; i++){
		scanf("%d", &num);
		in.push_back(num);
	}
	for(int i = 0; i < n; i++){
		scanf("%d", &num);
		pre.push_back(num);
	}
	for(int i = 0; i < in.size(); i++)
		pos[in[i]] = i;
	TreeNode* root = buildTree(0, n-1, 0, n-1);
	return 0;
}

③ 后序 + 中序 遍历构建二叉树

👉练习题:点这里
在这里插入图片描述

#include <bits/stdc++.h>
using namespace std;

int n;
vector<int> in, post;
vector<vector<int>> tree;
unordered_map<int, int> pos;

struct TreeNode {
	int val;
	TreeNode *left;
	TreeNode *right;
	TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};

TreeNode* buildTree(int il, int ir, int pl, int pr) {
	if(il > ir) return nullptr;
	int k = pos[post[pr]];
	TreeNode* root = new TreeNode(post[pr]);
	//中序遍历中「从 左边界 开始到 根节点位置-1」的元素就对应了
	//后序遍历中「从 左边界 开始到 左边界+左子树节点数目」的元素
	root->left = buildTree(il, k-1, pl, pl+(k-1-il));
	//中序遍历中「从 根节点定位+1 到 右边界」的元素就对应了
	//后序遍历「从 左边界+左子树节点数目+1 开始到 右边界-1」的元素
	root->right = buildTree(k+1, ir, pl+(k-1-il)+1, pr-1);
	return root;
}

int main(){
	int num;
	cin >> n;
	for(int i = 0; i < n; i++){
		scanf("%d", &num);
		post.push_back(num);
	}
	for(int i = 0; i < n; i++){
		scanf("%d", &num);
		in.push_back(num);
	}
	for(int i = 0; i < in.size(); i++)
		pos[in[i]] = i;
	TreeNode* root = buildTree(0, n-1, 0, n-1);
	return 0;
}

二、前中后序 遍历二叉树

① 前序遍历二叉树 (非递归)

使用栈来模拟递归的操作,循环条件:节点不为NULL,且栈不为空
  1. 如果当前节点不为空,访问节点,并且把节点进栈,节点指向其左孩子,直至左孩子为空
  2. 这时相当于左子树已经遍历完了,我们需要访问右节点,将当前元素指向栈顶元素右孩子,弹出栈顶元素

vector<int> preorder(TreeNode* root) {
    vector<int> res;
    stack<TreeNode*> st;
    TreeNode* cur = root;
    while(cur || st.size())
    {
        while(cur)
        {
            res.push_back(cur->val);	//第一次进栈时访问节点
            st.push(cur);
            cur = cur->left;
        }
        TreeNode* p = st.top();
        st.pop();
        cur = p->right;
    }
    return res;
}

② 中序遍历二叉树 (非递归)

  和前序遍历类似,只不过访问节点从第一次进栈时变成出栈时访问节点

vector<int> inorder(TreeNode* root) {
    vector<int> res;
    stack<TreeNode*> st;
    TreeNode* cur = root;
    while(cur || st.size())
    {
        while(cur)
        {
            st.push(cur);
            cur = cur->left;
        }
        TreeNode* p = st.top();
        st.pop();
        res.push_back(p->val);	//变成出栈时访问节点
        cur = p->right;
    }
    return res;
}

③ 后序遍历二叉树 (非递归)

  后序遍历的难点在于第一次将根节点出栈后,并不能直接访问,必须访问其右子树后才可以。非递归后序遍历有很多种实现方式,我个人比较喜欢使用pair来标记栈中元素的状态,状态为false时不可访问,状态为true时才能

vector<int> postorder(TreeNode* root) {
	vector<int> res;
	stack<pair<TreeNode*, bool>> s;
    s.push({root, false});
    bool visited;
    while (s.size())
    {
        root = s.top().first;
        visited = s.top().second;
        s.pop();
        if (!root) continue;
        if (visited) res.push_back(root->val);
        else {
            s.push({root, true});
            s.push({root->right, false});
            s.push({root->left, false});
        }
    }
    return res;
}

三、变种题 之 二叉树

BFS_198">① BFS层次遍历二叉树

  实现思路: 二叉树的层次遍历通常情况下借助于队列实现。当根节点不为空时,将根节点加入队列。当队列不为空的时候,访问队列头元素,同时将队头元素的非空子节点进队列,将队头元素出队

vector<int> levelTraversal(TreeNode* root) {
    vector<int> res;
    queue<TreeNode*> q;
    if(!root) return res;
    q.push(root);
    while(!q.empty())
    {
        TreeNode* cur = q.front();
        q.pop();
        res.push_back(cur->val);
        if(cur—>left) q.push(cur->left);
        if(cur->right) q.push(cur->right);
    }
    return res;
}

② DFS递归遍历二叉树

  有时候题目只需要得到某一层的最大值,最小值,第一个节点,最后一个节点,左右视图之类的,我们就没有必要严格按照层次遍历的顺序了。可以使用dfs来遍历每一层的节点。注意:这不是真正的层次遍历

vector<vector<int>> tree;
void dfs(TreeNode* root, int level){
    if(!root) return;
    if(tree.size() == level)
        tree.push_back({});
    tree[level].push_back(root->val);
    dfs(root->left, level + 1);
    dfs(root->right, level + 1);
}

③ 翻转二叉树

  也可以看作是二叉树的镜面,即将非叶子节点的左右孩子对换

void dfs(TreeNode* root) {
	if(!root) return;
	swap(root->left, root->right);
	dfs(root->left);
	dfs(root->right);
}

http://www.niftyadmin.cn/n/745540.html

相关文章

2020年机械员-通用基础(机械员)模拟试题及机械员-通用基础(机械员)模拟考试题

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2020年机械员-通用基础(机械员)模拟试题及机械员-通用基础(机械员)模拟考试题&#xff0c;包含机械员-通用基础(机械员)模拟试题答案和解析及机械员-通用基础(机械员)模拟考试题练习。由安全生产模拟考试一点通公众号…

C++多态认识篇

小白发文&#xff0c;若文中有任何不足欢迎大佬们来斧正~&#xff08;&#xff3e;∀&#xff3e;●&#xff09;&#xff89;&#xff7c; [TOC](多态的认识C) # 一、多态的定义   可能同学们不太了解什么是多态&#xff0c;多态指的是&#xff1a;**同一个基类拥有多种表现…

2020年压力焊答案解析及压力焊模拟考试题

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2020年压力焊答案解析及压力焊模拟考试题&#xff0c;包含压力焊答案解析答案和解析及压力焊模拟考试题练习。由安全生产模拟考试一点通公众号结合国家压力焊考试最新大纲及压力焊考试真题汇总&#xff0c;有助于压力…

2020年重氮化工艺报名考试及重氮化工艺考试报名

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2020年重氮化工艺报名考试及重氮化工艺考试报名&#xff0c;包含重氮化工艺报名考试答案和解析及重氮化工艺考试报名练习。由安全生产模拟考试一点通公众号结合国家重氮化工艺考试最新大纲及重氮化工艺考试真题汇总&a…

2022华为软挑赛题讲解(CodeCraft-2022)

2022华为软挑赛题讲解一、赛题任务书简述二、解题过程三、心得体会代码链接附在文后&#xff0c;赛题解释权归华为所有&#xff0c;本苟弱对赛题的讲解不一定是完美的&#xff0c;有任何不足和错误欢迎大佬们斧正~一、赛题任务书简述 总的来说是视频直播流量调度的问题&#xf…

2020年烟花爆竹经营单位主要负责人试题及答案及烟花爆竹经营单位主要负责人实操考试视频

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2020年烟花爆竹经营单位主要负责人试题及答案及烟花爆竹经营单位主要负责人实操考试视频&#xff0c;包含烟花爆竹经营单位主要负责人试题及答案答案和解析及烟花爆竹经营单位主要负责人实操考试视频练习。由安全生产…

【Template模式】C++设计模式——模板模式

Template模式一、软件设计流程探讨二、Template模式介绍三、代码实现现代软件设计的特征是“需求的频繁变化”。设计模式的要点是——寻找变化点&#xff0c;然后在变化点处应用设计模式&#xff0c;从而更好地应对需求的变化&#xff0c; “什么时候、什么地点应用设计模式” …

2020年煤炭生产经营单位(安全生产管理人员)考试内容及煤炭生产经营单位(安全生产管理人员)考试报名

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2020年煤炭生产经营单位&#xff08;安全生产管理人员&#xff09;考试内容及煤炭生产经营单位&#xff08;安全生产管理人员&#xff09;考试报名&#xff0c;包含煤炭生产经营单位&#xff08;安全生产管理人员&…