算法随笔-二叉树遍历的N种姿势
最近在练习用Python刷算法,leetcode上刷了快300题。一开始怀疑自己根本不会写代码,现在觉得会写一点点了,痛苦又充实的刷题历程。对我这种半路出家的人而言,收获真的很大。
今天就从二叉树遍历写起,曾经有次面试就被迭代实现卡过。。。

最简单的递归
#先序遍历
def preorderTraversal(self, root: TreeNode) -> List[int]:
res=[]
def preTraversal(node,result):
if node==None:
return
#输出放在最前
result.append(node.val)
preTraversal(node.left,result)
preTraversal(node.right,result)
preTraversal(root,res)
return res #中序遍历
def inorderTraversal(self, root: TreeNode) -> List[int]:
res=[]
def inorderTraversal(node,result):
if node==None:
return
inorderTraversal(node.left,result)
#输出放在中间
result.append(node.val)
inorderTraversal(node.right,result)
inorderTraversal(root,res)
return res #后序遍历
def postorderTraversal(self, root: TreeNode) -> List[int]:
res=[]
def postTraversal(node,result):
if node==None:
return
postTraversal(node.left,result)
postTraversal(node.right,result)
#输出放在最后
result.append(node.val)
postTraversal(root,res)
return res
递归实现极其简单,但是面试时往往会更进一步,问几个深入一点的问题:
- 递归实现存在什么问题?
- 尾递归是什么?
这两个问题都在讲一件事,就是迭代实现的二叉树遍历会存在StackOverflow异常。却决于操作系统和运行时,不同的程序拥有的栈大小不一,但是栈的容量都是较小的,基于递归的实现,如果未优化,就会导致堆栈溢出的异常。对.NET而言,系统分配的默认栈空间大小是1MB,树节点一多,很容就满了。
而尾递归则是对普通递归的优化,每次迭代最后都是直接调用自身。很多编译器都对尾递归做了生成优化,使得它可以不在调用栈上面每次都添加一个新的堆栈帧,而是更新它。这样就不会导致调用栈爆炸的异常。
如果你能答上上面的问题,往往会让你写一下二叉树遍历非递归的实现,这里难度就上了一个台阶。
非递归实现
二叉树迭代一定会用到栈!二叉树迭代一定会用到栈!二叉树迭代一定会用到栈!
talk is easy, show you my code
#超简单的先序遍历
def preorderTraversal(self, root: TreeNode) -> List[int]:
res=[]
if root==None:
return
stack=[root]
while stack:
node=stack.pop()
res.append(node.val)
#栈先进后出,顺序要注意
if node.right:
stack.append(node.right)
if node.left:
stack.append(node.left)
return res #稍复杂的中序遍历
def inorderTraversal(self, root: TreeNode) -> List[int]:
res=[]
curr=root
stack=[]
while curr or stack:
#优先遍历全部左节点
while curr:
stack.append(curr)
curr=curr.left
node=stack.pop()
res.append(node.val)
#当前节点切换到右节点
if node.right:
curr=node.right
return res #最复杂的后序遍历
#解法1 基于先序遍历的变形 leetcode官方题解:https://leetcode-cn.com/problems/binary-tree-postorder-traversal/solution/er-cha-shu-de-hou-xu-bian-li-by-leetcode/ def postorderTraversal(self, root: TreeNode) -> List[int]:
res=[]
if root==None:
return res
stack=[root]
while stack:
node=stack.pop()
res.append(node.val)
if node.left:
stack.append(node.left)
if node.right:
stack.append(node.right)
#反转结果
return res[::-1] #解法2 记录走过的路径
def postorderTraversal(self, root: TreeNode) -> List[int]:
res=[]
if root==None:
return res
stack=[root]
km=set()
while stack:
node=stack[-1]
#只有叶子节点和左右节点被遍历过的才可以输出
if (node.left==None and node.right==None) or (node.left in km or node.right in km):
res.append(node.val)
km.add(node)
stack.pop()
else:
#注意进栈顺序
if node.right:
stack.append(node.right)
if node.left:
stack.append(node.left)
return res #解法3 中序遍历的变形,左右子树遍历切换
def postorderTraversal(self, root: TreeNode) -> List[int]:
res=[]
if root==None:
return res
stack=[]
curr=root
last=None
while curr or stack:
if curr:
stack.append(curr)
#切换到左子树
curr=curr.left
else:
node=stack[-1]
#是否切换到右子树
if node.right and node.right!=last:
curr=node.right
else:
res.append(node.val)
stack.pop()
last=node
return res
后序遍历的几种迭代解法非常值得一看,想起当初在白板面前呆了半天也没写出来,蓝廋香菇,现在可以自信地讲树的遍历我掌握了!!!
算法随笔-二叉树遍历的N种姿势的更多相关文章
- C++ 二叉树遍历实现
原文:http://blog.csdn.net/nuaazdh/article/details/7032226 //二叉树遍历 //作者:nuaazdh //时间:2011年12月1日 #includ ...
- python实现二叉树遍历算法
说起二叉树的遍历,大学里讲的是递归算法,大多数人首先想到也是递归算法.但作为一个有理想有追求的程序员.也应该学学非递归算法实现二叉树遍历.二叉树的非递归算法需要用到辅助栈,算法着实巧妙,令人脑洞大开. ...
- 图文详解两种算法:深度优先遍历(DFS)和广度优先遍历(BFS)
参考网址:图文详解两种算法:深度优先遍历(DFS)和广度优先遍历(BFS) - 51CTO.COM 深度优先遍历(Depth First Search, 简称 DFS) 与广度优先遍历(Breath ...
- 【数据结构与算法】二叉树的 Morris 遍历(前序、中序、后序)
前置说明 不了解二叉树非递归遍历的可以看我之前的文章[数据结构与算法]二叉树模板及例题 Morris 遍历 概述 Morris 遍历是一种遍历二叉树的方式,并且时间复杂度O(N),额外空间复杂度O(1 ...
- javascript数据结构与算法--二叉树遍历(后序)
javascript数据结构与算法--二叉树遍历(后序) 后序遍历先访问叶子节点,从左子树到右子树,再到根节点. /* *二叉树中,相对较小的值保存在左节点上,较大的值保存在右节点中 * * * */ ...
- javascript数据结构与算法--二叉树遍历(先序)
javascript数据结构与算法--二叉树遍历(先序) 先序遍历先访问根节点, 然后以同样方式访问左子树和右子树 代码如下: /* *二叉树中,相对较小的值保存在左节点上,较大的值保存在右节点中 * ...
- javascript数据结构与算法--二叉树遍历(中序)
javascript数据结构与算法--二叉树遍历(中序) 中序遍历按照节点上的键值,以升序访问BST上的所有节点 代码如下: /* *二叉树中,相对较小的值保存在左节点上,较大的值保存在右节点中 * ...
- 【数据结构&算法】11-树基础&二叉树遍历
目录 前言 树的定义 树的存储结构 双亲表示法 孩子表示法 孩子兄弟表示法 二叉树 定义 特点 形态 特殊二叉树 斜树 满二叉树 完全二叉树 二叉树的性质 二叉树的存储结构 二叉树的顺序存储结构 二叉 ...
- 二叉树——遍历篇(递归/非递归,C++)
二叉树--遍历篇 二叉树很多算法题都与其遍历相关,笔者经过大量学习.思考,整理总结写下二叉树的遍历篇,涵盖递归和非递归实现. 1.二叉树数据结构及访问函数 #include <stdio.h&g ...
随机推荐
- windows下使用zkui
一,前言 使用zkui可以很方便的查看操作zookeeper 二,从源代码生成可执行jar zkui在github上的地址:https://github.com/DeemOpen/zkui 使用ide ...
- Centos修改yum源为国内阿里源
以下为修改Centos6.5的yum源: 1. 备份原镜像文件,便于后期恢复 [root@keepmydream ~]# mv /etc/yum.repos.d/CentOS-Base.repo /e ...
- .NET之Hangfire快速入门和使用
前言: 定时任务调度问题,是一个老生常谈的问题.网上有许多定时任务调度的解决方案,对于我而言很早以前主要是使用Window计划和Window服务来做任务定时执行,然后就开始使用定时任务调度框架Quar ...
- pyinstaller程序打包工具
PyInstaller是一个能将Python程序转换成单个可执行文件的程序, 操作系统支持Windows, Linux, Mac OS X, Solaris和AIX.并且很多包都支持开箱即用,不依赖环 ...
- Nginx+PHP7.3.9 Docker镜像制作
最近因项目需要制作了多个版本的php docker镜像,制作过程可谓是一波三折,因基于yum的方式安装php的方式在安装扩展插件时很不方便,不容易找到插件对应的yum源,所以PHP在docker镜像中 ...
- 安装vant2.2.7版本报错These dependencies were not found: vant/es/goods-action-big-btn in ./src/config/vant.config.js......
一.问题 前天,在使用vant的checkbox复选框的时候,注意到新增加一个全选功能,通过 ref 可以获取到 CheckboxGroup 实例并调用实例方法.于是我就想用这个,但是按照上面的示例写 ...
- 294 div2 C. A and B and Team Training
C. A and B and Team Training 题目:A and B are preparing themselves for programming contests. An import ...
- else块的用途
除了在if...else...中使用,else块还可以在for循环.while循环以及try...except中使用. 在for循环中使用: my_list = ['a','b','c','d'] f ...
- 求n以内的质数(质数的定义:在大于1的自然数中,除了1和它本身意外,无法被其他自然数整除的数)
思路: 1.(质数筛选定理)n不能够被不大于根号n的任何质数整除,则n是一个质数2.除了2的偶数都不是质数代码如下: /** * 求n内的质数 * @param int $n * @return ar ...
- spring5 源码深度解析----- 事务的回滚和提交(100%理解事务)
上一篇文章讲解了获取事务,并且通过获取的connection设置只读.隔离级别等,这篇文章讲解剩下的事务的回滚和提交 回滚处理 之前已经完成了目标方法运行前的事务准备工作,而这些准备工作最大的目的无非 ...