2022-4-27
哭唧唧,昨天做的记录没有保存,算啦1-d dp再做一遍好啦当加深记忆啦!当然要记得及时保存哈!
226. Invert Binary Tree
- 主要思路:dfs | 当前根节点的左右两个子孩子交换位置,问题就转化成了两个子孩子作为根节点的反转问题;
- version2为version1的简化版本,本质没啥区别;
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
class Solution:
def invertTree(self, root: TreeNode) -> TreeNode:
if not root:
return None
# 当前节点左右子节点交换位置
tmp = root.left
root.left = root.right
root.right = tmp
root.left = self.invertTree(root.left) # 左子树反转
root.right = self.invertTree(root.right) # 右子树反转
return root
def invertTree2(self, root: TreeNode) -> TreeNode:
if not root:
return None
root.left, root.right = self.invertTree(root.right), self.invertTree(root.left)
return root
104. Maximum Depth of Binary Tree
- 递归杀我千百回
- 跟neetcode的日常
- 主要思路:neetcode主要给出了三种解法
-
RECURSIVE DFS:当前节点的depth = 左右孩子的最大深度 + 1 【不用担心当前节点如果是None会不会也+1了,在base case的时候已经过滤掉了None节点】
-
ITERATIVE DFS | stack:stack存放[node, depth];root放进去stack,pop出来,放左右节点;每次pop都更新下最大深度res;后面就是pop栈顶元素,更新res,栈顶元素左右节点和depth信息压栈,loop直到栈空了;
头一次遇到stack可以同时存放node和depth的!是我愚钝了![之前遇到的要么就是node要么就放index哈哈哈]
-
BFS | queue:
- 该层节点放进去,记录depth
- 队首pop出来,队尾加进去pop出来的节点的左右孩子(如果有的话),记录depth;
- 重复2操作,直到queue为空;
可能会想问为什么要用队列呢?考虑以下队列的性质,先进先出的性质比较符合bfs的逻辑;而stack的前进后出的性质比较符合dfs的逻辑;
- depth的更新逻辑是while进去都是当前层的node在里面,for的是当前层的node,虽然append了node的左右子节点,按照逻辑来说append之后没有改变当前层的len(q),应该是在下次进去while之后queue已经把前一层的node全pop掉了,又变成了下一层(现在的当前层)的所有node了;
注意要处理root is None的case;
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
class Solution:
def maxDepth_re_dfs(self, root) -> int:
if not root:
return 0
return 1 + max(self.maxDepth(root.left), self.maxDepth(root.right))
def maxDepth_it_dfs(self, root) -> int:
if not root:
return 0
stack = [[root, 1]]
res = 0
while stack:
node, depth = stack.pop()
res = max(res, depth)
if node.left:
stack.append([node.left, depth + 1])
if node.right:
stack.append([node.right, depth + 1])
return res
def maxDepth_it_dfs2(self, root) -> int:
stack = [[root, 1]]
res = 0
while stack:
node, depth = stack.pop()
if node:
res = max(res, depth)
stack.append([node.left, depth + 1])
stack.append([node.right, depth + 1])
return res
def maxDepth_bfs(self, root) -> int:
if not root:
return 0
from collections import deque
q = deque([root])
depth = 0
while q:
for _ in range(len(q)):
node = q.popleft()
if node.left:
q.append(node.left)
if node.right:
q.append(node.right)
depth += 1
return depth
def maxDepth_bfs2(self, root) -> int:
from collections import deque
q = deque([root])
depth = 0
while q:
for _ in range(len(q)):
node = q.popleft()
if node:
q.append(node.left)
q.append(node.right)
depth += 1
return depth - 1
ps 2.的v1和v2以及3.的v1和v2就是在处理边界的时候稍微修改了一下;目前而言感觉还是v1的比较好理解符合逻辑,v2的话空节点也会压栈,但是可以一起处理一开始根节点为空的情况;嗐,各有所长吧;
2022-5-7 重新过了一遍dfs,果然我是不会写的那么简洁的orz【我的逻辑g了哈哈哈哈】
那三句实际上可以合并成return 1+max(dfs(root.left, depth),dfs(root.right))
class Solution:
def maxDepth(self, root):
def dfs(root, depth):
if not root:
return depth
depth += 1
depth = max(dfs(root.left, depth), dfs(root.right, depth))
return depth
return dfs(root, 0)
顺便把BFS系列过一遍
BFS
102. Binary Tree Level Order Traversal
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
class Solution:
def levelOrder(self, root: TreeNode) -> List[List[int]]:
if not root:
return []
from collections import deque
q = deque([root])
res = []
while q:
tmp = []
for _ in range(len(q)):
node = q.popleft()
tmp.append(node.val)
if node.left:
q.append(node.left)
if node.right:
q.append(node.right)
res.append(tmp)
return res
107. Binary Tree Level Order Traversal II
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
class Solution:
def levelOrderBottom(self, root: TreeNode):
if not root:
return []
from collections import deque
q = deque([root])
res = []
while q:
tmp = []
for _ in range(len(q)):
node = q.popleft()
tmp.append(node.val)
if node.left:
q.append(node.left)
if node.right:
q.append(node.right)
res.insert(0, tmp)
return res
199. Binary Tree Right Side View
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
class Solution:
def rightSideView(self, root):
if not root:
return []
from collections import deque
q = deque([root])
res = []
while q:
tmp = []
for _ in range(len(q)):
node = q.popleft()
tmp.append(node.val)
if node.left:
q.append(node.left)
if node.right:
q.append(node.right)
res.append(tmp[-1])
return res
637. Average of Levels in Binary Tree
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
class Solution:
def averageOfLevels(self, root):
from collections import deque
q = deque([root])
res = []
while q:
tmp = []
for _ in range(len(q)):
node = q.popleft()
tmp.append(node.val)
if node.left:
q.append(node.left)
if node.right:
q.append(node.right)
res.append(sum(tmp) / len(tmp))
return res
429. N-ary Tree Level Order Traversal
class TreeNode:
def __init__(self, val=0, children=None):
self.val = val
self.children = children
class Solution:
def levelOrder(self, root):
if not root:
return []
from collections import deque
q = deque([root])
res = []
while q:
tmp = []
for _ in range(len(q)):
node = q.popleft()
tmp.append(node.val)
if node.children:
q.extend(node.children)
res.append(tmp)
return res
515. Find Largest Value in Each Tree Row
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
class Solution:
def largestValues(self, root):
if not root:
return []
from collections import deque
q = deque([root])
res = []
while q:
tmp = float("-inf")
for _ in range(len(q)):
node = q.popleft()
tmp = max(tmp, node.val)
if node.left:
q.append(node.left)
if node.right:
q.append(node.right)
res.append(tmp)
return res
116. Populating Next Right Pointers in Each Node
这道题不需要存val;边连边存层;这里默认next是None所以range(len(q) - 1)就可以了;
class Node:
def __init__(self, val: int = 0, left: 'Node' = None, right: 'Node' = None, next: 'Node' = None):
self.val = val
self.left = left
self.right = right
self.next = next
class Solution:
def connect(self, root):
if not root:
return root
from collections import deque
q = deque([root])
while q:
for i in range(len(q) - 1):
q[i].next = q[i + 1]
for _ in range(len(q)):
node = q.popleft()
if node.left:
q.append(node.left)
if node.right:
q.append(node.right)
return root
117. Populating Next Right Pointers in Each Node II
和116一样可过;
104. Maximum Depth of Binary Tree
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
class Solution:
def maxDepth(self, root) -> int:
if not root:
return 0
from collections import deque
q = deque([root])
depth = 0
while q:
for i in range(len(q)):
node = q.popleft()
if node.left:
q.append(node.left)
if node.right:
q.append(node.right)
depth += 1
return depth
111. Minimum Depth of Binary Tree
相比104:当遇到左右孩子都空时,说明该节点是其中一条路径的终点了,可以提前终止了;
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
class Solution:
def minDepth(self, root):
if not root:
return 0
from collections import deque
q = deque([root])
depth = 0
while q:
depth += 1
for _ in range(len(q)):
node = q.popleft()
if not node.left and not node.right:
return depth
if node.left:
q.append(node.left)
if node.right:
q.append(node.right)
return depth
543. Diameter of Binary Tree
- 思路很明显去记录下每个节点左右子树的最大深度的和,注意复用;
自己写dfs还是会卡住!!
- 求二叉树的最大深度模块[104]就可以复用了,空节点返回-1真的绝!
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
class Solution:
def diameterOfBinaryTree(self, root):
res = [0]
def dfs(root):
if not root:
return -1
left = dfs(root.left)
right = dfs(root.right)
res[0] = max(res[0], 2+left+right)
return 1 + max(left, right)
dfs(root)
return res[0]
110. Balanced Binary Tree
- 还是复用dfs的104模块!
- 这道题的关键在与如何保存下面节点是不是平衡的信息
- 为了和543看起来差不多我们还是选择使用res全局变量来搞;
- 我们要知道这是一个dfs到最后再逐步往上check的过程,也就是说如果下面有一个节点不平衡的时候res[0]的状态就改为False了,好像没法提前终止返回,所以后面就算节点是平衡的也不需要改变res[0]的状态了;状态改为false之后不管你之后平不平衡都已经无所谓了
所以修改的条件为:
if res[0] and (left - right > 1 or right - left > 1): res[0] = False
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
class Solution:
def isBalanced(self, root):
res = [True]
def dfs(root):
if not root:
return -1
left = dfs(root.left)
right = dfs(root.right)
if res[0] and (left - right > 1 or right - left > 1):
res[0] = False
return 1 + max(left, right)
dfs(root)
return res[0]
100. Same Tree
属于边dfs边check的类型,和226差不多
- 比较节点:
- 终止条件:都是空的 说明到了根节点,两边结构相同;
- 终止条件:一边空一边不空,说明两边结构不同;
- 值不等;
class TreeNode:
def __init__(self, val=0, right=None, left=None):
self.val = val
self.right = right
self.left = left
class Solution:
def isSameTree(self, p, q):
if not p and not q:
return True
if (not p and q) or (p and not q): # if not p or not q:
return False
if p.val != q.val:
return False
return self.isSameTree(p.left, q.left) and self.isSameTree(p.right, q.right)
572. Subtree of Another Tree
- 我的直观:从根节点开始判断是否是同一棵树(复用[100]),不是判断左右子树[相当于每个节点都判断了一遍是否是Same Tree]
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
class Solution:
def isSubtree(self, root, subRoot):
def isSameTree(p, q):
if not p and not q:
return True
if not p or not q:
return False
if p.val != q.val:
return False
return isSameTree(p.left, q.left) and isSameTree(p.right, q.right)
if not root:
return False
if isSameTree(root, subRoot):
return True
return self.isSubtree(root.left, subRoot) or self.isSubtree(root.right, subRoot)