LeetCode二叉树

二叉树

LeetCode二叉树刷题记录

基础知识

二叉树:树中各个节点的度小于等于2的有序树

二叉树可以分为五种形态

  1. 空树
  2. 只有根节点
  3. 只有左子树
  4. 只有右子树
  5. 左右子树都有

种类

满二叉树:除了叶子节点,所有节点的度都为2,叶子节点一定都在最后一层

深度为 k k k的完全二叉树,其节点总数为 2 k − 1 2^k - 1 2k1

完全二叉树:叶子节点只能出现在最后两层,且最后一层的叶子节点从左往右依次排列

同等节点的二叉树中,完全二叉树的深度最小;节点数为 n n n的完全二叉树可以理解为,同等深度的满二叉树取前 n n n个节点

二叉搜索树:任意节点的左子树若不为空,则左子树节点小于根节点;右子树若不为空,则右子树节点大于根节点

平衡二叉树:是二叉搜索树的一种,任意节点的左右子树高度差不超过1

存储结构

  • 顺序存储:保存在数组中,树中的节点一层层往下存储,每层从左到右存储
    left = $ 2 \times i + 1$ right = $ 2 \times i + 2$ parent = ( i − 1 ) / / 2 (i - 1) // 2 (i1)//2
  • 链式存储:保存在链表中,每个节点有一个数据域,还有两个指针域,分别指向左子树和右子树
class TreeNode:
	def __init__(self,val = 0, left = None, right = None):
		self.val = val
		self.left = left
		self.right = right

遍历

深度优先遍历

尽可能"深"地访问每个分支,直到到达叶子节点为止,然后回溯到上一个节点,遍历另一个分支

  1. 前序遍历:根节点 -> 左子树 -> 右子树
  2. 中序遍历:左子树 -> 根节点 -> 右子树
  3. 后序遍历:左子树 -> 右子树 -> 根节点

这里的"前中后"指的是根节点遍历时的顺序

深度优先遍历一般用递归来实现,也可以用栈

递归三要素:

  1. 确定递归函数的参数和返回值
  2. 确定递归终止条件
  3. 单层递归逻辑:假定能解决 k k k规模的问题,如何解决 k + 1 k+1 k+1规模的问题(数学归纳法)
前序遍历

我一开始使用list拼接来做,但这样需要额外的空间(每次递归时都要创建新的list),并不划算。不能直接在result列表上修改的原因是,result没有作为参数传入,可以考虑定义一个辅助函数

class Solution:
    def preorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        result = []
        if root == None:
            return result
        result.append(root.val)
        if root.left != None:
            resultleft = self.preorderTraversal(root.left)
            result = result + resultleft
        if root.right != None:
            resultright = self.preorderTraversal(root.right)
            result = result + resultright
        return result

改进版本:

class Solution:
    def preorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        result = []

        def preorder(root:Optional[TreeNode],result:List[int]) -> None:
            if root == None:
                return
            result.append(root.val)
            preorder(root.left, result) # 如果left为空子树,下一层递归的时候会自动退出的
            preorder(root.right, result)

        preorder(root,result)
        return result

在计算机内部,递归的函数调用就是用栈来实现的,每次递归调用都会将当前状态压入栈中,直到递归结束再弹出栈,因此我们也可以用栈来显示的模拟递归过程

tree1

递归栈的调用过程

  1. 调用 preorder(1)

    • 访问 1
    • 调用 preorder(2)(左子树)
  2. 调用 preorder(2)

    • 访问 2
    • 调用 preorder(4)(左子树)
  3. 调用 preorder(4)

    • 访问 4
    • 左子树为空,返回
    • 右子树为空,返回
  4. 返回到 preorder(2)

    • 调用 preorder(5)(右子树)
  5. 调用 preorder(5)

    • 访问 5
    • 左子树为空,返回
    • 右子树为空,返回
  6. 返回到 preorder(1)

    • 调用 preorder(3)(右子树)
  7. 调用 preorder(3)

    • 访问 3
    • 左子树为空,返回
    • 右子树为空,返回

如果我们想用显示的栈,来模拟这一调用过程,最需要解决的问题是如何“回退”,比如当调用完preorder(4)之后,发现左右子树为空,返回上一级调用preorder(2),开始遍历右子树,如果用显示的栈,应该如何实现这一个过程呢?

要想能遍历右子树,就需要在调用preorder(2)的时候把左右子树的信息都暂存到栈里,这样才能在遍历完左子树之后遍历右子树,由于栈是后进先出的数据结构,我们希望先遍历左子树,就要后把左子树的信息存到栈里,因此先存右子树信息,后存左子树信息

栈的数据结构在此处起到一个“记录”的作用,只有保存了当前递归的状态(左右子树的信息)才能回退,完成对树的遍历

显式栈的执行过程:

  1. 初始化栈:stack = [1]

  2. 弹出 1,访问 1

    • 将右孩子 3 压入栈:stack = [3]
    • 将左孩子 2 压入栈:stack = [3, 2]
  3. 弹出 2,访问 2

    • 将右孩子 5 压入栈:stack = [3, 5]
    • 将左孩子 4 压入栈:stack = [3, 5, 4]
  4. 弹出 4,访问 4

    • 没有孩子,stack = [3, 5]
  5. 弹出 5,访问 5

    • 没有孩子,stack = [3]
  6. 弹出 3,访问 3

    • 没有孩子,stack = []

当栈为空的时候表示遍历完成了

class Solution:
    def preorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        stack = [root]
        result = []
        while stack:
            parent = stack.pop()
            if parent == None:
                break
            if parent.right != None:
                stack.append(parent.right)
            if parent.left != None:
                stack.append(parent.left)
            result.append(parent.val)
        return result
后序遍历
class Solution:
    def postorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        def postorder(root:Optional[TreeNode], result:list) -> None:
            if root == None:
                return 
            postorder(root.left,result)
            postorder(root.right,result)
            result.append(root.val)
            return result
        
        result = []
        postorder(root,result)
        return result

我的思路就是从递归的栈直接改写成显示的栈,因为后序遍历是左子树->右子树->根节点,所以根节点先入栈,再右子树,再左子树,等到遍历到左右子树都为空的节点之后开始返回上一层,于是我写出来如下程序

class Solution:
    def postorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        stack = [root]
        result = []
        while stack:
            parent = stack[-1]
            if parent == None:
                break
            if parent.right == None and parent.left == None:
                result.append(parent.val)
                stack.pop()
                continue
            if parent.right != None:
                stack.append(parent.right)
            if parent.left != None:
                stack.append(parent.left)
        return result

但这个程序有一个地方是错的,当遍历到叶子节点,返回上一层的时候,叶子节点又重新被加入了栈,导致无限死循环,所以需要有一个容器来记录一下哪些被遍历过,哪些没有,遍历过的节点又遍历,说明其左右子树都已经遍历完了,回退到该节点

class Solution:
    def postorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        stack = [root]
        result = []
        visit = set()
        while stack:
            parent = stack[-1]
            
            if parent == None:
                break
            if parent.right == None and parent.left == None or (parent in visit):
                result.append(parent.val)
                stack.pop()
                continue
            if parent.right != None:
                stack.append(parent.right)
            if parent.left != None:
                stack.append(parent.left)
            visit.add(parent)
        return result
中序遍历
class Solution:
    def inorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        def inorder(root:Optional[TreeNode], result:List[int]) -> None:
            if root == None:
                return
            inorder(root.left, result)
            result.append(root.val)
            inorder(root.right,result)
            return result
        
        result = []
        inorder(root,result)
        return result

我的思路就是从递归的栈直接改写成显示的栈,因为中序遍历是左子树->根节点->右子树,所以右子树先入栈,再入根节点,再左子树,等到遍历到左右子树都为空的节点之后开始返回上一层

class Solution:
    def inorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        stack = [root]
        result = []
        visit = set()
        while stack:
            parent = stack.pop()
            if parent == None:
                continue
            if (parent.left == None and parent.right == None) or (parent in visit):
                result.append(parent.val)
                continue
            if parent.right != None:
                stack.append(parent.right)
            stack.append(parent)
            if parent.left != None:
                stack.append(parent.left)
            visit.add(parent)

        return result

为什么前序遍历不需要一个visit集合来记录当前节点是否被遍历过,而后序和中序需要呢?因为前序遍历要访问的节点和要处理的节点是一致的(访问节点表示当前获取到指针的节点,访问节点永远是根节点,处理节点表示要输出的节点)所以可以直接result.append(parent.val)

广度优先遍历

逐层访问节点,先访问离起始节点近的,再访问离起始节点远的

层序遍历:层之间从上到下,每层内部从左到右

广度优先遍历一般可以用队列来实现

tree2

层序遍历希望一层层输出,即result = [1, 2, 3, 4, 5, 6]但是二叉树的访问结构决定当我们访问跟节点的时候,我们只能取遍历它的左右孩子节点,而不能遍历到它的兄弟节点,所以我们需要引入队列这种数据结构来辅助,被遍历到的节点都暂存在队列中,当收集完一层的节点后统一输出,一边输出一遍加入下一层的节点

二叉树的层序遍历
class Solution:
    from collections import deque
    def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
        result = []
        queue = deque()
        if root != None:
            queue.append(root)
        while queue:
            size = len(queue)
            # 记录每层节点数
            tmp = []
            while size > 0:
                node = queue.popleft()
                tmp.append(node.val)
                if node.left != None:
                    queue.append(node.left)
                if node.right != None:
                    queue.append(node.right)
                size -= 1
            result.append(tmp)
        return result
二叉树的层序遍历II

返回二叉树自底向上的层序遍

class Solution:
    from collections import deque
    def levelOrderBottom(self, root: Optional[TreeNode]) -> List[List[int]]:
        queue = deque()
        result = []
        if root != None:
            queue.append(root)
        while queue:
            size = len(queue)
            tmp = []
            while size > 0:
                node = queue.popleft()
                tmp.append(node.val)
                if node.left != None:
                    queue.append(node.left)
                if node.right != None:
                    queue.append(node.right)
                size -= 1
            result.append(tmp)
        return result[::-1]
二叉树的右视图

想象自己站在二叉树的右侧,返回能看到的节点,也就是返回每层最后一个节点,可以使用层序遍历

class Solution:
    from collections import deque
    def rightSideView(self, root: Optional[TreeNode]) -> List[int]:
        queue = deque()
        result = []
        if root != None:
            queue.append(root)
        while queue:
            size = len(queue)
            while size > 0:
                node = queue.popleft()
                if size == 1:
                    result.append(node.val)
                if node.left != None:
                    queue.append(node.left)
                if node.right != None:
                    queue.append(node.right)
                size -= 1
        return result
二叉树的层平均值
class Solution:
    from collections import deque
    def averageOfLevels(self, root: Optional[TreeNode]) -> List[float]:
        queue = deque()
        result = []
        if root != None:
            queue.append(root)
        while queue:
            size = len(queue)
            n = size
            sum = 0
            while size > 0:
                node = queue.popleft()
                sum += node.val
                if node.left != None:
                    queue.append(node.left)
                if node.right != None:
                    queue.append(node.right)
                size -= 1
            result.append(sum/n)
        return result
N叉树的层序遍历

注意N叉树的节点定义

"""
# Definition for a Node.
class Node:
    def __init__(self, val: Optional[int] = None, children: Optional[List['Node']] = None):
        self.val = val
        self.children = children
"""

class Solution:
    from collections import deque
    def levelOrder(self, root: 'Node') -> List[List[int]]:
        queue = deque()
        result = []
        if root != None:
            queue.append(root)
        while queue:
            size = len(queue)
            tmp = []
            while size > 0:
                node = queue.popleft()
                tmp.append(node.val)
                n = 0
                while n < len(node.children):
                    if node.children[n] != None:
                        queue.append(node.children[n])
                    n += 1
                size -= 1
            result.append(tmp)
        return result
在每个树行中找最大值
class Solution:
    from collections import deque
    def largestValues(self, root: Optional[TreeNode]) -> List[int]:
        queue = deque()
        result = []
        if root != None:
            queue.append(root)
        while queue:
            size = len(queue)
            max = -float('inf')
            while size > 0:
                node = queue.popleft()
                if node.val > max:
                    max = node.val
                if node.left != None:
                    queue.append(node.left)
                if node.right != None:
                    queue.append(node.right)
                size -= 1
            result.append(max)
        return result   
填充每个节点的下一个右侧节点指针

填充每个节点的next指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将next指针设置为NULL

class Solution:
    from collections import deque
    def connect(self, root: 'Optional[Node]') -> 'Optional[Node]':
        queue = deque()
        if root != None:
            queue.append(root)
        while queue:
            size = len(queue)
            pre = None
            while size > 0:
                node = queue.popleft()
                if pre:
                    pre.next = node
                pre = node
                if pre.left != None:
                    queue.append(pre.left)
                if pre.right != None:
                    queue.append(pre.right)
                size -= 1
        return root
二叉树的最大深度
class Solution:
    from collections import deque
    def maxDepth(self, root: Optional[TreeNode]) -> int:
        queue = deque()
        max = 0
        if root != None:
            queue.append(root)
        while queue:
            size = len(queue)
            max += 1
            while size > 0:
                node = queue.popleft()
                if node.left != None:
                    queue.append(node.left)
                if node.right != None:
                    queue.append(node.right)
                size -= 1
        return max 
二叉树的层序遍历
class Solution:
    from collections import deque
    def minDepth(self, root: Optional[TreeNode]) -> int:
        queue = deque()
        min = 0
        if root != None:
            queue.append(root)
        while queue:
            size = len(queue)
            min += 1
            while size > 0:
                node = queue.popleft()
                if node.left == None and node.right == None:
                    # 遇到叶子节点
                    return min
                if node.left != None:
                    queue.append(node.left)
                if node.right != None:
                    queue.append(node.right)
                size -= 1
        return min

题1:翻转二叉树

二叉树的问题,解题前要想清楚用前中后序遍历,还是层序遍历

class Solution:
    def invertTree(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
        if root == None:
            return
        root.left = self.invertTree(root.left) #翻转左子树
        root.right = self.invertTree(root.right) #翻转右子树
        root.left, root.right = root.right, root.left # 左右子树翻转
        return root

虽然我能写出翻转二叉树,但我开发不出Homebrew啊

注意:此题我用的是后序遍历,即先处理左子树和右子树,最后处理根节点(交换左右子树);也可以采用前序遍历,先交换根节点的左右子树,再处理左子树和右子树,中序遍历就不太行

如果非要中序遍历

class Solution:
    def invertTree(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
        if root == None:
            return
        root.left = self.invertTree(root.left) #翻转左子树
        root.left, root.right = root.right, root.left # 左右子树翻转
        root.left = self.invertTree(root.left) #翻转左子树,原来的右子树
        return root
  1. 函数返回值和参数:节点
  2. 终止条件:当前子树为空树
  3. 每层的处理规则:先 f ( l e f t ) f(left) f(left)交换好左子树,再 f ( r i g h t ) f(right) f(right)交换好右子树,最后交换左右子树,当前树就翻转好了

层序遍历也可以,每层翻转左右子树

class Solution:
    from collections import deque
    def invertTree(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
        queue = deque()
        if root != None:
            queue.append(root)
        while queue:
            size = len(queue)
            while size > 0:
                node = queue.popleft()
                node.left, node.right = node.right, node.left # 放前放后无所谓
                if node.left != None:
                    queue.append(node.left)
                if node.right != None:
                    queue.append(node.right)
                size -= 1
        return root

题2:对称二叉树

一开始想,检测每层的"对称性"所以采用了层序遍历,特别要注意的是,如果左子树右子树在某个位置为空,层序遍历的时候那层的这个位置需要用none来填充,不然的话,如下情况会被误判为对称

nonsymmetric

class Solution:
    from collections import deque
    def isSymmetric(self, root: Optional[TreeNode]) -> bool:
        queue = deque()
        flag = True
        if root != None:
            queue.append(root)
        while queue:
            n = len(queue)
            tmp = []
            while n > 0:
                node = queue.popleft()
                if node == None:
                    tmp.append(None)
                else:
                    tmp.append(node.val)
                    queue.append(node.left)
                    queue.append(node.right)
                n -= 1
            if tmp[:] != tmp[::-1]:
                # 这层不对称
                flag = False
        return flag

这个题目也可以用深度优先遍历里面的后序遍历

如何判断一棵树是否是对称二叉树呢?这棵树的左右子树是否对称,那么怎么判断左右子树是否对对称呢?在左右子树根节点相同的基础上,判断左子树的左子树是否和右子树的右子树对称,左子树的右子树是否和右子树的左子树对称

所以假设我有一个函数能判断两个树是否为对称的树,每次只要调用 f ( l e f t r e e . l e f t , r i g h t r e e . r i g h t ) f(leftree.left, rightree.right) f(leftree.left,rightree.right) f ( l e f t r e e . r i g h t , r i g h t r e e . l e f t ) f(leftree.right,rightree.left) f(leftree.right,rightree.left) 如果两次调用得到的结果都是对称,加上当前两个根节点相同,就是对称

典型递归的问题:

  1. 参数和返回值:要比较两个树是否对称,传入两个树的指针,返回是否对称,布尔类型数据
  2. 边界条件:如果两个树中有空树
  3. 每层处理规则:判断tree1的左子树是否和tree2的右子树对称,tree1的右子树是否和tree2的左子树对称
class Solution:
    def isSymmetric(self, root: Optional[TreeNode]) -> bool:
        def compare(tree1:Optional[TreeNode], tree2:Optional[TreeNode]) -> bool:
            if tree1 == None and tree2 == None:
                return True
            if tree1 == None and tree2 != None:
                return False
            if tree1 != None and tree2 == None:
                return False
            flag1 = compare(tree1.left, tree2.right)
            flag2 = compare(tree1.right, tree2.left)
            return flag1 and flag2 and tree1.val == tree2.val
        if root == None:
            return True
        return compare(root.left, root.right)

注意这是一种后序遍历的逻辑,先处理完子树,再处理本节点,这和tree1.val == tree2.val这条语句放在哪里无关,这条语句可以放在前面,但是必须得等子树处理好后才能返回,还是理解为后序遍历

也可以用迭代做,思路是一样的。用队列这种数据结构,每次比较一对节点,并且把这对节点中的节点1的左子树加入队列,再把节点2的右子树加入队列,把节点1的右子树加入队列,再把节点2的左子树加入队列 — “从两边到中间”原则

class Solution:
    from collections import deque
    def isSymmetric(self, root: Optional[TreeNode]) -> bool:
        if root == None:
            return True
        queue = deque()
        queue.append(root.left)
        queue.append(root.right)

        while queue:
            node1 = queue.popleft()
            node2 = queue.popleft()
            ## 一对对弹出
            ## node1在同一层中从左到右
            ## node2在同一层从右到左
            ## 有点类似层序,不过把比较每层的对称性这一步直接和遍历过程融合了
            if node1 == None and node2 == None:
                continue
            if node1 == None and node2 != None:
                return False
            if node1 != None and node2 == None:
                return False
            if node1.val != node2.val:
                return False
            # 加入下一层节点,不过此处不用明显区分层,直接按顺序比较下去
            # 因为节点被配对好了
            queue.append(node1.left)
            queue.append(node2.right)
            queue.append(node1.right)
            queue.append(node2.left)
        return True

题3:相同的树

感觉比对称好想,直接递归的比较左子树和右子树是否相等就可以。同样用迭代,层序都可以

class Solution:
    def isSameTree(self, p: Optional[TreeNode], q: Optional[TreeNode]) -> bool:
        if p == None and q == None:
            return True
        if p != None and q == None:
            return False
        if p == None and q != None:
            return False
        if q.val != p.val :
            return False
        return self.isSameTree(p.left,q.left) and self.isSameTree(p.right,q.right)

题4:另一棵树的子树

一开始我想到的是暴力解法,遍历每个节点,检查该节点的子树是否和目标数相等,时间复杂度是 O ( ∣ s ∣ × ∣ t ∣ ) O(\vert s \vert \times \vert t \vert) O(s×t) 其中 ∣ s ∣ \vert s \vert s表示树s的节点数量

class Solution:
    def isSubtree(self, root: Optional[TreeNode], subRoot: Optional[TreeNode]) -> bool:
        def isSameTree(p: Optional[TreeNode], q: Optional[TreeNode]) -> bool:
            if p == None and q == None:
                return True
            if p != None and q == None:
                return False
            if p == None and q != None:
                return False
            if q.val != p.val :
                return False
            return isSameTree(p.left,q.left) and isSameTree(p.right,q.right)

        if subRoot == None:
            return True
        if root == None and subRoot != None:
            return False
        if isSameTree(root, subRoot):
            return True
        return self.isSubtree(root.left, subRoot) or self.isSubtree(root.right, subRoot)

方法二:同样是暴力搜索,但是不引入辅助函数,代码更简洁

初看这道题,感觉递归的影子非常明显,假设我有一个函数能检查一棵树是否为另一棵的子树,那么我只需要检查f(root.left, subRoot) f(root.right, subRoot) f(root, subRoot) 在试图拆解问题的时候,我尝试把它分为原树的左子树是否包含目标子树,原树的右子树是否包含目标子树,然后?解决当前节点出问题了,难道我验证原树是否包含目标子树吗?这样就不符合递归把大问题拆成小问题的原则,而是把大问题拆分成了两个小问题和一个大问题

为了解决这个问题,把问题拆解成,原树的左子树是否包含目标子树,原树的右子树是否包含目标子树,原树是否和和目标子树相等(包含目标子树,说明在某个节点上,目标子树和原树的子树相等)然后引入一个变量flag来记录当前是在判断相等还是在检查子树

class Solution:
    def isSubtree(self, root: Optional[TreeNode], subRoot: Optional[TreeNode], flag : bool = True) -> bool:
        if subRoot == None and root == None:
            return True
        if subRoot != None and root == None:
            return False
        if root != None and subRoot == None:
            return flag # 是子树但不是相等
        
        result = False

        if flag:
            # 如果当前在判断子树模式
            if root.val == subRoot.val and self.isSubtree(root.left, subRoot.left, False) and self.isSubtree(root.right, subRoot.right, False):
                # 判断原树是否为目标子树,进入判断相等模式
                return True
            # 如果当前节点不匹配,递归检查 root 的左子树或右子树
            return self.isSubtree(root.left, subRoot) or self.isSubtree(root.right, subRoot)
        else: 
            # 当前在判断相等模式
            return root.val == subRoot.val and self.isSubtree(root.left, subRoot.left, False) and self.isSubtree(root.right, subRoot.right, False)

复杂度 O ( ∣ s ∣ × ∣ t ∣ ) O(\vert s \vert \times \vert t \vert) O(s×t)与暴力法没本质区别

  • flagTrue时,表示当前调用是在查找subRoot是否是root的子树
  • flagFalse时,表示当前调用是在检查两棵树是否完全相同

好丑陋的代码,就为了用一个函数实现递归,毫无必要

题5:二叉树的最大深度

在层序遍历中做过这个题二叉树的最大深度,一层层遍历计数,此处不用层序遍历再做一遍

  • 深度:深度是指从树的根节点到某个节点的路径长度(即经过的边的数量),深度是从上往下计算的(根左右,前序遍历)
  • 高度:高度是指从某个节点到它的最远叶子节点的路径长度,高度是从下往上计算的(左右根 后序遍历)

不过根节点的高度就是二叉树的最大深度,所以用前序遍历和后序遍历解这道题都可以

前序遍历(标准的深度求法)

根左右,每次深度都加1,但是当前我在遍历根的时候我不知道深度是多少,这个要交给后续的遍历左子树右子树去算,所以在函数里面传递深度这个参数,返回值也是深度

  1. 参数和返回值:传递当前根节点指针和当前深度
  2. 边界条件:空节点,直接返回深度(深度到空节点就不在增加)
  3. 每层处理逻辑:当前节点不为空,深度+1,求左子树的深度,求右子树的深度,取它们的最长深度,返回
class Solution:
    def maxDepth(self, root: Optional[TreeNode], depth = 0) -> int:
        if root == None:
            return depth
        return max(self.maxDepth(root.left,depth + 1), self.maxDepth(root.right,depth+1))

后序遍历

左右根,根节点的高度是左右节点+1

class Solution:
    def maxDepth(self, root: Optional[TreeNode]) -> int:
        if root == None:
            return 0
        return max(self.maxDepth(root.left), self.maxDepth(root.right)) + 1
  1. 参数和返回值:传递当前根节点指针
  2. 边界条件:空节点,直接返回0,空节点的高度为0
  3. 每层处理逻辑:求左子树的高度,求右子树的高度,它们的高度+1就是当前根节点的高度

*参考资料|代码随想录

n叉树的最大深度

class Solution:
    def maxDepth(self, root: 'Node') -> int:
        if root == None:
            return 0
        max = 0
        n = len(root.children) - 1
        while n >= 0:
            tmp = self.maxDepth(root.children[n])
            if tmp > max:
                max = tmp
            n -= 1
        return max + 1

题6:二叉树的最小深度

在层序遍历中做过这个题二叉树的最小深度,一层层遍历计数,碰到叶子节点直接返回,此处不用层序遍历再做一遍

最小深度:根节点到叶子节点的最短距离

后序遍历

class Solution:
    def minDepth(self, root: Optional[TreeNode]) -> int:
        if root == None:
            return 0
        if root.right == None:
            return self.minDepth(root.left) + 1
        if root.left == None:
            return self.minDepth(root.right) + 1
        return min(self.minDepth(root.left), self.minDepth(root.right)) + 1
  1. 参数和返回值:传递当前根节点指针
  2. 边界条件:空节点,直接返回0,空节点的高度为0
  3. 每层处理逻辑:若右子树不存在,求左子树的高度,若左子树不存在,求右子树最小高度,若左右子树都存在,两者中最小高度更小的加一就是当前根节点的高度

避免当遇到不存在的节点时返回的高度小于另外一侧的高度,造成误判,我们要找的是叶子节点而不是空节点

前序遍历

class Solution:
    def minDepth(self, root: Optional[TreeNode],depth:int = 0) -> int:
        if root == None:
            return depth
        if root.right == None:
            return self.minDepth(root.left, depth + 1)
        if root.left == None:
            return self.minDepth(root.right, depth + 1)
        return min(self.minDepth(root.left, depth + 1), self.minDepth(root.right, depth + 1))
  1. 参数和返回值:传递当前根节点指针和当前深度
  2. 边界条件:空节点,直接返回深度(深度到空节点就不在增加)
  3. 每层处理逻辑:当前节点不为空,深度+1,若右子树不存在,求左子树的深度,若左子树不存在,求右子树最小深度,若左右子树都存在,两者中最小深度更小的,返回

TBC
(未完待续)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值