二叉树也是面试的高频考察点。
主要还是讲一下迭代法。递归三种遍历都差不多,很简单。
def preorderTraversal(self, root: TreeNode) -> List[int]:
if not root:
return []
stack = [root]
res = []
while len(stack) > 0:
cur = stack.pop()
res.append(cur.val)
if cur.right:
stack.append(cur.right)
if cur.left:
stack.append(cur.left)
return res
这里有一个点是要先将右孩子加入栈再加入左孩子,这样pop的时候才是左孩子优先。
注意:二叉树的遍历和图的遍历完全不是一个概念,除了层序遍历=BFS。
思路是使用null节点来判断是否应该处理当前节点。
中序遍历是左 - 右 - 中,所以先一路向左,碰到null就说明当前分支左边走完了,这时pop处理左边最底端的节点,接着向右处理。因为每个节点只会被加入栈一次(在parent向左遍历的过程中),所以不会重复。
def inorderTraversal(self, root: TreeNode) -> List[int]:
if not root:
return []
stack = []
res = []
cur = root
while cur or len(stack) > 0:
if cur:
stack.append(cur)
cur = cur.left
else:
cur = stack.pop()
res.append(cur.val)
cur = cur.right
return res
后序又不一样了。左-右-中,反过来是中-右-左;后面两个再反一下就是中-左-右,前序遍历。因此后序是可以通过前序来推的。
def postorderTraversal(self, root: TreeNode) -> List[int]:
if not root:
return []
stack = [root]
res = []
while len(stack) > 0:
cur = stack.pop()
res.append(cur.val)
if cur.left:
stack.append(cur.left)
if cur.right:
stack.append(cur.right)
return list(reversed(res))
要统一写法,思路和之前的中序一样,仍然是使用null节点。
基本操作:
以中序遍历为例:
def inorderTraversal(self, root: TreeNode) -> List[int]:
if not root:
return []
stack = [root]
res = []
while len(stack) > 0:
cur = stack.pop()
if cur:
if cur.right:
stack.append(cur.right)
stack.append(cur)
stack.append(None)
if cur.left:
stack.append(cur.left)
else:
cur = stack.pop()
res.append(cur.val)
return res
层序的话用迭代更方便,因此介绍一下递归的写法。
递归的话一定要在参数中加depth。
def levelOrder(self, root: TreeNode) -> List[List[int]]:
res = []
def helper(root, depth):
if not root: return []
if len(res) == depth: res.append([]) # start the current depth
res[depth].append(root.val) # fulfil the current depth
if root.left: helper(root.left, depth + 1) # process child nodes for the next depth
if root.right: helper(root.right, depth + 1)
helper(root, 0)
return res
顺便也贴一个用deque的写法:
def lvlOrder(root):
queue = deque([root])
res = []
while (len(queue)>0):
size = len(queue)
temp = []
for _ in range(size):
cur = queue.popleft()
temp.append(cur.val)
if cur.left:
queue.append(cur.left)
if cur.right:
queue.append(cur.right)
res.append(temp)
return res
容易想到层序遍历,但是挑战常数空间就要换个思路了。
递归思路:假设当前层已经用next指针连好了,怎么样让下一层也用next指针连好?
根据两种next ptr类型,可以写出如下递归代码:
def connect(self, root: 'Node') -> 'Node':
if not root:
return None
if root.left:
root.left.next = root.right
if root.next:
root.right.next = root.next.left
self.connect(root.left)
self.connect(root.right)
return root
与之前的不同是要追踪一个last节点。
def link(self, last, leftmost, cur):
if not last:
last = cur
leftmost = cur
else:
last.next = cur
last = cur
return (last, leftmost)
def connect(self, root: 'Node') -> 'Node':
last = None
leftmost = root
while leftmost:
cur = leftmost
leftmost = None
while cur:
if cur.left:
last, leftmost = self.link(last, leftmost, cur.left)
if cur.right:
last, leftmost = self.link(last, leftmost, cur.right)
cur = cur.next
last = None
return root
注意python函数是pbv的,所以函数返回之后要重新赋值。