【LeetCode 算法专题突破】二叉树的深度优先遍历(⭐)

news2024/10/7 0:03:51

文章目录

  • 前言
  • 1. 二叉树的前序遍历
    • 题目描述
    • 代码
  • 2. 二叉树的中序遍历
    • 题目描述
    • 代码
  • 3. 二叉树的后序遍历
    • 题目描述
    • 代码
  • 4. 前序遍历的非递归实现
    • 代码与思路
  • 5. 中序遍历的非递归实现
    • 代码与思路
  • 6. 后序遍历的非递归实现
    • 代码与思路
  • 总结

前言

接下来我要开始攻克二叉树这一个大难题了,我打算把二叉树分成四个部分进行总结:

  1. 二叉树的深度优先遍历
  2. 二叉树的广度优先遍历(也叫层序遍历)
  3. 二叉树的基本属性求解
  4. 二叉树其他相关问题(删改、求公共祖先、二叉搜索树等等)

那我也不废话了,直接开始。

1. 二叉树的前序遍历

接下来,我们就将二叉树的前中后序的递归遍历都做一遍,然后再分别将这四种遍历的迭代实现方法也做一遍,基础不牢,地动山摇,我们慢慢来,一步一个脚印学好二叉树!

题目链接:144. 二叉树的前序遍历

题目描述


所谓的前中后序遍历,在前中后的是什么?其实就是根节点,所以我一般习惯用一个口诀来记住前中后序的遍历顺序:

  1. 前序就是:根左右
  2. 中序就是:左根右
  3. 后序就是:左右根

这其实就是按照根在遍历中的顺序划分的遍历方式。来看代码:

代码

/**
 * Definition for a binary tree node.
 * type TreeNode struct {
 *     Val int
 *     Left *TreeNode
 *     Right *TreeNode
 * }
 */
func preorderTraversal(root *TreeNode) (res []int) {
    var preorder func(node *TreeNode)
    preorder = func(node *TreeNode) {
        if node == nil {
            return
        }
        res = append(res, node.Val) // 根
        preorder(node.Left)  // 左
        preorder(node.Right) // 右
    }
    preorder(root) // 调用前序遍历
    return res
}

我们来看这个代码,其实这就是一个简单的递归遍历的函数,所谓的 “根左右” 其实就是先把 “根” 位置的值给塞进 res 数组而已。

2. 二叉树的中序遍历

那就继续中序遍历

题目链接:94. 二叉树的中序遍历

题目描述


其实刚刚已经介绍过前中后序是怎么样的了,所以我们直接看代码:

代码

/**
 * Definition for a binary tree node.
 * type TreeNode struct {
 *     Val int
 *     Left *TreeNode
 *     Right *TreeNode
 * }
 */
func inorderTraversal(root *TreeNode) (res []int) {
    var inorder func(node *TreeNode)
    inorder = func(node *TreeNode) {
        if node == nil {
            return 
        }
        inorder(node.Left)
        res = append(res, node.Val)
        inorder(node.Right)
    }
    inorder(root)
    return res
}

这里我注释也懒得打了,可以发现他其实比起前序遍历就只是改了一个地方的代码,就是把 根 的位置和向左递归的位置换了一下,这就是中序遍历的遍历方法。

3. 二叉树的后序遍历

一做肯定是得做全套滴

题目链接:145. 二叉树的后序遍历

题目描述


其实现在题目描述也没有什么意义了,知道是后序遍历,我们直接写代码就完了:

代码

/**
 * Definition for a binary tree node.
 * type TreeNode struct {
 *     Val int
 *     Left *TreeNode
 *     Right *TreeNode
 * }
 */
func postorderTraversal(root *TreeNode) (res []int) {
    var postorder func(node *TreeNode) 
    postorder = func(node *TreeNode) {
        if node == nil {
            return 
        }
        postorder(node.Left)
        postorder(node.Right)
        res = append(res, node.Val)
    } 
    postorder(root)
    return res
}

没有意外,也是朴实无华的改个代码的位置。

现在开胃小菜算是做完了,像前中后这样的遍历其实并没有什么难度,所以我们还需要挑战一下前中后序的非递归遍历,不然不能算是真正学会了这三种遍历的方式。既是锻炼我们的代码能力,也是让我们熟悉二叉树的基本遍历方式,打牢基础,后面才不会越看越懵逼。

4. 前序遍历的非递归实现

这里我就不把题目描述给放出来了,这里我们用的就是第一题

代码与思路

/**
 * Definition for a binary tree node.
 * type TreeNode struct {
 *     Val int
 *     Left *TreeNode
 *     Right *TreeNode
 * }
 */
func preorderTraversal(root *TreeNode) (ans []int) {
    if root == nil {
        return
    }
    st := list.New()
    st.PushBack(root)
    for st.Len() > 0 {
        node := st.Remove(st.Back()).(*TreeNode)
        ans = append(ans, node.Val)
        if node.Right != nil {
            st.PushBack(node.Right)
        }
        if node.Left != nil {
            st.PushBack(node.Left)
        }
    }
    return ans
}

我来讲讲使用迭代法求解的一个流程,采取的是用一个栈来模拟递归的方法(这里我采用的是 go 语言的 list 来模拟栈操作),模拟前序遍历 “根左右” 的遍历方式

  1. 首先将根(或者说当前走到的节点)的值存入数组
  2. 然后分别将右节点和左节点入栈,因为出栈的顺序是相反的,所以我们入栈的时候就要这样先入右节点再入左节点,这样出栈遍历的时候就能达成 “根左右” 的遍历方式了

其实核心的步骤就是这两步,那我们继续实现中序的非递归遍历

5. 中序遍历的非递归实现

代码与思路

/**
 * Definition for a binary tree node.
 * type TreeNode struct {
 *     Val int
 *     Left *TreeNode
 *     Right *TreeNode
 * }
 */
func inorderTraversal(root *TreeNode) (ans []int) {
    st := list.New()
    cur := root
    for cur != nil || st.Len() > 0 {
        if cur != nil {
            st.PushBack(cur)
            cur = cur.Left
        } else {
            cur = st.Remove(st.Back()).(*TreeNode)
            ans = append(ans, cur.Val)
            cur = cur.Right
        }
    }
    return ans
}

前序遍历的时候,我们就是不断计算根位置的值,然后只管用栈按顺序遍历二叉树就行了,但是到了中序遍历,我们需要取的是左节点的值,所以我们就需要在左节点遍历的时候边遍历边取值,我们来根据代码梳理一下他的流程

  1. 将当前节点入栈,然后一直往左遍历直到走到 nil
  2. 走到 nil 之后,上一个节点,也就是栈顶的元素就是最左节点,输出到 ans
  3. 然后往右遍历一步,持续这个三步循环

这三步模拟的就是 “左根右” 的遍历方式,刚好也是三步,找到最左,取根,找右,循环往复,就能完成中序遍历了。迭代算法确实不太好想,但是上手模拟一遍还是比较好理解的。

6. 后序遍历的非递归实现

代码与思路

/**
 * Definition for a binary tree node.
 * type TreeNode struct {
 *     Val int
 *     Left *TreeNode
 *     Right *TreeNode
 * }
 */
func postorderTraversal(root *TreeNode) (ans []int) {
    if root == nil {
        return
    }
    st := list.New()
    st.PushBack(root)
    for st.Len() > 0 {
        node := st.Remove(st.Back()).(*TreeNode)
        ans = append(ans, node.Val)
        if node.Left != nil {
            st.PushBack(node.Left)
        }
        if node.Right != nil {
            st.PushBack(node.Right)
        }
    }
    reverse(ans)
    return ans
}

func reverse(a []int) { // 反转数组
    l, r := 0, len(a)-1
    for l < r {
        a[l], a[r] = a[r], a[l]
        l, r = l+1, r-1
    }
} 

后序遍历有一个非常巧妙的解法:因为前序遍历是 “根左右”,后续遍历是 “左右根”,如果我们根据 “根右左” 的顺序来进行遍历,然后再将结果进行反转就能将 “根右左” -> “左右根”,这样就完成了后续遍历,也就是我们只需要修改一下前序遍历的代码即可

就那上述代码来说,我将遍历左右的顺序进行了调换,然后实现了一个 reverse 反转数组(go 语言没有 C++ 那样提供 STL,难受)按照刚刚分析出来的思路实现后序遍历。

总结

基础不牢,地动山摇,把二叉树的前中后序学会,我们下一节挑战二叉树的广度优先遍历,又或者说,二叉树的层序遍历。

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

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

相关文章

Mac 配置环境变量

Mac 配置环境变量 修改配置文件 vim ~/.bash_profile i进入编辑模式. Esc&#xff1a;wq 保存文件 esc:q 退出 如&#xff1a;jdk环境变量配置 JAVA_HOME/Library/Java/JavaVirtualMachines/jdk1.8.0_361.jdk/Contents/Home CLASSPATH J A V A H O M E / l i b / t o o l…

SAP SPAD新建打印纸张

SAP SPAD新建打印纸张 1.事务代码SPAD 2.完全管理&#xff0d;设备类型&#xff0d;页格式-显示(创建格式页) 3.按标准A4纸张为模板参考创建。同一个纸张纵向/横向各创建1次(创建格式页) 4.完全管理&#xff0d;设备类型&#xff0d;格式类型-显示(创建格式类型&#xff0…

PID控制示例

PID控制简单示例 import numpy as np import matplotlib.pyplot as plt import copy# 定义曲线函数 y sin(x) def target_curve(x):return np.sin(x)class PID:def __init__(self, kp, ki, kd):self.kp kpself.ki kiself.kd kdself.ep 0.0self.ei 0.0self.ed 0.0self.d…

个人服务器怎么搭建?个人服务器搭建方法

​  个人服务器是指一台由个人拥有和管理的服务器&#xff0c;用于存储和提供个人网站、应用程序或其他在线服务。搭建个人服务器可以让我们更好地掌控自己的数据和网络资源。下面介绍一种常见的个人服务器搭建方法。 第一步&#xff1a;选择合适的硬件 我们需要选择一台适合…

【自用】英语一新题型每年归类

图片出自马天艺老师视频课。

数据存储新难题:“面粉”又涨价,“面包”怎么卖?

文 | 智能相对论 作者 | 叶远风 存储介质的价格要压不住了。 在减产策略执行数月后&#xff0c;三星、美光、SK海力士等存储介质巨头纷纷“扬言”要涨价&#xff0c;其中三星计划在今年四季度起调整NAND Flash产品的合约价格&#xff0c;涨幅超过10%。 “减产、削供、提价”…

lvsDR模式

LVS-DR模式 是最常用的LVS负载方式 直接路由模式 Lvs调度器 只负责请求和转发 转发到真实服务器 但是响应结果 由后端服务器直接转发给客户端 不需要经过调度器处理 可以减轻Lvs调度器的负担 提高系统性能和稳定性 工作原理&#xff1a; 客户端发送请求到vipLVS调度器接受请求…

mulesoft开发支撑

mulesoft开发支撑 开发支撑1. raml语法说明2. dataweave在线测试平台3. dataweave基础语法4. dataweave官方指南 感 开发支撑 1. raml语法说明 点击跳转 raml-10.md 重点看下面这部分内容&#xff0c;对raml语法做了详细说明和举例。 2. dataweave在线测试平台 点击跳转 d…

服务器感染了.mxdown-V-XXXXXXXX勒索病毒,如何确保数据文件完整恢复?

尊敬的读者&#xff1a; 在当今数字化时代&#xff0c;网络犯罪的阴谋在虚拟世界中蔓延&#xff0c;mxdown-V-XXXXXXXX、.vollhavhelp-V-XXXXXXXX、.arricklu-V-XXXXXXXX勒索病毒是其中的恶梦般存在&#xff0c;这三种勒索病毒均属于同一个勒索病毒家族旗下。这种勒索病毒以其…

【C/C++】引用的本质是指针常量

在c内部&#xff0c;**引用的本质是一个指针常量&#xff0c;**如 int * const ref &a。引用一旦初始化后&#xff0c;就不可以改变指向。 指针常量&#xff0c;int * const ref &a; const修饰的是 "ref "–> , ref 的值不可以改&#xff0c;即指针变量…

vue3中使用vue-i18n

1.先在项目中下载vue-i18n npm install vue-i18n9.6.2 2.1 .为了结构清晰&#xff0c;在src目录下创建一个lang文件夹 2.2 . 在lang文件夹分别下创建两个文件&#xff1a;en.js / zh.js代表两种语言 en.js: export default {message: {hello: hello,world,btn:modif…

国外怎么传大文件到国内,这款传输软件跨国企业必备

从国外传输文件到国内&#xff0c;这项任务常常充满了挑战。国际之间的距离、网络延迟、数据安全和文件大小限制等问题使得这个过程异常复杂。本文将深入剖析这些挑战&#xff0c;并说明一款优秀的跨国传输软件&#xff0c;如何能够成为解决这些问题的强有力工具。 国外传输文件…

暴力递归转动态规划(十一)

题目1&#xff1a; 这篇帖子中有多道题&#xff0c;由浅入深。 arr是货币数组&#xff0c;其中的值都是正数。再给定一个正数aim。每个值都认为是一张货币&#xff0c;即便是值相同的货币也认为每一张都是不同的&#xff0c;返回组成aim的方法数。 例如&#xff1a;arr {1,1,1…

企业文件防泄密方法

企业文件防泄密方法 安企神数据防泄密系统下载使用 企业文件是企业的核心资产&#xff0c;其中可能包含大量的敏感信息&#xff0c;如客户资料、产品配方、财务数据等。一旦这些文件泄露&#xff0c;可能会给企业带来不可估量的损失。 然而&#xff0c;企业文件防泄密是确保…

气膜场馆里面噪声很大怎么解决?

随着气膜结构在各个领域的广泛应用&#xff0c;人们开始意识到在这些场馆内部&#xff0c;特别是在大型活动和展览中&#xff0c;噪声问题可能会变得相当严重。传统的气膜结构通常难以提供良好的声学环境&#xff0c;这对于参与者的舒适度和活动的质量构成了挑战。为了解决气膜…

QECon大会亮相产品,支持UI自动化测试?RunnerGo

最近在gitee上看见一款获得GVP&#xff08;最有价值开源项目&#xff09;的测试平台RunnerGo&#xff0c;看他们官网介绍包含了接口测试、性能测试、自动化测试。知道他们有saas版可以试用&#xff0c;果断使用了一下&#xff0c;对其中场景管理和性能测试印象深刻&#xff0c;…

Wmware虚拟机网络配置

Wmware虚拟机网络配置 这几天我在家里电脑安装虚拟机打算学习一下集群配置&#xff0c;出现了一些问题。现在想把它记录下来&#xff0c;如果能给看到的人一些帮助&#xff0c;那就更好了。 1、桥接模式的配置 这个时候 我们的虚拟机就是桥接模式上网了。这时候可能会出现不能…

vue 获取年龄

今天对接完成百度身份证号识别相关API后&#xff0c;需要从身份中的出生年月日业获取其年龄&#xff0c;封装如下方法。 实现代码&#xff1a;ageValue()接受一个出生字符串 export function ageValue(val) {// 新建日期对象let date new Date()// 今天日期&#xff0c;数组&a…