理论基础
定义:每个 node 的 degree 为 0 或 2,且 degree=0 的 nodes 都在同一层。
depth=k 的树共有 2 k − 1 2^k-1 2k−1 个 nodes。
定义:
它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。
前/中/后序遍历,其实指的就是中间节点的遍历顺序(中间节点出现在哪里)。
class TreeNode:
def __init__(self, val, left = None, right = None):
self.val = val
self.left = left
self.right = right
理论基础
递归的核心:
题目链接
class Solution:
def preorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
if root == None:
return []
left_lst = self.preorderTraversal(root.left)
right_lst = self.preorderTraversal(root.right)
return [root.val] + left_lst + right_lst
题目链接
class Solution:
def postorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
if root == None:
return []
left_lst = self.postorderTraversal(root.left)
right_lst = self.postorderTraversal(root.right)
return left_lst + right_lst + [root.val]
题目链接
class Solution:
def inorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
if root == None:
return []
left_lst = self.inorderTraversal(root.left)
right_lst = self.inorderTraversal(root.right)
return left_lst + [root.val] + right_lst
理论基础
一般面试中不会要求用迭代的方法实现复杂的题目,主要是考察以非递归的方式实现简单题目的能力。
递归的底层使用栈来实现的,所以迭代的时候其实也是要用栈来实现(相当于手动实现递归)。
但要注意,栈的 LIFO 的特性导致加入元素的顺序应当与最终数组里的结果是相反的。
通过栈的特性,来完成实质上的递归:每当栈的 top element 发生改变的时候,实质是开始了子树的递归。
因为栈的 LIFO,操作时的入栈顺序是中右左。
class Solution:
def preorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
if root == None:
return []
stack = [root]
results = []
while (len(stack) > 0):
curr_node = stack.pop()
results.append(curr_node.val) # middle
if curr_node.right != None: # right
stack.append(curr_node.right)
if curr_node.left != None: # left
stack.append(curr_node.left)
return results
与前序遍历是一样的,只需要进行一些顺序的翻转即可(前序是“中左右”,后序是“左右中”)。
class Solution:
def postorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
if root == None:
return []
stack = [root]
results = []
while (len(stack) > 0):
curr_node = stack.pop()
results.append(curr_node.val)
if curr_node.left != None:
stack.append(curr_node.left)
if curr_node.right != None:
stack.append(curr_node.right)
results.reverse()
return results
中序遍历和前后序遍历的代码基础是不同的,没办法像后序一样直接在前序的基础上修改。区别在于,中序遍历的访问节点和处理节点是不同的。
当访问根节点的时候,想要先处理左子树中的元素(而非像之前一样,先处理根节点),所以只有访问到最左边的 leaf 节点时,才能开始处理节点。解决思路是用栈进行其中的记录,而用指针来访问节点,而不像之前一样访问即可以处理。
主要逻辑:
class Solution:
def inorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
if root == None:
return []
stack = []
results = []
curr_node = root
while (len(stack) > 0 or curr_node != None):
if curr_node != None:
stack.append(curr_node)
curr_node = curr_node.left # left
else:
curr_node = stack.pop()
results.append(curr_node.val) # middle
curr_node = curr_node.right # right
return results
理论基础
迭代方法无法像递归一样以统一框架来处理前中后序遍历,最大的原因是中序遍历的处理与访问时分开的(类似于双指针)。
处理方法是将访问的节点放入栈中,把要处理的节点也放入栈中但是要做标记(要处理的节点放入栈之后,紧接着放入一个空指针作为标记。
统一的解法采用先全部记录,再进行处理的思路,用栈先收集所有节点,然后在栈中弹出顺序元素。
不过这种统一的迭代方法的理论意义大,实际使用还是正常用迭代和递归即可。
class Solution:
def preorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
if root == None:
return []
stack = [root]
results = []
while (len(stack) > 0):
curr_node = stack.pop()
if curr_node != None:
if curr_node.right != None:
stack.append(curr_node.right) # right
if curr_node.left != None:
stack.append(curr_node.left) # left
stack.append(curr_node) # middle
stack.append(None)
else:
results.append(stack.pop().val)
return results
class Solution:
def postorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
if root == None:
return []
stack = [root]
results = []
while (len(stack) > 0):
curr_node = stack.pop()
if curr_node != None:
stack.append(curr_node) # middle
stack.append(None)
if curr_node.right != None:
stack.append(curr_node.right) # right
if curr_node.left != None:
stack.append(curr_node.left) # left
else:
results.append(stack.pop().val)
return results
class Solution:
def inorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
if root == None:
return []
stack = [root]
results = []
while (len(stack) > 0):
curr_node = stack.pop()
if curr_node != None:
if curr_node.right != None:
stack.append(curr_node.right) # right
stack.append(curr_node) # middle
stack.append(None)
if curr_node.left != None:
stack.append(curr_node.left) # left
else:
results.append(stack.pop().val)
return results