【二叉树】(一)二叉树基础知识及多种遍历方式

二叉树基础知识及多种遍历方式

  • 关于二叉树,该了解这些
    • 二叉树的种类
    • 存储方式
    • 遍历方式
    • 节点定义
  • 二叉树的三种递归遍历(深度遍历)
    • 递归遍历设计核心思想
    • 递归遍历力扣例题解析
  • 二叉树的三种迭代遍历(深度遍历)
    • 前后续遍历
    • 中序遍历
  • 二叉树的层序遍历(广度遍历)
    • 层序遍历的思想与实现
    • 层序遍历力扣十题分析

代码随想录二叉树链接:二叉树理论基础
相关配套视频教程:二叉树基础

关于二叉树,该了解这些

二叉树的种类

  • 满二叉树: 节点数 2^k-1 (k为深度)
  • 完全二叉树: 除了底层以外,其他层都是满的,并且底层从左往右是连续的。
  • 二叉搜索树: 左子数的所有节点都小于中间节点,右子数的所有节点都大于中间节点,同时左右子树也都满足这个规律。
  • 平衡二叉搜索树: 左右两个⼦树的⾼度差的绝对值不超过1

存储方式

  • ☆链式存储:数据域,左子树和右子树
  • 线性存储:用字符数组保存数据,对每个节点进行标序(左子树下标:2 * i + 1;右子树下标:2 * i + 2

遍历方式

1. 深度优先搜索: 沿着一个方向搜索,一直到终点后回退

  • ☆前序遍历、中序遍历、后续遍历递归实现
    • 前序遍历:中左右
    • 中序遍历:左中右
    • 后续遍历:左右中
    • 这里的左右指的的左右子树
  • 也可以用模拟实现,递归的实现过程就是用栈实现递归的行为
  • 迭代法(非递归方式)也可以分别实现前中后序遍历

2. 广度优先搜索: 一层一层的搜索或者一圈一圈的搜索

  • 层序遍历就是广度优先搜索
  • 层序遍历就是用的迭代法,用队列原理实现

节点定义

struct TreeNode{int val;					//节点数据struct TreeNode *left;		//左子树struct TreeNode *right;		//右子树TreeNode():	val(0),left(NULL),right(NULL){}			//无参构造TreeNode(int x):val(x),left(NULL),right(NULL){}		//只有值构造TreeNode(int x, TreeNode* left, TreeNode* right):val(x),left(left),right(right){}
};

二叉树的三种递归遍历(深度遍历)

递归遍历设计核心思想

以前序遍历为例:

  • 【确定递归函数的参数与返回值】
      因为要打印出前序遍历节点的数值,所以参数里需要传入vector来放节点的数值,除了这一点就不需要再处理什么数据了也不需要有返回值,所以递归函数返回类型就是void,代码如下:

    void traversal(TreeNode* cur, vector<int>& vec)
    
  • 【确定终止条件】
      在递归的过程中,如何算是递归结束了呢,当然是当前遍历的节点是空了,那么本层递归就要结束了,所以如果当前遍历的这个节点是空,就直接return,代码如下:

    if(cur == NULL)return ;
    
  • 【确定单层递归逻辑】
      序遍历是中左右的循序,所以在单层递归的逻辑,是要先取中节点的数值,代码如下:

    vec.push_back(cur->val);    // 中
    traversal(cur->left, vec);  // 左
    traversal(cur->right, vec); // 右
    

中序遍历程序:

void traversal(TreeNode* cur, vector<int>& vec) 
{if (cur == NULL) return;traversal(cur->left, vec);  // 左vec.push_back(cur->val);    // 中traversal(cur->right, vec); // 右
}

后续遍历程序:

void traversal(TreeNode* cur, vector<int>& vec) {if (cur == NULL) return;traversal(cur->left, vec);  // 左traversal(cur->right, vec); // 右vec.push_back(cur->val);    // 中
}

此时可以做一做leetcode上三道题目练练手,分别是:

  • 144. 二叉树的前序遍历
  • 94. 二叉树的中序遍历
  • 145. 二叉树的后序遍历

递归遍历力扣例题解析

144. 二叉树的前序遍历
  定义 preorder(root) 表示当前遍历到 root 节点的答案。按照定义,我们只要首先将 root 节点的值加入答案,然后递归调用 preorder(root->left) 来遍历 root 节点的左子树,最后递归调用 preorder(root->right) 来遍历 root 节点的右子树即可,递归终止的条件为碰到空节点。

class Solution {
public:void preorder(TreeNode* node, vector<int>& res){if(node == NULL)return;res.push_back(node->val);preorder(node->left,res);	preorder(node->right,res);	}vector<int> preorderTraversal(TreeNode* root) {vector<int> res;preorder(root,res);return  res;}
};

94. 二叉树的中序遍历
对于中序和后序遍历机制与前序遍历一样,只需要更改左中右顺序即可

class Solution {
public:void inorder(TreeNode* node, vector<int>& res){if(node == NULL)return;inorder(node->left,res);res.push_back(node->val);inorder(node->right,res);}vector<int> inorderTraversal(TreeNode* root) {vector<int> res;inorder(root,res);return res;}
};

145. 二叉树的后序遍历

class Solution {
public:void postorder(TreeNode* node, vector<int>& res){if(node == NULL)return;postorder(node->left,res);postorder(node->right,res);res.push_back(node->val);}vector<int> postorderTraversal(TreeNode* root) {vector<int> res;postorder(root,res);return res;}
};

二叉树的三种迭代遍历(深度遍历)

  递归的实现就是:每一次递归调用都会把函数的局部变量、参数值和返回地址等压入调用栈中,然后递归返回的时候,从栈顶弹出上一次递归的各项参数,所以这就是递归为什么可以返回上一层位置的原因,因此用栈也可以实现二叉树的前后中序遍历。

前后续遍历

  前序遍历是中左右,每次先处理的是中间节点,那么先将根节点放入栈中,然后将右孩子加入栈,再加入左孩子
为什么要先加入 右孩子,再加入左孩子: 因为这样出栈的时候才是中左右的顺序。
动画如下:

不难写出如下代码:

vector<int> preorderTraversal(TreeNode* root) {stack<TreeNode*> st;	//栈vector<int> res;		//结果容器TreeNode* node;			//栈顶节点st.push(root);			//根节点入栈while(!st.empty())		{node = st.top();	//获取栈顶节点并弹出st.pop();//中if(node != NULL)	//非空元素加入数组res.push_back(node->val);elsecontinue;//左右节点 先放右子树再放左子树入栈//这样出栈才达到中左右顺序if(node->right)				//空节点不入栈st.push(node->right);if(node->left)st.push(node->left);}return res;}

  再来看后序遍历,先序遍历是中左右,后续遍历是左右中,那么我们只需调整一下先序遍历的代码顺序,就变成中右左的遍历顺序,然后再反转res数组,输出的结果顺序就是左右中了,如下图:

所以后序遍历只需要前序遍历的代码稍作修改就可以了,代码如下:

vector<int> postorderTraversal(TreeNode* root) 
{stack<TreeNode*> st;	//栈vector<int> res;		//结果容器TreeNode* node;			//栈顶节点st.push(root);			//根节点入栈while(!st.empty()){node = st.top();	//获取栈顶节点并弹出st.pop();//中if(node != NULL)	//非空元素加入数组res.push_back(node->val);elsecontinue;//左右节点 先放左子树再放右子树入栈//出栈顺序: 中右左if(node->left)				st.push(node->left);if(node->right)st.push(node->right);}//将结果反转之后就是左右中的顺序了reverse(res.begin(), res.end());return res;
}

中序遍历

在刚刚在迭代的过程中,其实我们有两个操作:

  • 处理: 将元素放进result数组中
  • 访问: 遍历节点

  分析一下为什么刚刚写的前序遍历的代码,不能和中序遍历通用呢,因为前序遍历的顺序是中左右,先访问的元素是中间节点,要处理的元素也是中间节点,所以刚刚才能写出相对简洁的代码,因为要访问的元素和要处理的元素顺序是一致的,都是中间节点。
  那么再看看中序遍历,中序遍历是左中右,先访问的是二叉树顶部的节点,然后一层一层向下访问,直到到达树左面的最底部,再开始处理节点(也就是在把节点的数值放进result数组中),这就造成了处理顺序和访问顺序是不一致的。

  那么在使用迭代法写中序遍历,就需要借用指针的遍历来帮助访问节点,栈则用来处理节点上的元素。

中序遍历,可以写出如下代码:

//中序遍历
vector<int> middleorderTraversal(TreeNode* root) 
{stack<TreeNode*> st;	//栈 记录遍历过的节点vector<int> res;		//结果容器TreeNode* cur = root;	//遍历节点while(cur != NULL || (!st.empty())){if(cur != NULL)			// 指针来访问节点,访问到最底层{st.push(cur);		//访问指针入栈cur = cur->left;	//左}else					//到左端极限{cur = st.top();		//记录栈弹出节点st.pop();			res.push_back(cur->val);	//节点元素放进数组 中cur = cur->right;			//右}}return res;
}

二叉树的层序遍历(广度遍历)

学会二叉树的层序遍历,可以一口气打完以下十题:

  • 102.二叉树的层序遍历
  • 107.二叉树的层次遍历II
  • 199.二叉树的右视图
  • 637.二叉树的层平均值
  • 429.N叉树的层序遍历
  • 515.在每个树行中找最大值
  • 116.填充每个节点的下一个右侧节点指针
  • 117.填充每个节点的下一个右侧节点指针II
  • 104.二叉树的最大深度
  • 111.二叉树的最小深度

在这里插入图片描述

层序遍历的思想与实现

  层序遍历一个二叉树,就是从左到右一层一层的去遍历二叉树。需要借用一个辅助数据结构即队列来实现,队列先进先出,符合一层一层遍历的逻辑,而用栈先进后出适合模拟深度优先遍历也就是递归的逻辑。
  而这种层序遍历方式就是图论中的广度优先遍历,只不过我们应用在二叉树上。

使用队列实现二叉树广度优先遍历,动画如下:
在这里插入图片描述

  • 102.二叉树的层序遍历

代码如下:这份代码也可以作为二叉树层序遍历的模板,打十个就靠它了。

vector<vector<int>> levelOrder(TreeNode* root) 
{queue<TreeNode*> que;		//定义队列vector<vector<int>> res;	//结果容器TreeNode* node = root;		//当前节点int size = 0;//为空if(root != NULL)que.push(root);while(!que.empty())		{vector<int> vec;		//每一层的数组size = que.size();		//记录当前层节点数量//弹出当前层节点数量 并把下一层节点加入队列while(size--){node = que.front();vec.push_back(node->val);	//队列数据加入结果数组que.pop();//左右子树入队列if(node->left)que.push(node->left);if(node->right)que.push(node->right);}//更新队列大小size = que.size();//层数组加入结果容器res.push_back(vec);}return res;
}

层序遍历力扣十题分析

107.二叉树的层次遍历II
  给定一个二叉树,返回其节点值自底向上的层次遍历。 (即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历)

思路
  相对于102.二叉树的层序遍历,就是最后把结果result数组反转一下就可以了

 reverse(result.begin(), result.end()); 

199.二叉树的右视图

  给定一棵二叉树,想象自己站在它的右侧,按照从顶部到底部的顺序,返回从右侧所能看到的节点值。

思路
  层序遍历的时候,判断是否遍历到单层的最后面的元素,如果是,就放进result数组中,随后返回result就可以了。

while(!que.empty())		
{size = que.size();	//记录当前层节点数量while(size--)		//弹出当前层节点数量 并把下一层节点加入队列{node = que.front();if(size == 0)	//每一层最右侧节点数据加入结果数组res.push_back(node->val);	que.pop();	//左右子树入队列if(node->left)que.push(node->left);if(node->right)que.push(node->right);}size = que.size();	//更新队列大小
}

637.二叉树的层平均值
  给定一个非空二叉树, 返回一个由每层节点平均值组成的数组。
思路
  本题就是层序遍历的时候把一层求个总和在取一个均值。

while(!que.empty())		
{len = size = que.size();	//记录当前层节点数量while(size--)		//弹出当前层节点数量 并把下一层节点加入队列{node = que.front();sum += node->val;	que.pop();	//左右子树入队列if(node->left)que.push(node->left);if(node->right)que.push(node->right);}res.push_back(1.0 * sum / len);sum = 0;size = que.size();	//更新队列大小
}

429.N叉树的层序遍历

515.在每个树行中找最大值

116.填充每个节点的下一个右侧节点指针

117.填充每个节点的下一个右侧节点指针II

104.二叉树的最大深度

111.二叉树的最小深度

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

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

相关文章

数据库开启远程连接

服务器端添加一个允许远程连接的root用户: mysql -u root -p create user root192.168.10.20 identified by admin; //创建一个192.168.10.20地址远程连接的root用户 grant all privileges on *.* to root192.168.10.20; //赋予远程root用户所有的权…

中国地形可调节高度-UE5-UE4

2000坐标系&#xff0c;可进行高度调整。 支持版本4.21-5.4版本 下载位置&#xff1a;https://mbd.pub/o/bread/ZpWZm5Zs

教你解决PUBG绝地求生启动不了 登录不了 登录不进去的问题

尽管《绝地求生》&#xff08;PUBG&#xff09;凭借其紧张刺激的战术对抗与高度仿真的战场环境在全球范围内赢得了众多拥趸&#xff0c;但不少玩家在经历了一场紧张激烈的生命争夺赛后&#xff0c;却面临着一个不愉快的现实&#xff1a;游戏无法顺利启动或登录。这种紧随激烈战…

【docker】常用的把springboot打包为docker镜像的maven插件

Spring Boot Maven Plugin: Spring Boot 自带的 Maven 插件 (spring-boot-maven-plugin) 支持直接生成 Docker 镜像。通过配置&#xff0c;可以在 Maven 构建过程中自动构建 Docker 镜像&#xff0c;而无需单独编写 Dockerfile。这种方法简化了将应用打包为 Docker 镜像的过程。…

工业数据采集软件 高效的数字化信息管理系统

近年来&#xff0c;随着产线自动化程度的提升&#xff0c;越来越多的工业设备被运用到自动化生产中&#xff0c;产线中各位置所产生的数据也越来越多&#xff0c;每个设备又都是独立的&#xff0c;如何将其整合&#xff0c;进行系统化查看处理&#xff0c;就是工业数据采集软件…

05.线程

进程有哪些缺陷&#xff1f; 1.创建的代价较高 进程是OS进行资源分配的基本单位&#xff0c;也就是说创建进程是需要分配资源的&#xff0c;所以创建代价很高 2.通信不方便 进程之间要想进行通信必须借助第三方资源&#xff08;管道、内存映射、消息队列&#xff09; 线程的优…

大模型日报2024-05-10

大模型日报 2024-05-10 大模型资讯 阿里巴巴发布新AI语言模型Qwen2.5&#xff0c;超越OpenAI的GPT-4 摘要: 阿里巴巴集团旗下的阿里云推出了新型大型语言模型Qwen2.5。据称&#xff0c;在语言生成能力上超过了OpenAI的GPT-4&#xff0c;尽管在其他领域也有出色表现。该模型的发…

JAVA大量数据导出excel

背景&#xff1a;因项目需要导出3万行&#xff0c;90列的数据到excel&#xff0c;使用传统的apache poi 直接导出&#xff0c;导致504连接超时无法导出。然后改造方法&#xff0c;异步导出。 一、准备一个导出类&#xff0c;属性有id&#xff0c;outputstrream,finleName,err,e…

[debian12] wps for linux打开PDF卡死

原因 wps使用wpspdf处理PDF文件&#xff0c;而wpspdf依赖于libtiff5.so.5。而系统更新后&#xff0c;linux发行版提供的是libtiff.so.6或更新版本&#xff0c;导致其无法正常工作。 解决方案 理论上&#xff0c;安装libtiff5即可&#xff1a; apt install libtiff5.so.5 但实…

VM虚假机联网(无代码,超简单)NAT模式

1、左边顶上编辑里面最下面找到虚拟网络编辑器2.启用管理员特权3.重新创建一个NAT模式的网络&#xff08;名称随便一个&#xff09; 4.打开这两个设置里面的东西进行拍照并记住IP区间和网关&#xff0c;等下要用&#xff1b; 5.打开虚拟机&#xff0c;右上角&#xff0c;下标点…

腾讯云服务器部署前后端服务

服务器&#xff1a;OpenCloudOS &#xff08;兼容centos8&#xff09; 后端&#xff1a;javaSpringboot 前端&#xff1a;Vue 下载jdk 1&#xff09;下载jdk11 wget https://download.java.net/openjdk/jdk11/ri/openjdk-1128_linux-x64_bin.tar.gz 2&#xff09;解压jdk …

论文研读 Disentangled Information Bottleneck

解耦信息瓶颈 摘要&#xff1a; 信息瓶颈方法是一种从源随机变量中提取与预测目标随机变量相关的信息的技术&#xff0c;通常通过优化平衡压缩和预测项的IB拉格朗日乘子f来实现&#xff0c;然而拉格朗日乘子很难优化&#xff0c;需要多次实验来调整拉格朗日乘子的值&#xff0c…

使用leafletjs实现地图洋流、风场气象6要素地图标注、等值面图

前期实现的功能由于数据失效无法显示效果&#xff0c;今天重新对接一个数据源进行展示&#xff0c;实现效果如下图&#xff1a; 访问地址&#xff1a;可视化三维 GIS 特效 - 沉浸式视觉体验呈现令人惊叹的三维 GIS 特效&#xff0c;提供沉浸式视觉体验。https://www.wheart.cn/…

QAnything 在mac M2 上纯python环境安装使用体验(避坑指南)

这是一篇mac m2本地纯python环境安装 qanything的文章。安装并不顺利&#xff0c;官方提供的模型无法在本地跑。 这篇文章记录了&#xff0c;使用xinference来部署本地模型&#xff0c;并利用openAi的通用接口的方式&#xff0c;可以正常使用。 记录了遇到的所有的问题&#xf…

*****水上飞机:继承,虚函数,虚继承

一题目 请设计以下航行器、飞机、船、水上飞机等 4 个类。 CRAFT 为航行器类&#xff0c;是公共基类&#xff0c;提供航行器的基本特性。包括&#xff1a; 一个保护数据成员&#xff1a;speed(速度)。 三个公有成员函数&#xff1a;构造函数(初始化速度)、析构函数和 Show 函数…

AOSP开发

Android 开发者 | Android Developers (google.cn) android开源代码&#xff1a; Android 开源项目 | Android Open Source Project (google.cn)

FreeRTOS任务调度器

目录 1、什么是任务调度器 2、FreeRTOS中的任务调度器 2.1 抢占式调度 2.2 时间片调度 2.3 协作式调度 3、任务调度案例分析 3.1 实验需求 3.2 CubeMX配置 3.3 代码实现 3.3.1 uart.c 重定向printf 3.3.2 打开freertos.c并添加代码 3.3.4 代码现象 1、什么是任务调度…

Spring:spring-boot-starter-parent与spring-boot-dependencies的区别

参考&#xff1a;spring-boot-starter-parent与spring-boot-dependencies的区别

[uniapp] 配置ts类型声明

我想引进图片,但是报错 声明一下就行 TypeScript 支持 | uni-app官网 创建tsconfig.json文件,复制官网的配置 然后在随便一个目录下写一个随便名字的.d.ts文件 例如这样 保存就行 因为ts是默认扫描全部的,所以要按照官网的写法 把不必要的排除掉就行,免得浪费性能

JS-导入导出

export和export default是ES6中导出模块中变量的语法 导入导出变量 //导出方法&#xff08;js文件中&#xff09; export const 变量名值//导入方法 对应导入的变量&#xff0c;一定要加花括号 import {变量名} from js文件路径 导入导出函数 //导出方法&#xff08;js文件中…