算法套路八——二叉树深度优先遍历(前、中、后序遍历)

news2024/10/5 16:30:21

算法套路八——二叉树深度优先遍历(前、中、后序遍历)

算法示例:LeetCode98:验证二叉搜索树

给你一个二叉树的根节点 root ,判断其是否是一个有效的二叉搜索树。
有效 二叉搜索树定义如下:
节点的左子树只包含 小于 当前节点的数。
节点的右子树只包含 大于 当前节点的数。
所有左子树和右子树自身必须也是二叉搜索树。
在这里插入图片描述

方法一:前序遍历——先判断,再递归

前序遍历即先遍历根节点,再遍历左右子树
前序遍历我们的思路是先判断当前结点是否满足二叉搜索树的条件,再递归左右子树。
在这里插入图片描述
且如上图所示,在二叉搜索树中,使用前序遍历时有如上的规律,从根节点传递取值范围,对于任意一个结点,其取值范围已经确定,若结点值不在范围内,则不是二叉搜索树。
步骤如下所示:

  1. root结点的取值范围为(-inf,+inf),判断是否满足条件
  2. 判断左子树是否是二叉搜索树,且此时最大值应该小于root.val,所以取值范围为(-inf,root.val]
  3. 判断右子树是否是二叉搜索树,且此时最小值应该大于root.val,所以取值范围为[root.val,inf)
  4. 对于2,3采取递归遍历

且注意判断root是否为空

class Solution:
    def isValidBST(self, root: Optional[TreeNode], left=-inf, right=inf) -> bool:
        if root is None:
            return True
        x = root.val
        return left < x < right and \
               self.isValidBST(root.left, left, x) and \
               self.isValidBST(root.right, x, right)

方法二:中序遍历——先判断,再递归

中序遍历即先遍历左节点、根节点,最后遍历右节点
且中序遍历下二叉搜索树应该为递增数组,所以我们直接判断当前节点值是否大于上一个遍历的节点值pre
其实这也等价于约束节点的范围,在中序遍历时只需要修改最小值,即取值范围是(pre,inf)

  1. 判断左子树是否是二叉搜索树,且记录左子树最后一个被遍历的节点值为pre,也是左子树的最大值
  2. 比较当前节点指是否大于pre,即取值范围是(pre,inf),
  3. 判断右子树是否是二叉搜索树
  4. 对于1,3采取递归遍历
class Solution:
    pre = -inf
    def isValidBST(self, root: Optional[TreeNode]) -> bool:
        if root is None:
            return True
        if not self.isValidBST(root.left):
            return False
        if root.val<=self.pre:
            return False
        self.pre = root.val
        return self.isValidBST(root.right)
        

方法三:后序遍历——先递归,在判断

后序遍历即先遍历左节点、右节点,最后遍历根节点
后序遍历也可以传递节点的范围,不过是从叶子节点向根节点传递,根节点需要大于左子树的最大值,小于右子树的最小值。
在这里插入图片描述

  1. 如果当今节点为null空节点,则返回(inf,-inf),因为任何值都会小于inf,任何值都会大于-inf,这样就不会影响到树的最大最小值的取值,可以仔细体会。
  2. 遍历左子树,且返回左子树的最小值l_min与最大值l_max
  3. 遍历右子树,且返回右子树的最小值r_min与最大值r_max
  4. 比较当前节点的值,若取值位于(l_max ,r_min),则更新最小值与最大值并返回即min(l_min, x), max(r_max, x)。若取值不在范围内,则表示不是二叉搜索树,此时我们返回正常情况不会返回的(-inf,inf)来表示False
  5. 比较返回值是否是正常值,这等价与判断是否等于inf无穷即非正常值,若等于inf则返回False,若不等于inf则返回True
class Solution:
    def isValidBST(self, root: Optional[TreeNode]) -> bool:
        def dfs(node: Optional[TreeNode]) -> Tuple:
            if node is None:
                return inf, -inf
            l_min, l_max = dfs(node.left)
            r_min, r_max = dfs(node.right)
            x = node.val
            # 也可以在递归完左子树之后立刻判断,如果不是二叉搜索树,就不用递归右子树了
            if x <= l_max or x >= r_min:
                return -inf, inf#返回无穷表示为False,不满足搜索树
            return min(l_min, x), max(r_max, x)
        return dfs(root)[1] != inf

总结:

前序遍历在某些数据下不需要递归到边界(base case)就能返回,而另外两种需要递归到至少一个边界,从这个角度上来说它是最快的。
中序遍历很好地利用到了二叉搜索树的性质,使用到的变量最少。
后序遍历的思想是最通用的,即自底向上计算子问题的过程。想要学好动态规划的话,请务必掌握这个思想。
且由以上示例代码都可以看出,在代码书写时要定义内部匿名函数dfs,不然可能会由于LeetCode判断问题影响结果

算法练习一:LeetCode230. 二叉搜索树中第K小的元素

给定一个二叉搜索树的根节点 root ,和一个整数 k ,请你设计一个算法查找其中第 k 个最小元素(从 1 开始计数)。在这里插入图片描述

利用特性中序遍历下二叉搜索树应该为递增数组
本题可以采用中序遍历,每次遍历时k–,当k为0时则表示当前结点为第k个结点,则令ans等于该值

func kthSmallest(root *TreeNode, k int) int {
    var ans int
    var dfs func(node *TreeNode) 
    dfs=func(node *TreeNode) {
        if node==nil{
            return 
        }
        dfs(node.Left)
        k--
        if k==0{
            ans=node.Val
        }
        dfs(node.Right)
    }
    dfs(root)
    return ans
}

算法练习二:LeetCode501. 二叉搜索树中的众数

给你一个含重复值的二叉搜索树(BST)的根节点 root ,找出并返回 BST 中的所有 众数(即,出现频率最高的元素)。如果树中有不止一个众数,可以按 任意顺序 返回。
在这里插入图片描述

利用特性中序遍历下二叉搜索树应该为递增数组
本题可以采用中序遍历,将遍历节点与前一个节点比较,然后使用变量cur,max来记录当前节点与最多节点,且注意要定义匿名函数解决。

func findMode(root *TreeNode) []int {
    var (
        ans []int
        pre, cur, max int
        dfs func(*TreeNode)
    )
    dfs = func(node *TreeNode) {
        if node == nil {
            return
        }
        dfs(node.Left)
        if node.Val == pre {
            cur++
        } else {
            cur = 1
        }
        if cur > max {
            max = cur
            ans = []int{node.Val}
        } else if cur == max {
            ans = append(ans, node.Val)
        }
        pre = node.Val
        dfs(node.Right)
    }
    dfs(root)
    return ans
}

算法练习三:LeetCode530. 二叉搜索树的最小绝对差

给你一个二叉搜索树的根节点 root ,返回 树中任意两不同节点值之间的最小差值 。差值是一个正数,其数值等于两值之差的绝对值。在这里插入图片描述

利用特性中序遍历下二叉搜索树应该为递增数组
本题可以采用中序遍历,将遍历节点与前一个节点比较,然后使用变量pre,min来记录前一个结点节点值与当前最小差值,并定义匿名函数解决。

func getMinimumDifference(root *TreeNode) int {
    min, pre := math.MaxInt64, -1
    var dfs func(node *TreeNode)
    dfs=func(node *TreeNode){
    if node==nil{
        return 
    }
    dfs(node.Left)
    sub:=node.Val-pre
    if sub<min&&pre!=-1{
        min=sub
    }
    pre=node.Val
    dfs(node.Right)
}
    dfs(root)
    return min
}

算法练习四:LeetCode700. 二叉搜索树中的搜索

给定二叉搜索树(BST)的根节点 root 和一个整数值 val。你需要在 BST 中找到节点值等于 val 的节点。 返回以该节点为根的子树。 如果节点不存在,则返回 null。在这里插入图片描述

若 root 为空则返回空节点;
若 val=root.val,则返回 \textit{root}root;
若val<root.val,递归左子树;
若 val>root.val,递归右子树。

func searchBST(root *TreeNode, val int) *TreeNode {
    if root == nil {
        return nil
    }
    if val == root.Val {
        return root
    }
    if val < root.Val {
        return searchBST(root.Left, val)
    }
    return searchBST(root.Right, val)
}

算法进阶一:LeetCode236. 二叉树的最近公共祖先

给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。在这里插入图片描述

本题可以使用分类讨论,如下图所示,定义函数dfs()返回当前结点node的子树是否找到p或q,有以下情况
在这里插入图片描述

func lowestCommonAncestor(root, p, q *TreeNode) *TreeNode {
	return dfs(root,p,q)
}
func dfs(node, p, q *TreeNode) *TreeNode{
    if node == nil || node == p || node == q {
		return node
	}
	left := dfs(node.Left, p, q)
	right := dfs(node.Right, p, q)
	if left != nil && right != nil {
		return node
	}
	if left != nil {
		return left
	}
	return right
}

算法进阶二:LeetCode236. 二叉树的最近公共祖先

给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。
在这里插入图片描述

本题与上题一样,只不过在判断p,q的位置时可以利用线索二叉树值的大小性质来判断
在这里插入图片描述

func lowestCommonAncestor(root, p, q *TreeNode) *TreeNode {
	return dfs(root,p,q)
}
func dfs(node, p, q *TreeNode) *TreeNode{
    if node == nil || node == p || node == q {
		return node
	}
	if node.Val>p.Val&&node.Val>q.Val{
        return dfs(node.Left,p,q)
    }else if node.Val<p.Val&&node.Val<q.Val{
        return dfs(node.Right,p,q)
    }
    return node
}

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

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

相关文章

网络原理——网络协议

目录传输层协议TCP协议TCP协议格式段TCP原理连接管理可靠传输流量控制拥塞控制延迟应答和捎带应答错误检测TCP异常UDP协议UDP协议格式段UDP协议特点无连接不可靠面向数据报大小受限网络层协议IP协议数据链路层协议​以太网MTUARP协议传输层协议 ​ 传输层负责数据能够从发送端…

学Vue3这一篇就够了!

目录学习Vue的前提是掌握 HTML,CSS,Js中级知识vue介绍声明式渲染条件与循环处理用户输入组件化应用构建Vue与自定义元素的关系应用和组件实例Vue实例根组件组件实例 property生命周期钩子实例的生命周期图模板语法插值文本原始 HTMLAttribute使用 JavaScript 表达式指令参数动态…

不黑学孙溟㠭匪石社寒山寺游记

昔日寒山问拾得曰&#xff1a;世间谤我、欺我、辱我、笑我、轻我、贱我、恶我、骗我、如何处治乎&#xff1f;拾得云&#xff1a;只是忍他、让他、由他、避他、耐他、敬他、不要理他、再待几年你且看他。 经典对话显修行。是否文殊、普贤再来都不重要。因缘使然&#xff0c;朋友…

C++ :JSON 解析系统的设计与实现

JSON JavaScript Object Notation(JavaScript 对象表示法) 定义 存储和交换文本信息的语法&#xff0c;类似 XML&#xff1b; 比 XML 更小、更快&#xff0c;更易解析&#xff0c;属于轻量级的文本数据交换格式&#xff1b; 使用 Javascript语法来描述数据对象&#xff0c;但…

PMP学习笔记

day01 裸考1 题目1: 下列哪一项不属于项目的特征? D A:独特性 B:临时性 C:渐进明细 D:重复性 解析:重复性属于运营 题目2: 在确定项目目标时,应该更加关注哪一个? D A:进度要求 B:成本预算 C:质量标准 D:同等关注进度,成本和质量要求 前沿 管理的基础 三个学…

Hashicorp Vault(金库)

什么是Vault? HashiCorp Vault 是一个基于身份的秘密和加密管理系统。机密是您想要严格控制访问的任何内容,例如 API 加密密钥、密码和证书。Vault 提供由身份验证和授权方法门控的加密服务。使用 Vault 的 UI、CLI 或 HTTP API,可以安全地存储和管理、严格控制(限制)和审…

微服务高级篇【1】之微服务保护

文章目录前言一 初识Sentinel1.1 雪崩问题1.2 解决方法1.3 小结1.4 服务保护技术对比1.5 Sentinel介绍1.6 Sentinel安装1.7 微服务整合Sentinel二 测试工具&#xff1a;Jmeter2.1 Jmeter安装和配置2.2 Jmeter快速入门2.2.1 设置中文语言2.2.2 设置Jmeter桌面快捷图标2.3 Jmeter…

Spring————java的反射机制,Spring的IOC和DI

一、认识Spring 1.1、Spring家族 SpringFramework&#xff1a; Spring框架&#xff1a;是Spring中最早核心的技术&#xff0c;也是所有其他技术及的基础。 SpringBoot:Spring是用来简化开发。而SpringBoot是来帮助Spring在简化的基础上能更快速进行开发。 SpringCloud&#xf…

FreeRTOS 任务相关 API 函数(一)

文章目录一、任务创建和删除 API 函数1. xTaxkCreate()2. xTaskCreateStatic()3. xTaskCreateRestricted()4. vTaskDelete()二、任务创建和删除实验(动态方法)三、任务创建和删除实验(静态方法)一、任务创建和删除 API 函数 FreeRTOS 最基本的功能就是任务管理&#xff0c;而任…

AcWing4957.飞机降落——学习笔记

目录 题目 代码 AC结果 思路 〇、例子 一、获取数据 二、深度优先遍历&#xff08;DFS&#xff09; 1.入参 2.出口 3.判断是否找到安全降落方案 4.递推过程 5.回溯过程 6.DFS完整代码 三、输出打印 题目 4957. 飞机降落 - AcWing题库https://www.acwing.com/pr…

打开 plist 文件

​ 对于使用苹果的进阶或资深玩家来说&#xff0c;有时候要编辑plist文件&#xff0c;比如要弄两个qq&#xff0c;要修改info.plist下的sku&#xff0c;而要是没有工具&#xff0c;就不能随意查看和编辑plist文件了。再有&#xff0c;可能要通过修改plist来破解某些游戏&#x…

windows服务器自带IIS搭建网站并发布公网访问【内网穿透】

文章目录1.前言2.Windows网页设置2.1 Windows IIS功能设置2.2 IIS网页访问测试3. Cpolar内网穿透3.1 下载安装Cpolar3.2 Cpolar云端设置3.3 Cpolar本地设置4.公网访问测试5.结语转载自远程源码文章&#xff1a;【IIS搭建网站】本地电脑做服务器搭建web站点并公网访问「内网穿透…

协同数据交换平台详细设计方案(word)

本资料来源公开网络&#xff0c;仅供个人学习&#xff0c;请勿商用&#xff0c;如有侵权请联系删除 1 架构设计 1.1 总体架构协同数据交换平台利用企业服务总线、数据抽取ETL、消息中间件、大文件传输等相关技术&#xff0c;包括文件适配器、数据库适配器、Web服务中间件等在内…

【排序算法(四)】归并排序计数排序(非比较排序)以及八大排序算法的总结

​ ​&#x1f4dd;个人主页&#xff1a;Sherry的成长之路 &#x1f3e0;学习社区&#xff1a;Sherry的成长之路&#xff08;个人社区&#xff09; &#x1f4d6;专栏链接&#xff1a;数据结构 &#x1f3af;长路漫漫浩浩&#xff0c;万事皆有期待 文章目录1、归并排序1.1 算法…

图像处理数据集

BSDS500 Berkeley Segmentation Dataset 500 是第一个用于评估超像素算法的数据集。对于参数优化&#xff0c;使用了验证集。 500张数据集200训练集train100验证集val200测试集test 每张图像有 5 个不同的高质量地面真值分割&#xff08;groundTruth,是.mat文件&#xff09; …

Android 中的混音器 AudioMixer 实现分析

Android framework 的音频处理模库 libaudioprocessing (位于 frameworks/av/media/libaudioprocessing) 提供了混音器组件 AudioMixer&#xff0c;它主要用在 audioflinger 里&#xff0c;用来将多路音频源数据混音&#xff0c;以方便送进音频设备播放出来。 音频混音操作本身…

MyBatis(九)MyBatis小技巧

一、#{}和${} #{}&#xff1a;先编译sql语句&#xff0c;再给占位符传值&#xff0c;底层是PreparedStatement实现。可以防止sql注入&#xff0c;比较常用。 ${}&#xff1a;先进行sql语句拼接&#xff0c;然后再编译sql语句&#xff0c;底层是Statement实现。存在sql注入现象。…

第09章_异常处理

第09章_异常处理 讲师&#xff1a;尚硅谷-宋红康&#xff08;江湖人称&#xff1a;康师傅&#xff09; 官网&#xff1a;http://www.atguigu.com 本章专题与脉络 1. 异常概述 1.1 什么是生活的异常 男主角小明每天开车上班&#xff0c;正常车程1小时。但是&#xff0c;不出…

计网第五章.运输层—TCP流量控制与可靠传输

以下来自湖科大计算机网络公开课笔记及个人所搜集资料 目录一、流量控制死锁死锁的解决&#xff1a;二、超时重传时间的选择解决方案Karn算法三、可靠传输补充&#xff1a;其实TCP的流量控制&#xff0c;可靠传输&#xff0c;拥塞控制&#xff0c;都是围绕滑动窗口机制来实现的…

SpringBoot的统一功能处理

目录 1.统一用户的的登录权限校验 最开始的用户登录 Spring拦截器 2.统一数据返回格式 统一数据的返回格式意义 统一数据返回格式的实现 3.统一异常处理 在上篇博客中我介绍了Spring AOP的基础知识,这篇博客则是AOP的实践练习,通过借助AOP实现三个目标 1.统一用户登录权…