leetcode 二叉树学习笔记(python3)

  把二叉树的学习历程记录一下。

遍历二叉树

  
  
在这里插入图片描述  
  如上图所示,二叉树是由父节点与子节点组成的,每个父节点最多有一左一右两个子节点,子节点不能脱离父节点单独存在。
  前序遍历:前序遍历先访问根节点,再遍历左子树,最后遍历右子树。简记为根->左->右。上图中的二叉树根据前序遍历得到的结果为:

F、B、A、D、C、E、G、I、H

  从遍历的过程中可以直观地看到,前序遍历和其名称一样,在左右两侧分别严格进行从上至下的遍历。
  中序遍历:中序遍历先遍历左子树,再访问根节点,最后遍历右子树。简记为左->根->右。上图中的二叉树根据中序遍历得到的结果为:

A、B、C、D、E、F、G、H、I

  从遍历的过程中可以看到,中序遍历的形式接近于将整个二叉树进行从左至右一层层地遍历。
  后序遍历:后序遍历先遍历左子树,再遍历右子树,最后访问根节点。简记为左->右->根。这里可以看出无论是前序,中序还是后续遍历,左子树一定是优先于右子树的。上图中的二叉树根据后序遍历得到的结果为:

A、C、E、D、B、H、I、G、F

  从遍历的过程中可以看到,后序遍历的形式接近于在左右两个子树中从后向前遍历。
  在程序中,可以使用递归和迭代两种方式来实现二叉树遍历。

递归

  由于二叉树中相连接的节点互为亲子代,因此二叉树是个天然适合递归的数据结构,实际上在代码中二叉树的递归也比迭代写起来更方便。
  以较为简单的前序遍历为例。在算法书中,递归函数需要有三个组成部分:
  递归函数的返回值与参数,递归结束的终止条件,单次递归中执行的功能
  这里以python3举例,构建一个前序遍历二叉树的递归函数。
  递归函数的返回值与参数
  由于我们是将遍历的结果存放在列表里面的,每次递归只是将遍历到的节点存放到列表之中,因此该函数不需要返回值。至于函数的参数,由于我们要遍历二叉树,因此肯定是要将二叉树的根节点传进来的,因此参数就应当是一个二叉树的节点形式。

	def preorder(root:TreeNode):

  递归结束的终止条件
  递归是按照一定的逻辑进行不断操作的,因此我们需要为其增加一个终止条件。显然递归的过程中,当前节点若是不存在,就应该跳出该层递归。

			if root == None:
                return

  单次递归中执行的功能
  每一次递归中需要按照顺序执行前向递归的逻辑。

 			res.append(root.val)
 			inorder(root.left)
            inorder(root.right)

  这样前序遍历的递归函数就写好了:

		def preorder(root:TreeNode):
            if root == None:
                return 
            res.append(root.val)
 			inorder(root.left)
            inorder(root.right)

  同理,中序遍历的递归函数:

		def inorder(root:TreeNode):
            if root == None:
                return 
            inorder(root.left)
            res.append(root.val)
            inorder(root.right)

  后序遍历的递归函数:

		def postorder(root:TreeNode):
            if root == None:
                return
            postorder(root.left)
            postorder(root.right)
            res.append(root.val)

  遍历法之所以很简洁,因为其隐式地维护了一个栈,所以代码量比较少。
  但是,遍历法的代码太定式了,有点偷懒,因此还需要学习迭代法。

迭代法

  迭代法的本质是将递归法中的栈显式地人工写出来,因此代码量明显增加。
  首先以前序遍历为例子。前序遍历的逻辑是存放根节点,遍历左子树,再遍历右子树。按照这样的逻辑,我们应当在栈中先放入根节点的右子节点,再放入根节点的左子节点,如此,在出栈时,先弹出左子节点(先进先出),再弹出右子节点,顺序放入记录用的列表即可。
  完整代码如下所示:

class Solution:
    def preorderTraversal(self, root: TreeNode) -> List[int]:
        res = list()  # 存结果的列表
        stack = [root]  # 栈,先将根节点放入
        if root == None:  # 如果二叉树是空的就直接返回
            return res
        while stack: # 如果栈内一直有节点就循环
            node = stack.pop() # 将栈顶的节点取出
            if node != None:
                res.append(node.val) # 将栈顶节点的值记录到列表中
                if node.right:  # 如果栈顶节点有右子树
                    stack.append(node.right)  # 将栈顶节点的右子节点放入栈
                if node.left:  # 如果栈顶节点有左子树
                    stack.append(node.left)  # 将栈顶节点的左子节点放入栈
                    
        return res

  在常规迭代算法的基础上衍生了模板流迭代算法。
  模板流的思路与常规迭代的不同,模板流的思路为,从根节点开始,遍历所有的孩子,并将它们都存放在栈中。
  随后将栈内节点依次弹出,每弹出一个节点,就将该节点的右子节点视作当前节点,并继续上面的操作。
  模板的前序遍历完整代码如下:

class Solution:
    def preorderTraversal(self, root: TreeNode) -> List[int]:
        res = list() # 存放结果的列表
        stack = list()  # 栈
        cur = root  # 当前节点,初始化为根节点
        if root == None:  # 如果根节点为空直接返回
            return res
        while stack or cur: # 如果栈内有节点或者当前节点存在,继续循环,注意如果不考虑当前节点存在,将无法开始循环(初始栈为空)
            while cur: # 对于每个当前节点,都需要执行该循环
                res.append(cur.val)  # 将当前节点的值存入列表
                stack.append(cur)  # 将当前节点入栈
                cur = cur.left  # 当前节点不断在左子树遍历
            temp = stack.pop() # 当前节点的左子树遍历完成后,将栈顶节点取出
            cur = temp.right  # 将栈顶节点的右子节点当做当前节点

        return res

  迭代法还可以用于层序遍历,层序遍历是将二叉树按照每层的形式进行记录,属于广度优先搜索。

class Solution:
    def levelOrder(self, root: TreeNode) -> List[List[int]]:
        res = list()  # 存放结果的列表
        if root == None:
            return res
        q = [root]  # 队列,先存放根节点
        while q: # 如果队列中存在节点
            n = len(q) # 当前队列中的节点数量
            level = [] # 存放每层节点的列表
            for i in range(n): # 当前队列中的每个节点都执行一次循环体内的内容 
                node = q.pop(0) # 队列形式,先进先出,取队列中节点出来
                level.append(node.val)  # 将当前节点记录到该层里
                if node.left: # 如果该节点还有左孩子,就将左孩子放入队列中,因为队列先进先出,所以这一层不会包含该左孩子
                    q.append(node.left)
                if node.right: # 如果该节点还有右孩子,就将右孩子放入队列中,因为队列先进先出,所以这一层不会包含该右孩子
                    q.append(node.right)
            res.append(level) # 将该层列表加入记录列表中
        return res
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值