最近在练习用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

  递归实现极其简单,但是面试时往往会更进一步,问几个深入一点的问题:

  1. 递归实现存在什么问题? 
  2. 尾递归是什么?

这两个问题都在讲一件事,就是迭代实现的二叉树遍历会存在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种姿势的更多相关文章

  1. C++ 二叉树遍历实现

    原文:http://blog.csdn.net/nuaazdh/article/details/7032226 //二叉树遍历 //作者:nuaazdh //时间:2011年12月1日 #includ ...

  2. python实现二叉树遍历算法

    说起二叉树的遍历,大学里讲的是递归算法,大多数人首先想到也是递归算法.但作为一个有理想有追求的程序员.也应该学学非递归算法实现二叉树遍历.二叉树的非递归算法需要用到辅助栈,算法着实巧妙,令人脑洞大开. ...

  3. 图文详解两种算法:深度优先遍历(DFS)和广度优先遍历(BFS)

    参考网址:图文详解两种算法:深度优先遍历(DFS)和广度优先遍历(BFS) - 51CTO.COM 深度优先遍历(Depth First Search, 简称 DFS) 与广度优先遍历(Breath ...

  4. 【数据结构与算法】二叉树的 Morris 遍历(前序、中序、后序)

    前置说明 不了解二叉树非递归遍历的可以看我之前的文章[数据结构与算法]二叉树模板及例题 Morris 遍历 概述 Morris 遍历是一种遍历二叉树的方式,并且时间复杂度O(N),额外空间复杂度O(1 ...

  5. javascript数据结构与算法--二叉树遍历(后序)

    javascript数据结构与算法--二叉树遍历(后序) 后序遍历先访问叶子节点,从左子树到右子树,再到根节点. /* *二叉树中,相对较小的值保存在左节点上,较大的值保存在右节点中 * * * */ ...

  6. javascript数据结构与算法--二叉树遍历(先序)

    javascript数据结构与算法--二叉树遍历(先序) 先序遍历先访问根节点, 然后以同样方式访问左子树和右子树 代码如下: /* *二叉树中,相对较小的值保存在左节点上,较大的值保存在右节点中 * ...

  7. javascript数据结构与算法--二叉树遍历(中序)

    javascript数据结构与算法--二叉树遍历(中序) 中序遍历按照节点上的键值,以升序访问BST上的所有节点 代码如下: /* *二叉树中,相对较小的值保存在左节点上,较大的值保存在右节点中 * ...

  8. 【数据结构&算法】11-树基础&二叉树遍历

    目录 前言 树的定义 树的存储结构 双亲表示法 孩子表示法 孩子兄弟表示法 二叉树 定义 特点 形态 特殊二叉树 斜树 满二叉树 完全二叉树 二叉树的性质 二叉树的存储结构 二叉树的顺序存储结构 二叉 ...

  9. 二叉树——遍历篇(递归/非递归,C++)

    二叉树--遍历篇 二叉树很多算法题都与其遍历相关,笔者经过大量学习.思考,整理总结写下二叉树的遍历篇,涵盖递归和非递归实现. 1.二叉树数据结构及访问函数 #include <stdio.h&g ...

随机推荐

  1. Scrapy项目 - 实现腾讯网站社会招聘信息爬取的爬虫设计

    通过使Scrapy框架,进行数据挖掘和对web站点页面提取结构化数据,掌握如何使用Twisted异步网络框架来处理网络通讯的问题,可以加快我们的下载速度,也可深入接触各种中间件接口,灵活的完成各种需求 ...

  2. 【Django】ModelForm

    ModelForm 1.Model与Form内容回顾 Model - 数据库操作 - 验证 class A(MOdel): user = email = pwd = Form - class Logi ...

  3. MongoDB的可视化工具(Studio 3T)

    ​​ 前面我们已经介绍了MongoDB怎么安装,接下来要安装他的可视化工具——Studio 3T. 先到这下载一个压缩包,百度网盘,https://pan.baidu.com/s/1M8mlWo334 ...

  4. 解决thinkphp批量上传图片只有一张上传成功解决方案

    批量上传时 存在一个生成文件名的问题 如果出现此bug,则不要用原生的生成规则来命名图片文件名 如果你试试同时上传两个不同类型,例如一张jpg,一张png,你就发现的确是可以两张同时上传的! 方案1: ...

  5. [LeetCode] 822. Card Flipping Game

    Description On a table are N cards, with a positive integer printed on the front and back of each ca ...

  6. Golang的反射reflect深入理解和示例

    编程语言中反射的概念 在计算机科学领域,反射是指一类应用,它们能够自描述和自控制.也就是说,这类应用通过采用某种机制来实现对自己行为的描述(self-representation)和监测(examin ...

  7. Feign【入门】

    feign简介: feign是一种声明式,模板化的HTTP客户端,spring cloud对feign进行了增强,使其支持SpringMvc的相关注解,并整合了ribbon做负载均衡.在spring ...

  8. JSON parse error: No suitable constructor found for type

    错误信息: 2019-02-19 09:17:58,678 [http-nio-8080-exec-1] WARN org.springframework.web.servlet.mvc.suppor ...

  9. jsp JavaBean el表达式

    JSP三大指令 一个jsp页面中,可以有0~N个指令的定义! 1. page --> 最复杂:<%@page language="java" info="xx ...

  10. QR 码详解(下)

    快速响应矩阵码(下) 书接上回,继续下半场. 纠错码 QR 码采用纠错算法生成一系列纠错码字,添加在数据码字序列之后,使得符号可以在遇到损坏时可以恢复.这就是为什么二维码即使有残缺也可以扫出来.没有残 ...