1. 中序遍历
类型1:对于一个二叉查找树来说,其中序遍历结果是一个有序数组
def convertBiNode(self, root: TreeNode) -> TreeNode:
self.ans = self.pre = TreeNode(-1)
def dfs(root):
if not root:
return
dfs(root.left)
root.left = None
self.pre.right = root
# 当第一次执行到下面这一行代码,恰好是在最左下角, 这个时候 self.pre = root 就切断了 self.pre
# 和 self.ans 的联系
# 之后 self.pre 的变化都不会体现到 self.ans 上。
# 直观上来说就是 self.ans 在遍历到最左下角的时候下车了
# 因此最后返回 self.ans 即可
self.pre = root
dfs(root.right)
dfs(root)
return self.ans.right
self.pre 和 self.ans 这2个变量, 指向的都是同一个内存中的对象, 也就是 Treenode(-1) 这个对象:
self.pre = self.ans = TreeNode(-1)
因为他们指向的是同一个对象, 而且这个Treenode对象属于 "可变类型对象" : https://blog.csdn.net/find_a/article/details/82384639
所以,对可变类型对象进行操作时, id不会改变,也就是说,还是在相同的内存空间中,对于Treenode(-1)进行修改 (修改它的left 和 right属性).
所以,在递归的过程中,一直递归到第一次到达叶子节点之后, 作者对当前的叶子节点(因为是二叉搜索树的中序遍历,所以这个叶子节点就是整颗树最左边的那个节点)做了以下操作:
root.left = None self.pre.right = root self.pre = root
其中第一句 root.left 只是设置当前节点的左节点, 不重要. 第二句
self.pre.right = root
. 这句代码作用是将当前节点设置“前置节点”,方便下一个节点用它,也不重要。 重要的在于第三句self.pre = root
, 这句代码的关键之处在于, 他将 self.pre 这个变量,直接指向了另一块内存空间, 也就是 "root" 节点所在的内存空间。这一指,就把self.pre 和 self.ans 断开的联系。什么意思呢?当执行完这一句命令后,目前:sell.pre 指向的,是内存中 root 节点所在的内存。
self.ans 指向的,还是之前内存中 Treenode(-1) 哑节点的位置。
也就是说,在执行
self.pre = root
这句程序之前, self.pre 做的任何改变, 都会对 Treenode(-1) 生效,同时,由于self.ans 也指向同一个Treenode(-1) 数据,所以等于也会对self.ans 生效。 但是,当执行完这句命令后, self.pre 之后做出任何改变,都不再改变那个我们在整个算法最开头创建出来的哑节点 Treenode(-1) 了。但是,我们看到上面第二句做了一个很关键的操作,就是self.pre.right = root
, 这一句在第三局之前,就将哑节点的right属性,设置成了“整颗树最左边的叶子节点,也就是我们需要最终返回的有序链表的头节点”。重要!!!!记住,这里self.pre 和 self.ans 仍然指向的是同一个内存地址中的哑节点Treenode(-1), 也就是说,目前self.ans.right 已经是整棵树最左边的叶子节点了。 然后,执行完第三句之后,self.pre = root 这句命令让Self.pre 不再指向Treenode(-1), 所以让self.ans永远停留在当前状态。也就是“self.ans.right=整棵树最左下角的叶子节点”的状态。这个状态,我们就可以直接在递归结束后返回self.ans.right.
2. 后序遍历
示例 1:
给定二叉树 [3,9,20,null,null,15,7]
3
/ \
9 20
/ \
15 7
返回 true 。
示例 2:
给定二叉树 [1,2,2,3,3,null,null,4,4]
1
/ \
2 2
/ \
3 3
/ \
4 4
返回 false 。
策略:后序遍历+ 剪枝
class Solution:
def isBalanced(self, root: TreeNode) -> bool:
def recur(root):
if not root:
return 0
left = recur(root.left)
if left == -1:
return -1
right = recur(root.right)
if right == -1:
return -1
#后序遍历
return max(left,right)+1 if abs(left-right)<=1 else -1
return recur(root) != -1
综合:
输入某二叉树的前序遍历和中序遍历的结果,请重建该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。
例如,给出
前序遍历 preorder = [3,9,20,15,7]
中序遍历 inorder = [9,3,15,20,7]
返回如下的二叉树:
3
/ \
9 20
/ \
15 7
class Solution:
def buildTree(self, preorder: List[int], inorder: List[int]) -> TreeNode:
def recur(root,left,right):
#相等就是自己
if left > right:
return
#root是在先序里面的
node = TreeNode(preorder[root])
# 有了先序的,再根据先序的,在中序中获 当前根的索引
idx = dic[preorder[root]]
#左子树的根节点就是左子树的(前序遍历)第一个,就是+1,左边边界就是left,右边边界是中
#间区分的idx-1
node.left = recur(root+1, left, idx-1)
#由根节点在中序遍历的idx 区分成2段,idx 就是根
#右子树的根,就是右子树(前序遍历)的第一个,就是当前根节点 加上左子树的数量
# root 当前的根 左子树的长度 = 左子树的左边-右边 (idx-1 - left+1) 。
# 最后+1就是右子树的根了
node.right = recur(root+idx-left+1,idx+1,right)
return node
dic = {}
for i in range(len(inorder)):
dic[inorder[i]] = i
return recur(0,0,len(inorder)-1)
递推参数: 根节点在前序遍历的索引 root 、子树在中序遍历的左边界 left 、子树在中序遍历的右边界 right ;
终止条件: 当 left > right ,代表已经越过叶节点,此时返回 nullnull ;
递推工作:
建立根节点 node : 节点值为 preorder[root] ;
划分左右子树: 查找根节点在中序遍历 inorder 中的索引 idx;这样建立起了inorder和preorder的关系