《代码随想录》专题:二叉树的遍历

news2024/12/23 20:30:47

0、二叉树的遍历方式总结

  • 二叉树主要有两种遍历方式
    • 深度优先遍历:先往深走,遇到叶子节点再往回走。
    • 广度优先遍历:一层一层的去遍历。
  • 从深度优先遍历和广度优先遍历进一步拓展,才有如下遍历方式
    • 深度优先遍历
      • 前序遍历(递归法,迭代法)
      • 中序遍历(递归法,迭代法(最好使用统一迭代法))
      • 后序遍历(递归法,迭代法)
    • 广度优先遍历
      • 层次遍历(递归法(少见),迭代法)
  • 深度优先遍历中的前、中和后指的是中间结点的遍历顺序
    在这里插入图片描述
  • 其他
    • 经常会使用递归的方式来实现深度优先遍历,也就是实现前中后序遍历,使用递归是比较方便的。
    • 栈其实就是递归的一种实现结构,也就说前中后序遍历的逻辑其实都是可以借助使用非递归的方式来实现的。
    • 广度优先遍历的实现一般使用队列来实现,这也是队列先进先出的特点所决定的,因为需要先进先出的结构,才能一层一层的来遍历二叉树。

1、二叉树的递归遍历

  • 前序递归遍历
    class Solution {
    public:
        void traversal(TreeNode* cur, vector<int>& vec) {
            if (cur == NULL) return;
            vec.push_back(cur->val);    // 中
            traversal(cur->left, vec);  // 左
            traversal(cur->right, vec); // 右
        }
        vector<int> preorderTraversal(TreeNode* root) {
            vector<int> result;
            traversal(root, result);
            return result;
        }
    };
    
    有了前序遍历,中序遍历和后序遍历只是更换了一下遍历的顺序
  • 中序递归遍历
    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);    // 中
    }
    

2、二叉树的迭代遍历

  • 前序迭代遍历
    前序遍历是中左右,每次先处理的是中间节点,那么先将根节点放入栈中,然后将右孩子加入栈,再加入左孩子。
    请添加图片描述

    class Solution {
    public:
        vector<int> preorderTraversal(TreeNode* root) {
            stack<TreeNode*> st;
            vector<int> result;
            if (root == NULL) return result;
            st.push(root);
            while (!st.empty()) {
                TreeNode* node = st.top();                       // 中
                st.pop();
                result.push_back(node->val);
                if (node->right) st.push(node->right);           // 右(空节点不入栈)
                if (node->left) st.push(node->left);             // 左(空节点不入栈)
            }
            return result;
        }
    };
    
  • 后序迭代遍历
    先序遍历是中左右,后续遍历是左右中,那么我们只需要调整一下先序遍历的代码顺序,就变成中右左的遍历顺序,然后再反转result数组,输出的结果顺序就是左右中了,如下图
    在这里插入图片描述

    class Solution {
    public:
        vector<int> postorderTraversal(TreeNode* root) {
            stack<TreeNode*> st;
            vector<int> result;
            if (root == NULL) return result;
            st.push(root);
            while (!st.empty()) {
                TreeNode* node = st.top();
                st.pop();
                result.push_back(node->val);
                if (node->left) st.push(node->left); // 相对于前序遍历,这更改一下入栈顺序 (空节点不入栈)
                if (node->right) st.push(node->right); // 空节点不入栈
            }
            reverse(result.begin(), result.end()); // 将结果反转之后就是左右中的顺序了
            return result;
        }
    };
    
  • 中序迭代遍历
    思索一下,中序迭代遍历完全不能用上面的套路,所以如果考到中序的话建议可以使用二叉树的统一迭代法,使用二叉树的统一迭代法是非常方便的。

3、二叉树的统一迭代法

  • 以中序遍历为例,提到说使用栈的话,无法同时解决访问节点(遍历节点)和处理节点(将元素放进结果集)不一致的情况。那我们就将访问的节点放入栈中,把要处理的节点也放入栈中但是要做标记。标记的方法:就是要处理的节点放入栈之后,紧接着放入一个空指针作为标记。这种方法称为标记法

  • 中序统一迭代遍历
    在这里插入图片描述

    class Solution {
    public:
        vector<int> inorderTraversal(TreeNode* root) {
            vector<int> result;
            stack<TreeNode*> st;
            if (root != NULL) st.push(root);
            while (!st.empty()) {
                TreeNode* node = st.top();
                if (node != NULL) {
                    st.pop(); // 将该节点弹出,避免重复操作,下面再将右中左节点添加到栈中
                    if (node->right) st.push(node->right);  // 添加右节点(空节点不入栈)
    
                    st.push(node);                          // 添加中节点
                    st.push(NULL); // 中节点访问过,但是还没有处理,加入空节点做为标记。
    
                    if (node->left) st.push(node->left);    // 添加左节点(空节点不入栈)
                } else { // 只有遇到空节点的时候,才将下一个节点放进结果集
                    st.pop();           // 将空节点弹出
                    node = st.top();    // 重新取出栈中元素
                    st.pop();
                    result.push_back(node->val); // 加入到结果集
                }
            }
            return result;
        }
    };
    

    有了这份代码,前序代码和后序代码只是修改其中的遍历顺序

  • 前序统一迭代遍历

    class Solution {
    public:
        vector<int> preorderTraversal(TreeNode* root) {
                	...
        	        ...
                    st.pop();
                    if (node->right) st.push(node->right);  // 右
                    if (node->left) st.push(node->left);    // 左
                    
                    st.push(node);                          // 中
                    st.push(NULL);
        	        ...
                	...
        }
    };
    
  • 后序统一迭代遍历

    class Solution {
    public:
        vector<int> postorderTraversal(TreeNode* root) {
            	    ...
    	            ...
                    st.pop();
                    st.push(node);                          // 中
                    st.push(NULL);
    
                    if (node->right) st.push(node->right);  // 右
                    if (node->left) st.push(node->left);    // 左
            	    ...
    	            ...
        }
    };
    

4、层序遍历

  • 层序遍历需要借用一个辅助数据结构即队列来实现,队列先进先出,符合一层一层遍历的逻辑,而用栈先进后出适合模拟深度优先遍历也就是递归的逻辑。而这种层序遍历方式就是图论中的广度优先遍历,只不过我们应用在二叉树上请添加图片描述
  • 迭代法
    class Solution {
    public:
        vector<vector<int>> levelOrder(TreeNode* root) {
            queue<TreeNode*> que;
            if (root != NULL) que.push(root);
            vector<vector<int>> result;
            while (!que.empty()) {
                int size = que.size();
                vector<int> vec;
                // 这里一定要使用固定大小size,不要使用que.size(),因为que.size是不断变化的
                for (int i = 0; i < size; i++) {
                    TreeNode* node = que.front();
                    que.pop();
                    vec.push_back(node->val);
                    if (node->left) que.push(node->left);
                    if (node->right) que.push(node->right);
                }
                result.push_back(vec);
            }
            return result;
        }
    };
    
  • 递归法(这种似乎比较少见)
    # 递归法
    class Solution {
    public:
        void order(TreeNode* cur, vector<vector<int>>& result, int depth)
        {
            if (cur == nullptr) return;
            if (result.size() == depth) result.push_back(vector<int>());
            result[depth].push_back(cur->val);
            order(cur->left, result, depth + 1);
            order(cur->right, result, depth + 1);
        }
        vector<vector<int>> levelOrder(TreeNode* root) {
            vector<vector<int>> result;
            int depth = 0;
            order(root, result, depth);
            return result;
        }
    };
    

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

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

相关文章

【网络编程】网络编程核心概念与模式

配套环境 vscode、gcc、ubuntu22.04 概念描述 网络编程的本质 使用操作系统提供的接口函数&#xff0c;使得应用程序具备收发网络数据的能力。 网络接口在代码层面是操作系统提供的函数 应用程序通过网络接口使用操作系统的联网能力 网络编程核心概念 协议&#xff1a;为进…

STM32——led灯的点亮+闪烁+流水灯的实现

文章目录 一、LED点亮1.原理2.寄存器方式--代码3.库函数--代码4.结果展示 二、LED闪烁三、LED流水灯1.代码2.结果展示 一、LED点亮 1.原理 其方式有两种一种是寄存器方式一种是库函数方式&#xff0c;但其原理都是一样的。如原理图所示&#xff0c;与LED相连接的IO口置低电平…

HTML学习 第一部分(前端学习)

参考学习网站: 网页简介 (w3schools.com) 我的学习思路是&#xff1a;网站实践视频。 视频很重要的&#xff0c;因为它会给你一种开阔思路的方式。你会想&#xff0c;噢&#xff01;原来还可以这样。这是书本或者网站教程 所不能教给你的。而且&#xff0c;对一些教程&#…

【深度解析】OSPF-开放式最短路径优先协议

目录 重点&#xff1a; VPN---Virtual Private Network(虚拟专用网络隧道) OSPF网络类型&#xff1a; 表格视图&#xff1a; P2P---点对点协议 BMA---广播多路访问网络 NBMA---非广播多路访问网络 P2MP---点对多点协议 OSPF认证&#xff1a;检测类型&#xff0b;密码 …

陪诊师接单app开发|陪诊小程序开发|陪诊师接单系统开发

随着人们健康意识的提高和医疗服务的不断完善&#xff0c;陪诊服务逐渐受到人们的关注和需求。为了满足顾客的需求&#xff0c;陪诊小程序应运而生。下面将介绍陪诊小程序开发的几个优势。   1. 提供方便的预约和服务体验   陪诊小程序可以让顾客随时随地通过手机预约陪诊服…

Java034——反射(Reflection)

一、Java中的反射及作用 Java的反射(reflection)机制是指在程序的运行状态中&#xff1a; 可以构造任意一个类的对象&#xff0c;可以了解任意一个对象所属的类&#xff0c;可以了解任意一个类的成员变量和方法&#xff0c;可以调用任意一个对象的属性和方法。 一句话&#…

3.类与对象

Java作为一种面向对象语言。支持以下基本概念&#xff1a; 多态继承封装抽象类对象实例方法重载 1.类与对象 对象&#xff1a;对象是类的一个实例&#xff0c;有状态和行为 类&#xff1a;类是一个模板&#xff0c;它描述一类对象的行为和状态 public class Dog {String bree…

编程语言里的转义字符真的叫转义字符吗?

我们先来看看别人是怎么说转义字符的&#xff1f; 这是我随便从两本书上截图来的&#xff0c;总的来说就是反斜杠加几个字符就是转义字符。 就问你晕不晕&#xff0c;斜杠加几个字符至少是两个字符&#xff0c;那还叫字符吗&#xff0c;那叫字符串吧&#xff0c;怎么取名转义字…

ModaHub魔搭社区:AI原生云向量数据库Zilliz Cloud与 LlamaIndex 集成搭建文档问答系统

目录 准备工作 准备数据 主要参数 处理数据 开始提问 本文将演示如何与集成 LlamaIndex 从指定源获取信息。 在 ChatGPT 占领各大头条时,越来越多的企业在考虑如何在它们的产品中使用 ChatGPT。一个比较大的使用场景就是如何使用 ChatGPT 来改造产品文档孱弱的搜索能力。…

【板栗糖GIS】——通过插件调教网页版b站首页

【板栗糖GIS】——通过插件调教网页版b站首页 参考视频&#xff1a;利用adblock关掉热搜和搜索推荐_哔哩哔哩_bilibili 在edge浏览器上使用的插件为Adblockplus&#xff0c;可以在插件中心查询到 拦截元素如下&#xff1a; bilibili.com##.adblock-tipsbilibili.com##.trend…

2023/7/15总结

JWT 在写项目的时候&#xff0c;本来想着把用户的数据全部存入session这样的话就不用每次都需要带用客户端的账号。后面了解到JWT&#xff0c;这个是在服务器无状态的一个方式。 具体是分为三个部分&#xff0c;第一个是头部主要存储用到的算法等信息&#xff0c;第二个是载荷…

51单片机--DS1302时钟

文章目录 DS1302引脚定义和应用电路内部结构框图寄存器的定义时序定义BCD码DS1302时钟代码 DS1302 DS1302是美国DALLAS公司推出的一款实时时钟电路芯片。它具有高性能和低功耗的特点&#xff0c;可以通过SPI三线接口与CPU进行同步通信。DS1302能够提供秒、分、时、日、星期、月…

HTML基础教程

1 什么是HTML HTML 是用来描述网页的一种语言。HTML 是一种在 Web 上使用的通用标记语言。HTML 允许你格式化文本&#xff0c;添加图片&#xff0c;创建链接、输入表单、框架和表格等等&#xff0c;并可将之存为文本文件&#xff0c;浏览器即可读取和显示。 HTML 指的是超文本…

复用cmake代码的其他方法

复用cmake代码的其他方法 函数和宏是代码复用的方法,在cmake3.18版本开始,cmake添加了cmake_language()命令,通过这个命令我们可以直接调用任意的cmake代码,无需将这些可复用的代码使用函数或者宏包起来. 当然cmake_language()命令不是为了代替函数和宏而设计的,而是希望通过…

ROS话题通信自定义msg

从21年至今写过不下10个ROS话题通信的工程&#xff0c;今天系统地记录下自定义msg的过程&#xff0c;让后来者少走弯路。 1、自定义msg简介 在 ROS 通信协议中&#xff0c;数据载体是一个较为重要组成部分&#xff0c;ROS 中通过 std_msgs 封装了一些原生的数据类型,比如:Stri…

redis7单节点、主从、哨兵、集群的安装和配置

redis7安装和配置 2022年4月份正式发布的redis 7.0&#xff0c;是目前历史上变化最大的版本&#xff0c;有超过50个以上的新增命令 官网&#xff1a; https://redis.io中文镜像 http://redis.cn中文学习网&#xff1a; https://redis.com.cn/redis版本&#xff0c;建议升级…

Spring初识(一)

一.Spring 是什么&#xff1f; 首先我们来看看官网的解释 Spring 使每个人都可以更快、更轻松、更安全地进行 Java 编程。Spring 对速度、简单性和生产力的关注使其成为 世界上最受欢迎的 Java框架。 这里我简单的说明一下什么是spring? 我们通常所说的 Spring 指的是 Sprin…

纯CSS实现的卡片切换效果

纯CSS实现的卡片切换效果 无需JS就可以实现限于纯静态页面产品展示不需要轮播,自动切换 示例代码 <template><div class"example-css-tab"><div class"container dwo"><div class"card"><input type"radio"…

(续2)选择屏幕

分页签组件 表单控件 双击空白处----出现右侧编辑框 其中 fixed的意思是固定几列。 可修改 回车会自动复原 回车会自动复原 原因 在pai中检查字段却没有做任何操作。 打算新建一个表单 表单中指定选择行. 按钮扩展. 执行后 修改列名. 创建一个moudle 修改后不会复原. …

前端卷算法系列(七)

前端卷算法系列&#xff08;六&#xff09; 删除有序数组中的重复项 给你一个 升序排列 的数组 nums &#xff0c;请你 原地 删除重复出现的元素&#xff0c;使每个元素 只出现一次 &#xff0c;返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然后返回 nums 中唯…