【夜深人静学数据结构与算法 | 第四篇】手撕二叉树遍历

news2024/11/20 8:47:24

目录

前言:

二叉树遍历方式:

手撕前中后序遍历(递归)的三大准备

深度优先搜索: 

手撕前中后遍历(递归):

手撕前中后序遍历(迭代):

深度优先搜索:

总结:


前言:

        今天我们将带领大家手撕二叉树的遍历,本篇会分别讲解深度优先搜索法和广度优先有搜索法下的各自详细算法,大家做好准备了嘛?

二叉树遍历方式:

  • 深度优先遍历
  • 广度优先遍历

手撕前中后序遍历(递归)的三大准备

  • 确定递归函数的参数和返回值。
  • 确定终止条件。
  • 确定单层递归的逻辑。

深度优先搜索: 

手撕前中后遍历(递归):

        讲深度优先搜索遍历,实际上就是在讲前中后序遍历的方法,我们先用前序遍历进行讲解。 

1.确定递归函数的参数和返回值:我们就只传递一个节点以及存储遍历结果用的vector容器就可以了。

void traversal(TreeNode* cur, vector<int>& vec)

2.确定终止条件:在递归的时候,如果下层没有节点,那么就证明我们此时已经遍历到最底层了,就要返回,也就是如果这个节点为空节点,我们就要终止递归。

if (cur == NULL) return;

3.确定单层递归的逻辑:前序遍历是中左右的循序,所以在单层递归的逻辑,是要先取中节点的数值,代码如下:

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

单层递归的逻辑就是按照中左右的顺序来处理的,这样二叉树的前序遍历,基本就写完了。其实整体的逻辑还是比较简单的:
        我们最开始把 1 传递进去,此时节点不为空,说明 1 节点实际存在,就把 1 存储到数组里面,之后我们把1的左右子节点分别再传入进去,反复进行  1 节点经历的操作,循环往复就完成了整个树的遍历

完整核心代码: 

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

    vector<int> preorderTraversal(TreeNode* root) {
            vector<int>result;
            findallnodes(root,result);
            return result;
    }
};

 其实中序遍历和后序遍历的逻辑都和前序遍历一样,就是数据在存储的时候要有所改动

中序遍历:

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

后序遍历:

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

手撕前中后序遍历(迭代):

 迭代法实际上是用栈来模拟递归的过程。

前序遍历:

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;
    }
};

详细解释:

这是一个二叉树的前序遍历算法的实现。算法的核心思想是使用栈来模拟递归遍历,具体步骤如下:

  1. 新建一个空栈st和一个空向量result;
  2. 若二叉树根节点为空,直接返回结果result;
  3. 将二叉树根节点压入栈中;
  4. 取出当前栈顶元素node,并将node的值加入result向量中;
  5. 若node的右子节点不为空,将右子节点压入栈中;
  6. 若node的左子节点不为空,将左子节点压入栈中;
  7. 重复步骤(4)-(6),直到栈为空;
  8. 返回向量result。

在实现过程中需要注意的是,因为前序遍历的顺序是根节点-左子树-右子树,所以需要先将右子节点入栈,再将左子节点入栈,才能保证取出栈顶元素时先访问左子树。另外,注意判断节点是否为空,为空时不需要入栈遍历。

中序遍历:

class Solution {
public:
    vector<int> inorderTraversal(TreeNode* root) {
        vector<int> result;
        stack<TreeNode*> st;
        TreeNode* cur = root;
        while (cur != NULL || !st.empty()) {
            if (cur != NULL) { // 指针来访问节点,访问到最底层
                st.push(cur); // 将访问的节点放进栈
                cur = cur->left;                // 左
            } else {
                cur = st.top(); // 从栈里弹出的数据,就是要处理的数据(放进result数组里的数据)
                st.pop();
                result.push_back(cur->val);     // 中
                cur = cur->right;               // 右
            }
        }
        return result;
    }
};

这是一个二叉树的中序遍历算法的实现。该算法使用栈来模拟递归遍历,具体步骤如下:

        1. 新建一个空向量result和一个空栈st,同时初始化一个指针cur指向二叉树的根节点;
        2. 当当前指针cur不为空或栈st不为空时,执行下列操作:
           a. 若当前指针cur不为空,则将该节点加入栈st中,并将指针cur指向其左子节点,相当于递归遍历到左子树的最底层;
           b. 否则,即当前节点已经访问到最底层,从栈中将其弹出,并将其加入到结果向量result中,并将指针cur指向当前节点的右子节点,相当于返回到上一层节点继续遍历右子树;
        3. 重复步骤2,直到指针cur为空且栈st为空;
        4. 返回结果向量result。

在实现过程中,需要注意的是,由于中序遍历的顺序是左子树-根节点-右子树,所以需要先将整个左子树压入栈中,从栈中弹出的节点即为左子树最底层节点的父节点,并且该节点的左子树已经访问完毕,接下来需要访问该节点,并将指针cur指向右子节点,以此类推。另外,要注意判断节点是否为空,为空时不需要入栈和处理。

后序遍历:

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;
    }
};

这是一个二叉树的后序遍历算法的实现。该算法使用栈来模拟递归遍历,具体步骤如下:

  1. 新建一个空栈st和一个空向量result;
  2. 若二叉树根节点为空,直接返回结果result;
  3. 将二叉树根节点压入栈中;
  4. 取出当前栈顶元素node,并将node的值加入result向量中;
  5. 若node的左子节点不为空,将左子节点压入栈中;
  6. 若node的右子节点不为空,将右子节点压入栈中;
  7. 重复步骤(4)-(6),直到栈为空;
  8. 返回向量result,并将其反转,得到左右中的顺序。

在实现过程中需要注意的是,由于后序遍历的顺序是左子树-右子树-根节点,所以需要先访问左子节点和右子节点,最后再访问根节点,即将左子节点和右子节点的遍历顺序颠倒,然后将结果向量反转即可得到左右中的顺序。另外,需要注意判断节点是否为空,为空时不需要入栈遍历。

深度优先搜索:

层序遍历:
        就是把二叉树中的数据一层一层的保存。

迭代法实现:

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;
         
            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;
    }
};

这是一个二叉树的层序遍历算法的实现,即按照树的层次依次输出各节点的值。该算法使用队列来实现,具体步骤如下:

1. 新建一个空队列que,将根节点加入队列中;
2. 初始化一个空二维向量result,用于存储节点的值;
3. 当队列非空时,执行下列操作:
   a. 获取当前队列元素的个数size,表示当前层的节点个数;
   b. 新建一个空向量vec,用于存储当前层的节点值;
   c. 依次从队列中取出元素,并将其值加入到vec中,并将其左右子节点加入队列中;
   d. 将vec加入到result中;
4. 返回result向量,其中每个子向量表示一层的节点值。

在实现过程中,需要注意的是,在每一层遍历完成之后,需要将该层节点的值加入到二维向量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;
    }
};

 这是一个二叉树的层序遍历算法的实现,与上一个算法不同的是,该算法采用递归实现。具体步骤如下:

1. 新建一个空二维向量result、一个初始深度depth为0的变量,并将根节点root作为当前节点cur;
2. 递归遍历当前节点cur的左子树和右子树,同时传递当前层的result二维向量和深度depth+1;
3. 将当前节点cur的值加入到result中对应深度的子向量中;
4. 当result中没有深度为depth的子向量时,新建一个空向量并加入到result中;
5. 返回result向量即可。

在实现过程中,需要注意的是,每个节点所在的深度取决于它的父节点深度,因此在递归遍历时深度depth需要加1。递归结束条件为当前节点cur为空。因为在递归遍历左子树和右子树之前,需要先将当前节点加入到result中对应深度的子向量中,因此result的初始值应为空。

总结:

        本篇详细的介绍了手撕二叉树遍历的各种常见方式,只有熟练的掌握树的遍历方式,我们才可以更加熟练的使用各种树结构,狠狠的拿下数据结构与算法。
 

如果我的内容对你有帮助,请点赞,评论,收藏。创作不易,大家的支持就是我坚持下去的动力!

 

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

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

相关文章

经典Java面试题收集

1、面向对象的特征有哪些方面&#xff1f; 答&#xff1a;面向对象的特征主要有以下几个方面&#xff1a; 抽象&#xff1a;抽象是将一类对象的共同特征总结出来构造类的过程&#xff0c;包括数据抽象和行为抽象两方面。抽象只关注对象有哪些属性和行为&#xff0c;并不关注这…

EHCI接口概述(三)

EHCI主机接口寄存器在BAR0所示的PCI MEM地址空间中&#xff0c;主要包括两部分&#xff1a; 1&#xff09;能力寄存器组 2&#xff09;操作寄存器组 下面先介绍能力寄存器组 CAPLENGTH寄存器&#xff0c;8位只读寄存器&#xff0c;给出了控制寄存器组的偏移量。 HCIVERSION…

springboot+vue项目之CSGO赛事管理系统(java项目源码+文档)

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于springboot的CSGO赛事管理系统。项目源码以及部署相关请联系风歌&#xff0c;文末附上联系信息 。 &#x1f495;&#x1f495;作者&#xff1a;风…

C语言:求两个数二进制中不同位的个数

题目&#xff1a; 编程实现&#xff1a;两个int&#xff08;32位&#xff09;整数 m 和 n 的二进制表达中&#xff0c;有多少个位(bit)不同&#xff1f; 输入例子 : 1999 2299 输出例子 : 7 思路&#xff1a; 总体思路&#xff1a; 把 m 异或 n 后&#xff0c;有几个相异就有几…

抢占父亲节市场:2023年出海品牌的海外网红营销策略揭秘

随着社交媒体的迅猛发展和全球化的趋势&#xff0c;网红营销已经成为品牌推广的一种重要方式。在父亲节这个特殊的节日里&#xff0c;出海品牌可以通过巧妙利用网红的影响力来推动产品销售和品牌知名度的提升。本文Nox聚星将详细介绍如何通过海外网红营销来提升品牌知名度和销售…

HarmonyOS学习路之开发篇—Java UI框架(动画开发)

动画开发 动画是组件的基础特性之一&#xff0c;精心设计的动画使UI变化更直观&#xff0c;有助于改进应用程序的外观并改善用户体验。Java UI框架提供了帧动画、数值动画和属性动画&#xff0c;并提供了将多个动画同时操作的动画集合。 帧动画 帧动画是利用视觉暂留现象&…

[电离层建模学习笔记]开源程序M_GIM学习记录

[电离层建模学习笔记]开源程序M_GIM学习记录 文章目录 [电离层建模学习笔记]开源程序M_GIM学习记录1. 程序相关信息2. 程序学习记录2.1 采用的数据说明2.2 程序运行前2.3 程序运行结果 3. 其他 1. 程序相关信息 开源程序M_GIM基于Matlab(Zhou et al., 2023)&#xff0c;用于实…

js数组高阶函数——includes()方法

js数组高阶函数——includes方法 前言数组的一般化操作创建数组获取数组长度访问&#xff08;遍历&#xff09;数组元素修改数组元素删除数组元素数组尾部添加数组尾部删除 includes&#xff08;&#xff09;方法举例说明关键点 前言 ⭐JS的数组是一种特殊的对象&#xff0c;其…

SSH通过VSCode远程访问服务器Opencv和matplotlib等无法直接显示图像问题

需求描述&#xff1a; 在VSCode中通过SSH连接服务器&#xff0c;使用cv2.imshow或plt.show()无法显示图像。 解决思路如下&#xff1a; 1、首先查看与服务器之间的网络连接问题&#xff08;百分之九十问题就是出在第一步骤&#xff0c;哈哈哈&#xff09; 在本地端打开cmd&…

「案例」95后占半壁江山的浙桂,如何在百家争鸣中快人一步

如果用一个历史时期来形容目前国内单光子雪崩二极管&#xff08;SPAD&#xff09;传感器芯片的市场格局&#xff0c;那就是——春秋。 各家IC设计公司百家争鸣&#xff0c;而浙桂半导体就是其中的“百分之一”。 浙桂半导体两大特点 一、浙桂研发SPAD传感器芯片需要召唤像元、…

C语言实现字符串的模式匹配

一.模式匹配 字符串的模式匹配算法是用来查找一个字符串中是否存在另一个指定的字符串&#xff08;即模式&#xff09;的算法。常见的模式匹配算法包括暴力匹配算法、KMP算法、Boyer-Moore算法和Rabin-Karp算法。 暴力匹配算法&#xff1a;暴力匹配算法也称为朴素匹配算法&am…

自学黑客!一般人我劝你还是算了吧

一、自学网络安全学习的误区和陷阱 1.不要试图以编程为基础的学习开始学习 我在之前的回答中&#xff0c;我都一再强调不要以编程为基础再开始学习网络安全&#xff0c;一般来说&#xff0c;学习编程不但学习周期长&#xff0c;而且实际向安全过渡后可用到的关键知识并不多 一…

【Java之JAR包解析】(三)除核心包 rt.jar之外的其他JAR包~

JAR包解析之其他jar包 前言:one: access-bridge-64.jar:two: charsets.jar:three: cldrdata.jar:four: deploy.jar:five: jce.jar:six: jfr.jar:seven: jfxrt.jar:eight: jfxswt.jar:nine: jsse.jar:keycap_ten: localedata.jar11、management-agent.jar12、nashorn.jar13、plu…

开发人员Git仓库提交与合并

参考&#xff1a;git 的变基(rebase)和合并(merge)具体有什么分别阿&#xff1f; - 知乎 1、Git工作流 在使用Git Flow工作模式时&#xff0c;业界普遍遵循的规则&#xff1a; 所有开发分支从develop分支拉取。所有hotfix分支从master分支拉取。所有在master分支上的提交都必…

flstudio21.0.3中文版水果软件下载

FL Studio就是国人众所熟知的水果编曲软件&#xff0c;圈内用户习惯叫它“水果”。它是一个全能音乐制作环境或数字音频工作站&#xff08;DAW&#xff09;。FL Studio可以进行编曲、剪辑、录音、混音&#xff0c;让你的电脑变成全功能录音室&#xff0c;帮助你制作出属于自己的…

轻量服务器架设网站打开速度慢,如何加速?

轻量服务器非常适合流量适中的小、中型网站&#xff0c;虽作为轻量级主机包&#xff0c;但它一般与云服务器使用同样的 CPU、内存、硬盘等底层资源。只是&#xff0c;轻量服务器的资源(可用的存储空间、RAM 和 CPU等硬件/内存容量)更低&#xff0c;虽然这些对于较中、小的网站来…

GEN回零调试

一.根据motionstudio软件检测各部件完备&#xff1b; 二.调试点位模式的CPP测试程序 其中&#xff0c;配置文件如下&#xff1a; 回零相关&#xff08;就是轴状态同步&#xff09;&#xff1a; 下面是相关代码: // 例程 7-1 点位运动 //#include "stdafx.h" #inclu…

selenium自动化的时候网址重定向问题的解决思路

一、背景 因为我们系统是用企业微信扫码登录的&#xff0c;就输入网址 management-xxx.xxx.com以后&#xff0c;url就会重定向到企业微信授权的url &#xff1a;https://open.work.weixin.qq.com/wwopen/sso/3rd_qrConnect?statexxx&redirect_urimanagement-xxx.xxx.com …

如何制作数据可视化、数孪、安防、区域人流量识别+控制的项目?

制作与数据可视化、数字孪生、安防、区域人群识别和控制以及其他类似计划相关的项目需要仔细规划和执行。建议遵循以下通用框架来有效地开发这些项目&#xff1a; 定义项目目标&#xff1a;清楚地阐明项目目的和目标。确定要解决的具体问题、期望的结果以及衡量成功的关键绩效指…

vue3+ts+vite+electron打包exe

文章目录 一. 前言二. 准备写好的vue项目打包2.1 修改ts打包代码检测.这个比较烦人. 在package.json中 2.2 配置打包参数2.3 打包vue 三. 打包exe3.1 拉取electron官方demo3.2 下载打包插件3.3 在electron-quick-start项目中找到入口文件 main.js &#xff0c;修改打包的文件路…