二叉树中用遍历思路解题时函数签名一般是 def traverse(...)
-> None ,没有返回值,靠更新外部变量来计算结果,而用分解问题思路解题时函数名根据该函数具体功能而定,而且一般会有返回值,返回值是子问题的计算结果。
中给出的函数签名一般也是没有返回值的 def backtrack(...)
,而在 [动态规划核心框架] 中给出的函数签名是带有返回值的 dp
函数。这也说明它俩和二叉树之间千丝万缕的联系。
以之为例226. 翻转二叉树
一、用遍历的方法,无返回值 想清楚每个结点要做什么
class Solution:
def invertTree(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
self.traverse(root)
return root
def traverse(self, root: TreeNode) -> None: #无返回值
if root is None:
return
tmp = root.left
root.left = root.right
root.right = tmp
#上面都是当前结点进行的操作 ,这是前序位置
self.traverse(root.left) #遍历之后节点 递归
self.traverse(root.right)
二、用分解问题的方法 递归逻辑自洽,base case 处理清楚
class Solution:
def invertTree(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
if root is None:
return
left = self.invertTree(root.left) #返回的是翻转之后的根节点
right = self.invertTree(root.right) #分解问题的思想
root.left = right
root.right = left
return root
这种“分解问题”的思路,即递归思想,核心在于对递归函数的定义。通过对函数的定义,解释代码的逻辑,并验证其正确性。如果逻辑自洽,那么算法就是正确的 递归定义和逻辑自洽
完美二叉树 (Perfect Binary Tree):
- 完美二叉树是指所有的层都被完全填满,即从根节点到叶子节点的所有层都没有任何空缺。
完全二叉树 (Complete Binary Tree):
- 完全二叉树是指除了最后一层外,每一层都被完全填满,并且最后一层的节点尽可能地靠左填充。
class Solution:
def connect(self, root: 'Optional[Node]') -> 'Optional[Node]':
if root is None:
return None
self.traverse(root.left, root.right)
return root #遍历完之后从根节点返回全部值
def traverse(self, node1, node2) -> None:
if node1 is None or node2 is None:
return
node1.next = node2 #当前结点进行的操作
self.traverse(node1.left, node1.right)
self.traverse(node2.left, node2.right)
self.traverse(node1.right, node2.left)
「遍历」这一思维模式,只要运用二叉树的递归遍历框架即可
「分解问题」这一模式,关键在于明确递归函数的定义,然后利用这个定义,来构造函数,而具体如何完成的可能比较模糊
class Solution:
def flatten(self, root: Optional[TreeNode]) -> None:#按照先序遍历获取结果
"""
Do not return anything, modify root in-place instead.
"""
#流程,1 root的左子树和右子树拉平 2 将root的右子树接到左子树下方,之后左子树作为右子树
if root is None:
return
#1.先递归拉平左右子树
self.flatten(root.left)
self.flatten(root.right)
#2.将左子树作为右子树
left = root.left
right = root.right
root.left = None
root.right = left
#3.将原先的右子树接到右子树的末端
p = root
while p.right is not None:
p = p.right
p.right = right
114.二叉树展开为链表
class Solution:
def flatten(self, root: Optional[TreeNode]) -> None:
"""
Do not return anything, modify root in-place instead.
"""
if root is None:
return
self.flatten(root.left)
self.flatten(root.right)
#后序位置也就是在处理完左右结点之后再处理根节点。所以是先处理左子树最左边的结点,再是右子树最左边结点。最后处理根节点
left = root.left
right = root.right
root.left = None
root.right = left
p = root
while p.right is not None:
p = p.right
p.right = right #把原来的右边接上去
需要把每个节点需要干什么的逻辑弄特别清楚