力扣刷题第二十五天--二叉树

前言

二叉树的第一天,掌握前序中序后序遍历,及对应的递归迭代,morris写法。难度一个比一个高是吧。。。

内容

一、二叉树的前序遍历

144.二叉树的前序遍历

给你二叉树的根节点 root ,返回它节点值的 前序 遍历。

递归

每次写递归,都按照这三要素来写,可以保证大家写出正确的递归算法!

  1. 确定递归函数的参数和返回值: 确定哪些参数是递归的过程中需要处理的,那么就在递归函数里加上这个参数, 并且还要明确每次递归的返回值是什么进而确定递归函数的返回类型。

  2. 确定终止条件: 写完了递归算法, 运行的时候,经常会遇到栈溢出的错误,就是没写终止条件或者终止条件写的不对,操作系统也是用一个栈的结构来保存每一层递归的信息,如果递归没有终止,操作系统的内存栈必然就会溢出。

  3. 确定单层递归的逻辑: 确定每一层递归需要处理的信息。在这里也就会重复调用自己来实现递归的过程。

/*** Definition for a binary tree node.* type TreeNode struct {*     Val int*     Left *TreeNode*     Right *TreeNode* }*/
var res []int
func preorderTraversal(root *TreeNode) []int {res=[]int{}dfs(root)return res
}func dfs(root *TreeNode){if root!=nil{res=append(res,root.Val)dfs(root.Left)dfs(root.Right)}
}
// func preorderTraversal(root *TreeNode) (vals []int) {
//     var preorder func(*TreeNode)//定义了一个名为preorder的变量,它的类型是一个函数,这个函数接收一个指向TreeNode类型的指针node作为参数
//     preorder = func(node *TreeNode) {
//         if node == nil {
//             return
//         }
//         vals = append(vals, node.Val)
//         preorder(node.Left)
//         preorder(node.Right)
//     }
//     preorder(root)
//     return
// }
迭代

递归的时候隐式地维护了一个栈,迭代的时候需要显式地将这个栈模拟出来

func preorderTraversal(root *TreeNode)[]int{var res []intvar stack []*TreeNodefor len(stack)>0||root!=nil{for root!=nil{res=append(res,root.Val)stack=append(stack,root.Right)root=root.Left}//循环结束后,即当前节点为空时root=stack[len(stack)-1]stack=stack[:len(stack)-1]}return res
}

递归迭代两种写法: 

时间复杂度:O(n),其中 n 是二叉树的节点数。每一个节点恰好被遍历一次。

空间复杂度:O(n),为迭代过程中显式栈的开销,平均情况下为 O(logn),最坏情况下树呈现链状,为 O(n)。

 Morris 遍历

参考文章Morris遍历详解

Morris 遍历的核心思想是利用树的大量空闲指针,实现空间开销的极限缩减。

时间复杂度:O(n),其中 n 是二叉树的节点数。没有左子树的节点只被访问一次,有左子树的节点被访问两次。

空间复杂度:O(1)。只操作已经存在的指针(树的空闲指针),因此只需要常数的额外空间

如果在遍历一棵树时严令禁止修改树的结构,那么Morris遍历就用不了

func preorderTraversal(root *TreeNode) (vals []int) {var p1,p2 *TreeNode=root,nilfor p1!=nil{p2=p1.Leftif p2!=nil{for p2.Right!=nil&&p2.Right!=p1{p2=p2.Right}if p2.Right==nil{vals=append(vals,p1.Val)p2.Right=p1p1=p1.Leftcontinue}p2.Right=nil}else{vals=append(vals,p1.Val)}p1=p1.Right}return
}
二、 二叉树的中序遍历

94.二叉树的中序遍历

给定一个二叉树的根节点 root ,返回 它的 中序 遍历 。

递归
func inorderTraversal(root *TreeNode)  (res []int) {var inorder func(*TreeNode)inorder=func(node *TreeNode){if node==nil{return}inorder(node.Left)res=append(res,node.Val)inorder(node.Right)}inorder(root)return
}
迭代
func inorderTraversal(root *TreeNode)  (res []int) {stack:=[]*TreeNode{}for root!=nil||len(stack)>0{for root!=nil{stack=append(stack,root)root=root.Left}root=stack[len(stack)-1]stack=stack[:len(stack)-1]res=append(res,root.Val)root=root.Right}return
}
Morris遍历
func inorderTraversal(root *TreeNode)  (res []int) {for root!=nil{if root.Left!=nil{// predecessor 节点表示当前 root 节点向左走一步,然后一直向右走至无法走为止的节点p:=root.Leftfor p.Right!=nil&&p.Right!=root{// 有右子树且没有设置过指向 root,则继续向右走p=p.Right}if p.Right==nil{// 将 predecessor 的右指针指向 root,这样后面遍历完左子树 root.Left 后,就能通过这个指向回到 rootp.Right=root// 遍历左子树root=root.Left}else{// predecessor 的右指针已经指向了 root,则表示左子树 root.Left 已经访问完了res=append(res,root.Val)p.Right=nil// 恢复原样root=root.Right// 遍历右子树}}else{// 没有左子树res=append(res,root.Val)// 若有右子树,则遍历右子树// 若没有右子树,则整颗左子树已遍历完,root 会通过之前设置的指向回到这颗子树的父节点root=root.Right}}return
}
三、二叉树的后序遍历

145.二叉树的后序遍历

给你一棵二叉树的根节点 root ,返回其节点值的 后序遍历 

递归
func postorderTraversal(root *TreeNode) []int {var res []intvar postorder func(*TreeNode)postorder=func(node *TreeNode){if node==nil{return }postorder(node.Left)postorder(node.Right)res=append(res,node.Val)}postorder(root)return res
}
迭代
func postorderTraversal(root *TreeNode)[]int{var res []intvar stack []*TreeNodefor len(stack)>0||root!=nil{for root!=nil{res=append(res,root.Val)stack=append(stack,root.Left)root=root.Right}//循环结束后,即当前节点为空时root=stack[len(stack)-1]stack=stack[:len(stack)-1]}reverse(res)return  res
}
func reverse(a []int) {l, r := 0, len(a) - 1for l < r {a[l], a[r] = a[r], a[l]l++r--}
}
//先序 中左右 调整为中右左 反转 左右中
func postorderTraversal(root *TreeNode)(res []int){stack:=[]*TreeNode{}var prev *TreeNodefor root!=nil||len(stack)>0{for root!=nil{stack=append(stack,root)root=root.Left}root=stack[len(stack)-1]stack=stack[:len(stack)-1]if root.Right==nil||root.Right==prev{res=append(res,root.Val)prev=rootroot=nil}else{stack=append(stack,root)root=root.Right}}return
}
Morris遍历
func postorderTraversal(root *TreeNode) (res []int) {addPath:=func(node *TreeNode){resSize:=len(res)for ;node!=nil;node=node.Right{res=append(res,node.Val)}reverse(res[resSize:])}p1:=rootfor p1!=nil{if p2:=p1.Left;p2!=nil{for p2.Right!=nil&&p2.Right!=p1{p2=p2.Right}if p2.Right==nil{p2.Right=p1p1=p1.Leftcontinue}p2.Right=niladdPath(p1.Left)}p1=p1.Right}addPath(root)return
}
func reverse(a []int){for i,n:=0,len(a);i<n/2;i++{a[i],a[n-i-1]=a[n-i-1],a[i]}
}

最后

迭代和Morris没理解透,看后续训练吧。

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

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

相关文章

TensorFlow C++编译及推理

TensorFlow环境配置&#xff1a; Tensorflow c源码编译&#xff08;踩坑版&#xff09; tensorflow C服务开发指南 预测推理代码&#xff1a; C运行TensorFlow模型

【UE5】显示或隐藏物体轮廓线

效果 步骤 1. 先下载所需的材质文件“M_Highlight.uasset” 材质下载链接&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1rxmRhkUoXVq6-DkIKyBhAQ 提取码&#xff1a;55bv 2. 在视口中拖入后期处理体积 根据需求设置后期处理体积的大小或者直接设置无限范围&…

python爬取网站数据,作为后端数据

一. 内容简介 python爬取网站数据&#xff0c;作为后端数据 二. 软件环境 2.1vsCode 2.2Anaconda version: conda 22.9.0 2.3代码 链接&#xff1a; 三.主要流程 3.1 通过urllib请求网站 里面用的所有的包 ! pip install lxml ! pip install selenium ! pip install…

德迅云安全带您了解什么是虚拟内存 - 103.88.35.X

虚拟内存是一种内存管理技术&#xff0c;可以像主内存的一部分一样使用辅助内存。虚拟内存是计算机操作系统 (OS) 中使用的常用技术。 虚拟内存是用硬盘空间做内存来弥补计算机RAM空间的缺乏。当实际RAM满时&#xff08;实际上&#xff0c;在RAM满之前&#xff09;&#xff0c;…

【Linux】U盘安装的cfg引导文件配置

isolinux.cfg文件 default vesamenu.c32 timeout 600display boot.msg# Clear the screen when exiting the menu, instead of leaving the menu displayed. # For vesamenu, this means the graphical background is still displayed without # the menu itself for as long …

ClickHouse查看执行计划

在clickhouse 20.6版本之前要查看SQL语句的执行计划需要设置日志级别为trace才能可以看到&#xff0c;并且只能真正执行sql&#xff0c;在执行日志里面查看。在20.6版本引入了原生的执行计划的语法。在20.6.3版本成为正式版本的功能。 本文档基于目前较新稳定版21.7.3.14。 1.基…

Java声明式事务实战!工作中用这几种就够了!

文章目录 1.几种常用的事务传播行为1.1 REQUIRED1.2 REQUIRES_NEW1.2 NESTED 2. 事务问题2.1 事务不生效&#xff1f;2.2 事务不回滚&#xff1f; 文章会分为两个部分来讲解&#xff0c;第一部分是声明式事务的几种使用场景。第二部分包含事务没有生效&#xff0c;没有回滚的情…

计算两个图形遮盖率

读取图像 首先&#xff0c;加载待处理的图像&#xff0c;可以使用图像处理库&#xff08;例如OpenCV&#xff09;来实现这一步。确保已加载正确的图像。 定义特定颜色范围 确定所需的特定颜色范围。这将是要检测的马赛克填充的颜色。需要指定颜色的下限值和上限值&#xff0c;通…

深度学习中文汉字识别 计算机竞赛

文章目录 0 前言1 数据集合2 网络构建3 模型训练4 模型性能评估5 文字预测6 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 深度学习中文汉字识别 该项目较为新颖&#xff0c;适合作为竞赛课题方向&#xff0c;学长非常推荐&#xf…

快速搭建本地的chatgpt

快速搭建本地的chatgpt 参考&#xff1a;一篇文章教你使用Docker本地化部署Chatgpt&#xff08;非api&#xff0c;速度非常快&#xff01;&#xff01;&#xff01;&#xff09;及裸连GPT的方式&#xff08;告别镜像GPT&#xff09;-CSDN博客 前提是linux下 已安装docker 命…

批量下载Sentinel数据脚本2023

批量下载Sentinel数据脚本2023 那些最好的程序员不是为了得到更高的薪水或者得到公众的仰慕而编程&#xff0c;他们只是觉得这是一件有趣的事情&#xff01; 批量下载Sentinel数据脚本2023 批量下载Sentinel数据脚本2023&#x1f33f;前言&#x1f33f;脚本地址&#x1f4e7;Su…

Leetcode—142.环形链表II【中等】

2023每日刷题&#xff08;三十三&#xff09; Leetcode—142.环形链表II 实现代码 /*** Definition for singly-linked list.* struct ListNode {* int val;* struct ListNode *next;* };*/ struct ListNode *detectCycle(struct ListNode *head) {struct ListNode* …

Codeforces Round 909 (Div. 3) 题解 A-E

目录 A - Game with IntegersB - 250 Thousand Tons of TNTC - Yarik and ArrayD - Yarik and Musical NotesE - Queue Sort A - Game with Integers 原题链接 题目描述 给定一个整数N&#xff0c;A和B都可以对这个整数进行加一或者减一操作&#xff0c;从A开始&#xff0c;如…

2023.11.18 - hadoop之zookeeper分布式协调服务

1.zookeeper简介 ZooKeeper概念: Zookeeper是一个分布式协调服务的开源框架。本质上是一个分布式的小文件存储系统 ZooKeeper作用: 主要用来解决分布式集群中应用系统的一致性问题。 ZooKeeper结构: 采用树形层次结构&#xff0c;没有目录与文件之分,ZooKeeper树中的每个节点被…

防止显卡掉卡的一种方法:nvidia-smi -pm 1

背景 服务器较老&#xff0c;里面的显卡在使用一段时间后会出现掉卡现象 解决方法 在终端输入&#xff1a;nvidia-smi -pm 1 nvidia-smi -pm 1是用于在NVIDIA GPU上启用持久性模式的命令。 NVIDIA GPU的持久性模式是一种能够保持 GPU 在全功率状态下运行的设置。通常情况下…

阿里巴巴java开发手册-编程规约

编程规约 命名风格常量定义代码格式OOP 规约日期时间集合处理并发处理控制语句注释规约前后端规约其他 命名风格 【强制】代码中的命名均不能以下划线或美元符号开始&#xff0c;也不能以下划线或美元符号结束。 反例&#xff1a;_name / name / n a m e / n a m e / n a m e…

Linux下安装部署redis(离线模式)

一、准备工作 1.下载redis的安装包 下载地址&#xff1a;Index of /releases/ 大家可以自行选择redis的版本&#xff0c;笔者选择的是最新的 2.上传到服务器 前提是我先在服务器上创建了一个目录redis7.2.3&#xff0c;我直接上传到这个目录下 二、安装redis 1.解压redis t…

Cloud

Spring Cloud 是基于 Spring Framework 构建的一套分布式系统开发工具集&#xff0c;旨在简化分布式系统的开发。它提供了一系列的组件和工具&#xff0c;帮助开发者快速搭建分布式系统中的常见模块&#xff0c;例如服务注册与发现、配置中心、断路器、网关等。以下是我对 Spri…

uniapp 跨页面传值及跨页面方法调用

uniapp 跨页面传值及跨页面方法调用 1、跨页面传值 使用全局方法监听uni.$emit、uni.$on、uni.$off 发布、监听、移除 methods: {addFun(){let data [1]uni.navigateBack({ // 返回上一页delta: 1})uni.$emit(successFun,{data}) // 传值} }监听页 onLoad() {uni.$on(succ…

RIP路由信息协议

RIP路由信息协议(Routing Information Protocol) 最先得到广泛应用的协议&#xff0c;最大优点是简单要求网络中的每个路由器都要维护一张表&#xff0c;表中记录了从它自己到其他每一个目的网络的距离RIP是应用层协议&#xff0c;它在传输层使用UDP&#xff0c;RIP报文作为UD…