算法(2)-二叉树的遍历(递归/迭代)python实现

二叉树的遍历

  • 1.深度优先DFS
    • 1.1 DFS 递归解法
      • 1.1.1先序遍历
      • 1.1.2中序遍历
      • 1.1.3后序遍历
    • 1.2 DFS迭代解法
      • 1.2.1先序遍历
      • 1.2.2中序遍历
      • 1.2.3后序遍历
  • 2.广度优先BFS
  • 3.二叉树的最大深度
    • 3.1递归
    • 3.2迭代
  • 4.翻转二叉树
    • 4.1递归
    • 4.1迭代
  • 5.合并两棵二叉树
    • 5.1递归
    • 5.2迭代

有两种通用的遍历树的策略:深度优先遍历、广度优先遍历。
(树本身是一个递归的结构)

在这里插入图片描述
利用树类构造一个棵二叉树:

class TreeNode(object):def __init__(self, x):self.val = xself.left = Noneself.right = None
T=TreeNode(1)
n1=T.left=TreeNode(2)
n2=T.right=TreeNode(3)
n3=n1.left=TreeNode(4)
n4=n1.right=TreeNode(5)

1.深度优先DFS

DFS(Depth First Search)为二叉树的深度优先遍历方式,深度优先从根节点开始,往深处搜索至某个叶子节点,依据左孩子,右孩子,根节点的相对遍历顺序,可以分为先序遍历(根-左-右),中需遍历(左-根-右),后续遍历(左-右-根)。

1.1 DFS 递归解法

深度优先递归解法的三种顺序,框架一致。递归最重要的一点:递归结束条件,(1)当前节点为空,或者递归程序执行完最后一行。

1.1.1先序遍历

根-左-右 输出:[1,2,4,8,9,5,3,6,7]

class Solution(object):def PreOrder_rec(self,root):res=[]def DFS_pre(node,res):if node==None:           returnres.append(node.val)     # 先中DFS_pre(node.left,res)   # 递归左子树DFS_pre(node.right,res)  # 递归右子树DFS_pre(root,res)return res

1.1.2中序遍历

左-根-右 输出:[8,4,9,2,5,1,6,3,7]

class Solution(object):def InOrder_rec(self, root):res=[]def DFS_In(node,res):if node==None:returnDFS_In(node.left,res)res.append(node.val)DFS_In(node.right,res)DFS_In(root,res)return res

1.1.3后序遍历

左-右-根 输出[8,9,4,5,2,6,7,3,1]

class Solution(object):def BackOrder_rec(self, root):res=[]def DFS_Back(node,res):if node==None:returnDFS_Back(node.left,res)DFS_Back(node.right,res)res.append(node.val)DFS_Back(root,res)return res

1.2 DFS迭代解法

二叉树深度优先迭代 要借助(先进后出)的特点。依据三种不同的顺序将不同的节点压入堆栈。

1.2.1先序遍历

根-左-右 输出:[1,2,4,8,9,5,3,6,7]。维护一个堆栈stack(python中可以用List 高效实现)
从根结点开始(curr=Root);
如果当前节点非空 访问当前结点的值,将当前节点的右子树节点推入堆栈,更新当前结点:curr=curr.left.;
如果当前节点为空:弹出堆栈保存的最后一个右子树结点。

class Solution(object):def PreOrder_iter(self, root):""":type root: TreeNode:rtype: List[List[int]]"""res=[]stack=[]curr=rootwhile(curr or stack):if curr:res.append(curr.val)stack.append(curr.right)     # 递归到叶子结点时,会出现stack 增加None的情形curr=curr.left               # 将None pop 出来就是多了一次无增res操作else:curr=stack.pop()return res

1.2.2中序遍历

左-根-右 输出:[8,4,9,2,5,1,6,3,7]
维护一个堆栈stack(python中可以用List 高效实现)。
从根结点开始(curr=Root):
如果当前节点非空:将当前结点推入堆栈,更新当前结点:curr=curr.left;
如果当前节点为空:弹出堆栈保存的最后一个结点,访问该结点的值,更新当前结点:curr=curr.right.

class Solution(object):def InOrer_iter(self, root):""":type root: TreeNode:rtype: List[List[int]]"""res=[]stack=[]curr=rootwhile(curr or stack):if curr:stack.append(curr)curr=curr.left      # 一直搜索左子树到叶子节点,将路径上的结点推入堆栈else:                   curr=stack.pop()res.append(curr.val)curr=curr.right    # 访问右子树结点return res

1.2.3后序遍历

左-右-根 输出[8,9,4,5,2,6,7,3,1]
维护一个堆栈stack(python中可以用List 高效实现)。
从根结点开始(curr=Root):
如果当前节点非空:将当前结点推入堆栈,更新当前结点:curr=curr.left;
如果当前节点为空:弹出堆栈保存的最后一个结点,访问该结点的值,更新当前结点:curr=curr.right.

class Solution(object):def BackOrder_iter(self, root):""":type root: TreeNode:rtype: List[List[int]]"""res=[]stack=[]curr=rootwhile(curr or stack):  # 左右根尅分解为:根右左+逆序(根右左和根左右是一样的实现框架)if curr:res.append(curr.val)stack.append(curr.left)curr=curr.rightelse:curr=stack.pop()return res[::-1]

2.广度优先BFS

BFS(Breadth First Search)为二叉树的广度优先遍历方式,又叫层次遍历从根节点开始逐层遍历二叉树。1->2->3->4->5->6->7->8->9

借助队列 先进后出 的特点,将节点不断推入队列中。python 中用list可以快速实现队列。
迭代解法 输出[1,2,3,4,5,6,7,8,9]

class Solution(object):def BFS_iter1(self, root):""":type root: TreeNode:rtype: List[List[int]]"""levels=[]queue=[root]if  not root:return levelswhile(queue):      # node=queue.pop(0)        levels.append(node.val)if node.left:            # 装进队列里的元素都是非空的。queue.append(node.left)if node.right:queue.append(node.right)return levels

leetcode 102 要求输出的结果每一层的元素放在一起,则需要维护一个list,用于放置每层的元素,levels=[[1],[2,3],[4,5,6,7],[8,9]],同时一个level变量用于指示当前节点位于的层数。

迭代解法 :输出[[1],[2,3],[4,5,6,7],[8,9]]

class Solution(object):def levelOrder_iter2(self, root):""":type root: TreeNode:rtype: List[List[int]]"""levels=[]queue=[root]if  not root:return levelswhile(queue):      # 处理每一层前,增加一次levels,装该层的节点值levels.append([])n_q=len(queue)  # 该层节点个数for i in range(n_q): # i:[0,n_q-1] # 逐个处理该层节点:首先弹出队列首,记录数值,有左右孩子的将孩子压入队列node=queue.pop(0)levels[-1].append(node.val)if node.left:queue.append(node.left)if node.right:queue.append(node.right)return levels

递归解法 输出[[1],[2,3],[4,5,6,7],[8,9]]
实际上是利用DFS实现的BFS,每当DFS遍历到一个新的节点,就把它加入到所在层的list里面去。

class Solution(object):def levelOrder_rec(self, root):""":type root: TreeNode:rtype: List[List[int]]"""levels=[]if  not root:return levelsdef BFS_rec(node,level):if len(levels)==level:        # level作为levels 的索引,应该<len(levels),如果相等,则需要增加levels的装载能力,扩容levels.append([])levels[level].append(node.val)  # 将当前节点放入指定的层if node.left:					# 如果有左右孩子,递归调用BFS_recBFS_rec(node.left,level+1) if node.right:BFS_rec(node.right,level+1)BFS_rec(root,0)return levels

参考博文:
二叉树的前中后遍历,层次遍历,树的递归问题(递归与迭代python):https://www.cnblogs.com/liuyicai/p/10156455.html
二叉树及其遍历方法—python实现:https://www.cnblogs.com/lliuye/p/9143676.html

3.二叉树的最大深度

3.1递归

def Max_depth(node):if node==None:return 0dl=DFS(node.left)dr=DFS(node.right)res=max(dl,dr)+1return res
res=Max_depth(root)
return res

3.2迭代


def maxDepth(self, root):de=0if root:stack=[(1,root)]else:return 0while(stack):cur_de,node=stack.pop()if node:     # 只有当结点存在时+1后的深度才会被采用de=max(de,cur_de)stack.append((cur_de+1,node.left))stack.append((cur_de+1,node.right))return de

4.翻转二叉树

4.1递归

自低向上的交换过程

class Solution(object):def invertTree(self, root):if root==None:returnself.invertTree(root.left)self.invertTree(root.right)root.left,root.right=root.right,root.leftreturn root

4.1迭代

自顶向下的交换过程

class Solution(object):def invertTree(self, root):""":type root: TreeNode:rtype: TreeNode"""if root:q=[root]else:return rootwhile(q):curr=q.pop(0)curr.left,curr.right=curr.right,curr.leftif curr.left:q.append(curr.left)if curr.right:q.append(curr.right)return root

5.合并两棵二叉树

leetcode617: 两棵树有公共结点处的值为两数对应节点值想加

5.1递归

class Solution(object):def mergeTrees(self, t1, t2):if not t1 and not t2:return Noneroot=TreeNode(0)if t1 and t2:root.val=t1.val+t2.valroot.left=self.mergeTrees(t1.left,t2.left)root.right=self.mergeTrees(t1.right,t2.right)elif t1:root.val=t1.valroot.left=self.mergeTrees(t1.left,None)root.right=self.mergeTrees(t1.right,None)else:root.val=t2.valroot.left=self.mergeTrees(None,t2.left)root.right=self.mergeTrees(None,t2.right)return rootclass Solution(object):def mergeTrees2(self, t1, t2):if t1==None:return t2if t2==None:return t1t1.val+=t2.valt1.left=self.mergeTrees2(t1.left,t2.left)t1.right=self.mergeTrees2(t1.right,t2.right)return t1

5.2迭代

首先把两棵树的根节点入栈,栈中的每个元素都会存放两个根节点,并且栈顶的元素表示当前需要处理的节点。
以t1作为最后的输出返回,
当前结点的处理( 在stack里面的东西都是非空的):
两者相加的值放入t1.val
子结点的处理:
t1没有做孩子,t2的左孩子给t1.
t1,t2同时有左孩子,将其同时入栈,
右孩子的处理同理。

class Solution(object):def mergeTrees(self, t1, t2):if t1==None:return t2if t2==None:return t1stack=[(t1,t2)]while(stack):node1,node2=stack.pop() # 在stack里面的东西都是非零的node1.val+=node2.valif node1.left==None:node1.left=node2.leftelif node1.left and node2.left:  # 1.left 和2.left同时非零stack.append([node1.left,node2.left])if node1.right==None:node1.right=node2.right      # 放过来之后就有。elif  node1.right and node2.right:  # 1.left 和2.left同时非零stack.append([node1.right,node2.right])return t1

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

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

相关文章

利用posix_fadvise清理系统中的文件缓存

利用posix_fadvise清理系统中的文件缓存leoncom c/c,unix2011-08-03当我们需要对某段读写文件并进行处理的程序进行性能测试时&#xff0c;文件会被系统cache住从而影响I/O的效率&#xff0c;必须清理cache中的对应文件的才能正确的进行性能测试。通常清理内存可以采用下面的这…

空间分配

目前主流的垃圾收集器都会采用分代回收算法&#xff0c;因此需要将堆内存分为新生代和老年代&#xff0c;这样我们就可以根据各个年代的特点选择合适的垃圾收集算法。 大多数情况下&#xff0c;对象在新生代中 eden 区分配。当 eden 区没有足够空间进行分配时&#xff0c;虚拟…

创建与打开IPC通道的POSIX和SYSTEM V方法

先说&#xff30;&#xff2f;&#xff33;&#xff29;&#xff38;的吧&#xff1a; mq_open&#xff0c;sem_open&#xff0c;shm_open着三个函数用于创建或者打开一个IPC通道。 由此可见&#xff0c;消息队列的读写权限是任意的&#xff0c;然而信号灯就没有&#xff0c;…

软件测试基础知识

第一章 1.1 软件测试背景知识和发展史 互联网公司职位架构&#xff1a;产品 运营 技术 市场 行政软件测试&#xff1a;使用人工或自动化手段&#xff0c;来运行或测试某个系统的过程&#xff0c;其目的在于检验它是否满足规定的需求或弄清预期结果与实际结果之间的差别&#…

Jmeter-基础篇

常用压力测试工具对比 1、loadrunner 性能稳定&#xff0c;压测结果及细粒度大&#xff0c;可以自定义脚本进行压测&#xff0c;但是太过于重大&#xff0c;功能比较繁多 2、apache ab(单接口压测最方便) 模拟多线程并发请求,ab命令对发出负载的计算机…

shell一文入门通

简单来说“Shell编程就是对一堆Linux命令的逻辑化处理”。 W3Cschool 上的一篇文章是这样介绍 Shell的 hello world 学习任何一门编程语言第一件事就是输出HelloWord了&#xff01;下面我会从新建文件到shell代码编写来说下Shell 编程如何输出Hello World。 (1)新建一个文件…

RPC编程

图 3 说明在客户机和服务器之间完成 RPC 涉及的步骤。 图 3. 在客户机和服务器之间完成 RPC 涉及的步骤服务器 RPC 应用程序初始化期间它会向 RPC 运行时库注册接口。需要注册接口是因为&#xff0c;客户机在向服务器发出远程过程调用时&#xff0c;要检查它是否与服务器兼容。…

synchronized使用和原理全解

synchronized是Java中的关键字&#xff0c;是一种同步锁。它修饰的对象有以下几种&#xff1a; 修饰一个方法 被修饰的方法称为同步方法&#xff0c;其作用的范围是整个方法&#xff0c;作用的对象是调用这个方法的对象&#xff1b; 修饰一个静态的方法 其作用的范围是整个…

算法(11)-leetcode-explore-learn-数据结构-链表的经典问题

leetcode-explore-learn-数据结构-链表31.反转一个链表2.移除链表元素3.奇偶链表4.回文链表5.小结本系列博文为leetcode-explore-learn子栏目学习笔记&#xff0c;如有不详之处&#xff0c;请参考leetcode官网&#xff1a;https://leetcode-cn.com/explore/learn/card/linked-l…

Linux线程池的设计

我设计这个线程池的初衷是为了与socket对接的。线程池的实现千变万化&#xff0c;我得这个并不一定是最好的&#xff0c;但却是否和我心目中需求模型的。现把部分设计思路和代码贴出&#xff0c;以期抛砖引玉。个人比较喜欢搞开源&#xff0c;所以大家如果觉得有什么需要改善的…

leetcode121买卖股票的最佳时机

给定一个数组&#xff0c;它的第 i 个元素是一支给定股票第 i 天的价格。 如果你最多只允许完成一笔交易&#xff08;即买入和卖出一支股票&#xff09;&#xff0c;设计一个算法来计算你所能获取的最大利润。 注意你不能在买入股票前卖出股票。 示例 1: 输入: [7,1,5,3,6,…

epoll的内核实现

epoll是由一组系统调用组成。 int epoll_create(int size); int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event); int epoll_wait(int epfd, struct epoll_event *events,int maxevents, int timeout); select/poll的缺点在于&#xff1…

leetcode322 零钱兑换

给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额&#xff0c;返回 -1。 示例 1: 输入: coins [1, 2, 5], amount 11 输出: 3 解释: 11 5 5 1 示例 2: 输入: coins [2],…

leetcode538 把二叉搜索树转换成累加树

给定一个二叉搜索树&#xff08;Binary Search Tree&#xff09;&#xff0c;把它转换成为累加树&#xff08;Greater Tree)&#xff0c;使得每个节点的值是原来的节点值加上所有大于它的节点值之和。 对于每一个点来说&#xff0c;自己的父&#xff0c;和自己父的右子树都是大…

leetcode15 三数之和

给定一个包含 n 个整数的数组 nums&#xff0c;判断 nums 中是否存在三个元素 a&#xff0c;b&#xff0c;c &#xff0c;使得 a b c 0 &#xff1f;找出所有满足条件且不重复的三元组。 注意&#xff1a;答案中不可以包含重复的三元组。 例如, 给定数组 nums [-1, 0, 1,…

leetcode19. 删除链表的倒数第N个节点

给定一个链表&#xff0c;删除链表的倒数第 n 个节点&#xff0c;并且返回链表的头结点。 示例&#xff1a; 给定一个链表: 1->2->3->4->5, 和 n 2. 当删除了倒数第二个节点后&#xff0c;链表变为 1->2->3->5. 说明&#xff1a; 给定的 n 保证是有效…

python模块(5)-Matplotlib 简易使用教程

Matplotlib简易使用教程0.matplotlib的安装1.导入相关库2.画布初始化2.1 隐式创建2.2 显示创建2.3 设置画布大小2.4 plt.figure()常用参数3.plt. 能够绘制图像类型3.1等高线3.2 箭头arrow4.简单绘制小demodemo1.曲线图demo2-柱状、饼状、曲线子图5.plt.plot()--设置曲线颜色,粗…

leetcode20 有效的括号

给定一个只包括 (&#xff0c;)&#xff0c;{&#xff0c;}&#xff0c;[&#xff0c;] 的字符串&#xff0c;判断字符串是否有效。 有效字符串需满足&#xff1a; 左括号必须用相同类型的右括号闭合。 左括号必须以正确的顺序闭合。 注意空字符串可被认为是有效字符串。 示…

leetcode1 两数之和

给定一个整数数组 nums 和一个目标值 target&#xff0c;请你在该数组中找出和为目标值的那 两个 整数&#xff0c;并返回他们的数组下标。 你可以假设每种输入只会对应一个答案。但是&#xff0c;你不能重复利用这个数组中同样的元素。 示例: 给定 nums [2, 7, 11, 15], t…

leetcode3 无重复字符最长子串

给定一个字符串&#xff0c;请你找出其中不含有重复字符的 最长子串 的长度。 示例 1: 输入: "abcabcbb" 输出: 3 解释: 因为无重复字符的最长子串是 "abc"&#xff0c;所以其长度为 3。 示例 2: 输入: "bbbbb" 输出: 1 解释: 因为无重复字符…