根据前序、中序遍历序列构建二叉树-讲解与实现(C++)

根据前序、中序构造二叉树

一道有意思的题,可以很好地了解树的遍历

  • 各小节会给出阶段/核心代码
  • 在附录中有完整的代码

问题描述

有一棵二叉树,其前序序列为 [ 3 , 9 , 20 , 15 , 7 ] [3,9,20,15,7] [3,9,20,15,7],其中序序列为 [ 9 , 3 , 15 , 20 , 7 ] [9,3,15,20,7] [9,3,15,20,7],那么,该如何通过这两个序列构唯一造出一棵二叉树呢?

树的遍历

首先我们需要了解树的遍历是如何实现的?

假设有一棵树,其树型如下(其实就是问题描述中的树型):

  3/ \
9  20/\15 7
  • 前序遍历,其遍历方式可以用DLR来表示,代表当遍历到一个节点,优先查看其自身D,再按照左孩子L、右孩子R的顺序查看

其实现代码与结果如下,大家可结合来理解:

void pre(TreeNode* u)
{if (u) {cout << u->val << " ";pre(u->left);pre(u->right);}
}
输出:
3 9 20 15 7
  • 中序遍历,其遍历方式可以用LDR来表示,代表的内容可根据上面的解释理解

其实现代码与结果如下,大家可结合来理解:

void in(TreeNode* u)
{if (u) {in(u->left);cout << u->val << " ";in(u->right);}
}
输出:
9 3 15 20 7
  • 后序序遍历,其遍历方式可以用LRD来表示,代表的内容可根据上面的解释理解

其实现代码与结果如下,大家可结合来理解:

void post(TreeNode* u) 
{if (u){post(u->left);post(u->right);cout << u->val << " ";}
}
输出:
9 15 7 20 3

问题分析

现在,我们已经了解了树的遍历,现在回过去看一下问题,我们该如何构造二叉树?

以下的叙述大家可结合上一节,树的遍历中的例子理解

  1. 找到当前树的根节点
    • 我们可以发现,前序遍历的第一个元素,它一定是根节点
  2. 找到左右两棵子树
    • 我们可以发现,在中序遍历序列中,当你知道了根节点,其左边一定是该节点的左子树,右边一定是该节点的右子树
    • 可以结合中序遍历的LDR思考
  3. 基于2、3,我们可以递归地向下执行,即从根节点开始,分割左右子树,再从左右子树继续深入,直到叶节点

代码实现

  • 例题:105. 从前序与中序遍历序列构造二叉树
    • 理解了下面的代码后可以去尝试解例题
    • AC之后,可以思考一下,如何通过中序、后序构造二叉树
      • 题目链接:106. 从中序与后序遍历序列构造二叉树
/*** pl:  前序遍历的左边界; pr:  前序遍历的右边界* il:  中序遍历的左边界; ir:  中序遍历的右边界* pre: 前序遍历序列;    in:  中序遍历序列
*/
TreeNode* build(int pl, int pr, int il, int ir, int* pre, int* in)
{// 获取根节点的值int root_val = pre[pl];// 找到根节点在中序遍历中的位置int k = 0;while (in[k] != root_val) k ++;// 构建根节点TreeNode* root = new TreeNode(root_val);// 当中序遍历序列中, 根节点的左边还有位置, 代表当前树有左子树/*** 参数说明* pl + 1: pl对应根节点, 则其左子树一定从pl + 1开始, 对应左子树的左边界* il, k - 1:当前根节点, 对应中序遍历中, 左子树的左右边界* 左子树的数量是固定的, 可以用k - 1 - il表示, 所以在前序遍历中pl + 1 + (k - 1 - il)对应左子树的右边界* */if (il < k) root->left = build(pl + 1, pl + 1 + (k - 1 - il), il, k - 1, pre, in);// 当中序遍历序列中, 根节点的右边还有位置, 代表当前树有右子树/*** 参数说明* pl + 1 + (k - 1 - il) + 1: 左子树右边界 + 1 -> 右子树左边界* pr: 右子树右边界* k + 1, ir: 当前根节点, 对应中序遍历中, 右子树的左右边界*/if (k < ir) root->right = build(pl + 1 + (k - 1 - il) + 1, pr, k + 1, ir, pre, in);return root;
}

附录

树的遍历与完整样例实现

#include <iostream>using namespace std;struct TreeNode 
{int val;TreeNode *left;TreeNode *right;TreeNode() : val(0), left(nullptr), right(nullptr) {}TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
};void pre(TreeNode* u)
{if (u) {cout << u->val << " ";pre(u->left);pre(u->right);}
}void in(TreeNode* u)
{if (u) {in(u->left);cout << u->val << " ";in(u->right);}
}void post(TreeNode* u) 
{if (u){post(u->left);post(u->right);cout << u->val << " ";}
}/*** pl:  前序遍历的左边界; pr:  前序遍历的右边界* il:  中序遍历的左边界; ir:  中序遍历的右边界* pre: 前序遍历序列;    in:  中序遍历序列
*/
TreeNode* build(int pl, int pr, int il, int ir, int* pre, int* in)
{// 获取根节点的值int root_val = pre[pl];// 找到根节点在中序遍历中的位置int k = 0;while (in[k] != root_val) k ++;// 构建根节点TreeNode* root = new TreeNode(root_val);// 当中序遍历序列中, 根节点的左边还有位置, 代表当前树有左子树/*** 参数说明* pl + 1: pl对应根节点, 则其左子树一定从pl + 1开始, 对应左子树的左边界* il, k - 1:当前根节点, 对应中序遍历中, 左子树的左右边界* 左子树的数量是固定的, 可以用k - 1 - il表示, 所以在前序遍历中pl + 1 + (k - 1 - il)对应左子树的右边界* */if (il < k) root->left = build(pl + 1, pl + 1 + (k - 1 - il), il, k - 1, pre, in);// 当中序遍历序列中, 根节点的右边还有位置, 代表当前树有右子树/*** 参数说明* pl + 1 + (k - 1 - il) + 1: 左子树右边界 + 1 -> 右子树左边界* pr: 右子树右边界* k + 1, ir: 当前根节点, 对应中序遍历中, 右子树的左右边界*/if (k < ir) root->right = build(pl + 1 + (k - 1 - il) + 1, pr, k + 1, ir, pre, in);return root;
}int main()
{TreeNode* root = new TreeNode(3);TreeNode* lL = new TreeNode(9);TreeNode* rL = new TreeNode(20, new TreeNode(15), new TreeNode(7));root->left = lL;root->right = rL;pre(root);puts("");in(root);puts("");post(root);puts("");int pre[5] = {3, 9, 20, 15, 7}, in[5] = {9, 3, 15, 20, 7};int n = sizeof(pre) / sizeof(int) - 1;TreeNode* construct = build(0, n, 0, n, pre, in);puts("");post(construct);return 0;
}

例题代码实现

105

class Solution {
private:TreeNode* build(int pl, int pr, int il, int ir, vector<int>& pre, vector<int>& in) {int val = pre[pl];int k = 0;while (in[k] != val) k ++;TreeNode* root = new TreeNode(val);if (il < k) root->left = build(pl + 1, pl + 1 + k - 1 - il, il, k - 1, pre, in);if (k < ir) root->right = build(pl + 1 + k - 1 - il + 1, pr, k + 1, ir, pre, in);return root;}public:TreeNode* buildTree(vector<int>& pre, vector<int>& in) {int n = pre.size() - 1;return build(0, n, 0, n, pre, in);}
};

106

class Solution {
private:TreeNode* build(int pl, int pr, int il, int ir, vector<int>& in, vector<int>& post) {int val = post[pr];int k = 0;while (in[k] != val) k ++;TreeNode *root = new TreeNode(val);if (il < k) root->left = build(pl, pl + (k - 1 - il), il, k - 1, in, post);if (k < ir) root->right = build(pl + (k - 1 - il) + 1, pr - 1, k + 1, ir, in, post);return root;}public:TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {int n = inorder.size() - 1;return build(0, n, 0, n, inorder, postorder);}
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/455206.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[LeetCode] 230. 二叉搜索树中第K小的元素

题目描述&#xff1a; 给定一个二叉搜索树的根节点 root &#xff0c;和一个整数 k &#xff0c;请你设计一个算法查找其中第 k 小的元素&#xff08;从 1 开始计数&#xff09;。 示例 1&#xff1a; 输入&#xff1a;root [3,1,4,null,2], k 1 输出&#xff1a;1示例 2&am…

欧盟 RED 网络安全法规 EN 18031

目录 1. &#x1f4c2; EN 18031 1.1 背景 1.2 专业术语 1.3 覆盖产品范围 1.4 EN 18031标准主要评估内容&#xff1a; 1.5 EN 18031标准主要评估项目&#xff1a; 1.6 EN 18031 与 ETSI EN 303 645 的主要差异 1.7 RED 网络安全法规解读研讨会 2. &#x1f531; EN 1…

Docker:namespace环境隔离 CGroup资源控制

Docker&#xff1a;namespace环境隔离 & CGroup资源控制 Docker虚拟机容器 namespace相关命令ddmkfsdfmountunshare 进程隔离文件隔离 CGroup相关命令pidstatstresscgroup控制 内存控制CPU控制 Docker 在开发中&#xff0c;经常会遇到环境问题&#xff0c;比如程序依赖某个…

VirtualBox虚拟机桥接模式固定ip详解

VirtualBox虚拟机桥接模式固定ip详解 VirtualBox 桥接设置Ubuntu 24.04使用固定IP问题记录 VirtualBox 桥接设置 为什么设置桥接模式&#xff1f;桥接模式可以实现物理机和虚拟机互相通信&#xff0c;虚拟机也可以访问互联网&#xff08;推荐万金油&#xff09;&#xff0c;物…

AudioSegment 提高音频音量 - python 实现

一些采集的音频声音音量过小可以通过 AudioSegment 实现音量增强。 按照 python 库&#xff1a; pip install AudioSegment 代码具体实现&#xff1a; #-*-coding:utf-8-*- # date:2024-10 # Author: DataBall - XIAN # Function: 音频增加音量import os from pydub import …

网络安全领域推荐证书介绍及备考指南

在网络安全领域&#xff0c;拥有专业认证不仅可以证明个人的专业能力&#xff0c;还能帮助在实际工作中应用先进的技术和知识。以下是几种热门的网络安全证书介绍及备考指南。 1. OSCP (Offensive Security Certified Professional) 证书简介 OSCP是针对渗透测试领域的入门级…

在示波器里面外触发输入通道(EXT TRIG)什么作用?

在示波器中&#xff0c;外部触发输入通道&#xff08;EXT TRIG&#xff09;具有以下作用&#xff1a; 1. 提供外部信号触发 外部触发输入通道允许用户使用来自外部设备的信号作为触发源&#xff0c;而不仅仅依赖于示波器自身的输入通道。比如&#xff0c;可以用一个特定事件或…

Docker 基础入门

Docker 基础入门 前言 在云计算和微服务架构日益盛行的今天&#xff0c;软件开发与部署的效率和灵活性成为了企业竞争力的关键因素之一。Docker&#xff0c;作为一种开源的容器化平台&#xff0c;凭借其轻量级、可移植性和易于管理的特性&#xff0c;迅速成为现代软件开发和运…

qt QWidget详解

一、概述 QWidget是容器组件&#xff0c;继承自QObject类和QPaintDevice类。能够绘制自己和处理用户输入&#xff0c;是QT中所有窗口组件类的父类&#xff0c;是所有窗口组件的抽象&#xff0c;每个窗口组件都是一个QWidget&#xff0c;QWidget类对象常用作父组件或顶级组件使…

小新学习K8s第一天之K8s基础概念

目录 一、Kubernetes&#xff08;K8s&#xff09;概述 1.1、什么是K8s 1.2、K8s的作用 1.3、K8s的功能 二、K8s的特性 2.1、弹性伸缩 2.2、自我修复 2.3、服务发现和负载均衡 2.4、自动发布&#xff08;默认滚动发布模式&#xff09;和回滚 2.5、集中化配置管理和密钥…

信发软件之电脑版拖动——未来之窗行业应用跨平台架构

一、电脑版拖动 二、电脑版随意移动函数 var _movefalse;//移动标记 var _x,_y;//鼠标离控件左上角的相对位置 $("#"宿主id).click(function(){ }).mousedown(function(e){ _movetrue; _xe.pageX-parseInt($("#"宿主id).css("left")); _ye…

【Python爬虫实战】多进程结合 BeautifulSoup 与 Scrapy 构建爬虫项目

#1024程序员节&#xff5c;征文# &#x1f308;个人主页&#xff1a;易辰君-CSDN博客 &#x1f525; 系列专栏&#xff1a;https://blog.csdn.net/2401_86688088/category_12797772.html ​ 前言 在大数据时代&#xff0c;爬虫技术是获取和处理网络数据的利器。面对需要处理大…

报表工具怎么选?山海鲸VS帆软,哪个更适合你?

概述 在国产报表软件市场中&#xff0c;山海鲸报表和帆软这两款工具都占有一席之地&#xff0c;许多企业在选择报表工具时常常在它们之间徘徊。然而&#xff0c;随着企业对数据分析需求的不断增长和复杂化&#xff0c;如何选取一款高效、易用且性价比高的报表工具&#xff0c;…

Linux笔记之文件查找和搜索命令which,find,locate,whereis总结

Linux笔记之文件查找和搜索命令which,find,locate,whereis总结 code review! 文章目录 Linux笔记之文件查找和搜索命令which,find,locate,whereis总结1.对比2.whereis 和 which 命令区别3.locate 和 find 命令区别 1.对比 命令功能说明备注which常用于查找可直接执行的命令。…

【状态机DP】【记忆化搜索1:1翻译递归空间优化】力扣2771. 构造最长非递减子数组

给你两个下标从 0 开始的整数数组 nums1 和 nums2 &#xff0c;长度均为 n 。 让我们定义另一个下标从 0 开始、长度为 n 的整数数组&#xff0c;nums3 。对于范围 [0, n - 1] 的每个下标 i &#xff0c;你可以将 nums1[i] 或 nums2[i] 的值赋给 nums3[i] 。 你的任务是使用最…

【MySQL】表的增删改查(CRUD)

目录 1.前言 2.增加&#xff08;Create&#xff09; 3.查询&#xff08;Retrieve&#xff09; 3.1全列查询 3.2指定列查询 3.3查询字段为表达式 3.4别名 3.5去重&#xff1a;DISTINCT 3.6排序&#xff1a;ORDER BY 3.7条件查询&#xff1a;WHERE 3.8分页查询&#x…

【LLM之Agent】《Tool Learning with Large Language Models: A Survey》论文阅读笔记

概述 背景信息 近年来&#xff0c;基于大型语言模型&#xff08;LLMs&#xff09;的工具学习成为增强LLMs应对复杂任务能力的有力范式。尽管这一领域快速发展&#xff0c;现有文献的碎片化以及缺乏系统组织&#xff0c;给新入门者带来了阻碍。因此&#xff0c;本论文旨在对现…

stable-zero123模型构建指南

一、介绍 stabilityai出品&#xff0c;能够对有简单背景的物体进行三维视角图片的生成&#xff0c;简单来说也就是通过调整变换观察的视角生成对应视角的图片。 本项目通过comfyui实现。 二、容器构建说明 1. 部署ComfyUI &#xff08;1&#xff09;使用命令克隆ComfyUI g…

【设计模式系列】命令模式

目录 一、什么是命令模式 二、命令模式的角色 三、命令模式的典型应用场景 四、命令模式在Runnable中的应用 一、什么是命令模式 命令模式&#xff08;Command Pattern&#xff09;是一种行为设计模式&#xff0c;它将一个请求或简单操作封装为一个对象。这个模式提供了一种…

Axure垂直菜单展开与折叠

亲爱的小伙伴&#xff0c;在您浏览之前&#xff0c;烦请关注一下&#xff0c;在此深表感谢&#xff01; 课程主题&#xff1a;Axure垂直菜单展开与折叠 主要内容&#xff1a;垂直菜单单击实现展开/折叠&#xff0c;点击各菜单项显示选中效果 应用场景&#xff1a;后台菜单设…