当前位置:   article > 正文

Leetcode算法题之二叉树模板_二叉树题目模板

二叉树题目模板

一、树的定义

  1. # Definition for a binary tree node.
  2. class TreeNode:
  3. def __init__(self, val=0, left=None, right=None):
  4. self.val = val
  5. self.left = left
  6. self.right = right

二、树的遍历

基础知识

二叉树前序遍历的顺序为:

  1. 先遍历根节点;
  2. 随后递归地遍历左子树;
  3. 最后递归地遍历右子树。

二叉树中序遍历的顺序为:

  1. 先递归地遍历左子树;
  2. 随后遍历根节点;
  3. 最后递归地遍历右子树。

二叉树后序遍历的顺序为:

  1. 先递归地遍历左子树;
  2. 随后递归地遍历右子树;
  3. 最后遍历根节点。

对于任意一颗树而言,前序遍历的形式总是:

  • [ 根节点, [左子树的前序遍历结果],[右子树的前序遍历结果] ]

中序遍历的形式总是:

  • [ [左子树的中序遍历结果],根节点, [右子树的中序遍历结果] ]

后序遍历的形式总是:

  • [ [左子树的后序遍历结果],[右子树的后序遍历结果],根节点]

三、刷题模板

1、递归

  1. # 递归
  2. # 时间复杂度:O(n),n为节点数,访问每个节点恰好一次。
  3. # 空间复杂度:空间复杂度:O(h),h为树的高度。最坏情况下需要空间O(n),平均情况为O(logn)
  4. # 递归1:二叉树遍历最易理解和实现版本
  5. class Solution:
  6. def preorderTraversal(self, root: TreeNode) -> List[int]:
  7. if not root:
  8. return []
  9. # 前序递归
  10. return [root.val] + self.preorderTraversal(root.left) + self.preorderTraversal(root.right)
  11. # # 中序递归
  12. # return self.inorderTraversal(root.left) + [root.val] + self.inorderTraversal(root.right)
  13. # # 后序递归
  14. # return self.postorderTraversal(root.left) + self.postorderTraversal(root.right) + [root.val]
  15. # 递归2:通用模板,可以适应不同的题目,添加参数、增加返回条件、修改进入递归条件、自定义返回值
  16. class Solution:
  17. def preorderTraversal(self, root: TreeNode) -> List[int]:
  18. def dfs(cur):
  19. if not cur:
  20. return
  21. # 前序递归
  22. res.append(cur.val)
  23. dfs(cur.left)
  24. dfs(cur.right)
  25. # # 中序递归
  26. # dfs(cur.left)
  27. # res.append(cur.val)
  28. # dfs(cur.right)
  29. # # 后序递归
  30. # dfs(cur.left)
  31. # dfs(cur.right)
  32. # res.append(cur.val)
  33. res = []
  34. dfs(root)
  35. return res

2、迭代

  1. # 迭代
  2. # 时间复杂度:O(n),n为节点数,访问每个节点恰好一次。
  3. # 空间复杂度:O(h),h为树的高度。取决于树的结构,最坏情况存储整棵树,即O(n)
  4. # 迭代1:前序遍历最常用模板(后序同样可以用)
  5. class Solution:
  6. def preorderTraversal(self, root: TreeNode) -> List[int]:
  7. if not root:
  8. return []
  9. res = []
  10. stack = [root]
  11. # # 前序迭代模板:最常用的二叉树DFS迭代遍历模板
  12. while stack:
  13. cur = stack.pop()
  14. res.append(cur.val)
  15. # 由于栈是“先进后出”的顺序,所以入栈时先将右子树入栈,这样使得前序遍历结果为 “根->左->右”的顺序。
  16. if cur.right:
  17. stack.append(cur.right)
  18. if cur.left:
  19. stack.append(cur.left)
  20. return res
  21. # # 后序迭代,相同模板:将前序迭代进栈顺序稍作修改,最后得到的结果反转
  22. # while stack:
  23. # cur = stack.pop()
  24. # if cur.left:
  25. # stack.append(cur.left)
  26. # if cur.right:
  27. # stack.append(cur.right)
  28. # res.append(cur.val)
  29. # return res[::-1]
  30. # 迭代1:层序遍历最常用模板
  31. class Solution:
  32. def levelOrder(self, root: TreeNode) -> List[List[int]]:
  33. if not root:
  34. return []
  35. cur, res = [root], []
  36. while cur:
  37. lay, layval = [], []
  38. for node in cur:
  39. layval.append(node.val)
  40. if node.left: lay.append(node.left)
  41. if node.right: lay.append(node.right)
  42. cur = lay
  43. res.append(layval)
  44. return res
  45. # 迭代2:前、中、后序遍历通用模板(只需一个栈的空间)
  46. class Solution:
  47. def inorderTraversal(self, root: TreeNode) -> List[int]:
  48. res = []
  49. stack = []
  50. cur = root
  51. # 中序,模板:先用指针找到每颗子树的最左下角,然后进行进出栈操作
  52. while stack or cur:
  53. while cur:
  54. stack.append(cur)
  55. cur = cur.left
  56. cur = stack.pop()
  57. res.append(cur.val)
  58. cur = cur.right
  59. return res
  60. # # 前序,相同模板
  61. # while stack or cur:
  62. # while cur:
  63. # res.append(cur.val)
  64. # stack.append(cur)
  65. # cur = cur.left
  66. # cur = stack.pop()
  67. # cur = cur.right
  68. # return res
  69. # # 后序,相同模板
  70. # while stack or cur:
  71. # while cur:
  72. # res.append(cur.val)
  73. # stack.append(cur)
  74. # cur = cur.right
  75. # cur = stack.pop()
  76. # cur = cur.left
  77. # return res[::-1]
  78. # 迭代3:标记法迭代(需要双倍的空间来存储访问状态):
  79. # 前、中、后、层序通用模板,只需改变进栈顺序或即可实现前后中序遍历,
  80. # 而层序遍历则使用队列先进先出。0表示当前未访问,1表示已访问。
  81. class Solution:
  82. def preorderTraversal(self, root: TreeNode) -> List[int]:
  83. res = []
  84. stack = [(0, root)]
  85. while stack:
  86. flag, cur = stack.pop()
  87. if not cur: continue
  88. if flag == 0:
  89. # 前序,标记法
  90. stack.append((0, cur.right))
  91. stack.append((0, cur.left))
  92. stack.append((1, cur))
  93. # # 后序,标记法
  94. # stack.append((1, cur))
  95. # stack.append((0, cur.right))
  96. # stack.append((0, cur.left))
  97. # # 中序,标记法
  98. # stack.append((0, cur.right))
  99. # stack.append((1, cur))
  100. # stack.append((0, cur.left))
  101. else:
  102. res.append(cur.val)
  103. return res
  104. # # 层序,标记法
  105. # res = []
  106. # queue = [(0, root)]
  107. # while queue:
  108. # flag, cur = queue.pop(0) # 注意是队列,先进先出
  109. # if not cur: continue
  110. # if flag == 0:
  111. # 层序遍历这三个的顺序无所谓,因为是队列,只弹出队首元素
  112. # queue.append((1, cur))
  113. # queue.append((0, cur.left))
  114. # queue.append((0, cur.right))
  115. # else:
  116. # res.append(cur.val)
  117. # return res

3、莫里斯遍历

  1. # 莫里斯遍历
  2. # 时间复杂度:O(n),n为节点数,看似超过O(n),有的节点可能要访问两次,实际分析还是O(n),具体参考大佬博客的分析。
  3. # 空间复杂度:O(1),如果在遍历过程中就输出节点值,则只需常数空间就能得到中序遍历结果,空间只需两个指针。
  4. # 如果将结果储存最后输出,则空间复杂度还是O(n)。
  5. # PS:莫里斯遍历实际上是在原有二叉树的结构基础上,构造了线索二叉树,
  6. # 线索二叉树定义为:原本为空的右子节点指向了中序遍历顺序之后的那个节点,把所有原本为空的左子节点都指向了中序遍历之前的那个节点
  7. # emmmm,好像大学教材学过,还考过
  8. # 此处只给出中序遍历,前序遍历只需修改输出顺序即可
  9. # 而后序遍历,由于遍历是从根开始的,而线索二叉树是将为空的左右子节点连接到相应的顺序上,使其能够按照相应准则输出
  10. # 但是后序遍历的根节点却已经没有额外的空间来标记自己下一个应该访问的节点,
  11. # 所以这里需要建立一个临时节点dump,令其左孩子是root。并且还需要一个子过程,就是倒序输出某两个节点之间路径上的各个节点。
  12. # 具体参考大佬博客
  13. # 莫里斯遍历,借助线索二叉树中序遍历(附前序遍历)
  14. class Solution:
  15. def inorderTraversal(self, root: TreeNode) -> List[int]:
  16. res = []
  17. # cur = pre = TreeNode(None)
  18. cur = root
  19. while cur:
  20. if not cur.left:
  21. res.append(cur.val)
  22. # print(cur.val)
  23. cur = cur.right
  24. else:
  25. pre = cur.left
  26. while pre.right and pre.right != cur:
  27. pre = pre.right
  28. if not pre.right:
  29. # print(cur.val) 这里是前序遍历的代码,前序与中序的唯一差别,只是输出顺序不同
  30. pre.right = cur
  31. cur = cur.left
  32. else:
  33. pre.right = None
  34. res.append(cur.val)
  35. # print(cur.val)
  36. cur = cur.right
  37. return res

4.N叉树遍历

  1. # N叉树遍历
  2. # 时间复杂度:时间复杂度:O(M),其中 M 是 N 叉树中的节点个数。每个节点只会入栈和出栈各一次。
  3. # 空间复杂度:O(M)。在最坏的情况下,这棵 N 叉树只有 2 层,所有第 2 层的节点都是根节点的孩子。
  4. # 将根节点推出栈后,需要将这些节点都放入栈,共有 M−1个节点,因此栈的大小为 O(M)。
  5. """
  6. # Definition for a Node.
  7. class Node:
  8. def __init__(self, val=None, children=None):
  9. self.val = val
  10. self.children = children
  11. """
  12. # N叉树简洁递归
  13. class Solution:
  14. def preorder(self, root: 'Node') -> List[int]:
  15. if not root: return []
  16. res = [root.val]
  17. for node in root.children:
  18. res.extend(self.preorder(node))
  19. return res
  20. # N叉树通用递归模板
  21. class Solution:
  22. def preorder(self, root: 'Node') -> List[int]:
  23. res = []
  24. def helper(root):
  25. if not root:
  26. return
  27. res.append(root.val)
  28. for child in root.children:
  29. helper(child)
  30. helper(root)
  31. return res
  32. # N叉树迭代方法
  33. class Solution:
  34. def preorder(self, root: 'Node') -> List[int]:
  35. if not root:
  36. return []
  37. s = [root]
  38. # s.append(root)
  39. res = []
  40. while s:
  41. node = s.pop()
  42. res.append(node.val)
  43. # for child in node.children[::-1]:
  44. # s.append(child)
  45. s.extend(node.children[::-1])
  46. return res

四、案例

树的深度

  1. # Definition for a binary tree node.
  2. # class TreeNode:
  3. # def __init__(self, val=0, left=None, right=None):
  4. # self.val = val
  5. # self.left = left
  6. # self.right = right
  7. class Solution:
  8. def maxDepth(self, root: Optional[TreeNode]) -> int:
  9. if not root:
  10. return 0
  11. return max(self.maxDepth(root.left), self.maxDepth(root.right)) + 1
  12. # 层序遍历
  13. # Definition for a binary tree node.
  14. # class TreeNode:
  15. # def __init__(self, val=0, left=None, right=None):
  16. # self.val = val
  17. # self.left = left
  18. # self.right = right
  19. class Solution:
  20. def maxDepth(self, root: Optional[TreeNode]) -> int:
  21. if not root:
  22. return 0
  23. cur, res = [root], 0
  24. while cur:
  25. layer, tmp = [], []
  26. for info in cur:
  27. tmp.append(info.val)
  28. if info.left:
  29. layer.append(info.left)
  30. if info.right:
  31. layer.append(info.right)
  32. cur = layer
  33. res+=1
  34. return res

公共祖先

  1. """
  2. p 和 q 在 root 的子树中,且分列 root 的 异侧(即分别在左、右子树中);
  3. p = root ,且 q 在 root 的左或右子树中;
  4. q = root ,且 p 在 root 的左或右子树中;
  5. """
  6. class Solution:
  7. def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
  8. if not root or root == q or root == p:
  9. return root
  10. left = self.lowestCommonAncestor(root.left, p, q)
  11. right = self.lowestCommonAncestor(root.right, p, q)
  12. if not left and not right: return
  13. if not left: return right
  14. if not right: return left
  15. return root

112. 路径总和

  1. class Solution:
  2. def hasPathSum(self, root: Optional[TreeNode], targetSum: int) -> bool:
  3. if not root:
  4. return False
  5. if not root.left and not root.right:
  6. return targetSum == root.val
  7. return self.hasPathSum(root.left, targetSum - root.val) or self.hasPathSum(root.right, targetSum - root.val)

98. 验证二叉搜索树

  1. class Solution:
  2. # 中序遍历时,判断当前节点是否大于中序遍历的前一个节点,如果大于,说明满足 BST,继续遍历;否则直接返回 false。
  3. # 中序遍历为升序
  4. pre = float("-inf")
  5. def isValidBST(self, root: Optional[TreeNode]) -> bool:
  6. if root == None: return True
  7. if not self.isValidBST(root.left):
  8. return False
  9. if root.val <= self.pre:
  10. return False
  11. self.pre = root.val
  12. return self.isValidBST(root.right)

参考

题目:

144. 二叉树的前序遍历
94. 二叉树的中序遍历
145. 二叉树的后序遍历
102. 二叉树的层序遍历
589. N叉树的前序遍历

资料:

LeetCode官方题解
powcai的题解
Grandyang的博客
Annie Kim's Blog
维基百科对于线索二叉树的解释
莫里斯的文章

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/羊村懒王/article/detail/595882
推荐阅读
相关标签
  

闽ICP备14008679号