感觉写了一个世纪,终于把二叉树的章节过完了。还是挺充实开心的,以前确实没这么亲密地接触过二叉树。
## BM 39 序列化二叉树
url:BM
39
考察知识点:二叉树的遍历,序列化及反序列化
序列化及反序列化的定义:
序列化: 把对象转化为可传输的字节序列过程称为序列化。
反序列化: 把字节序列还原为对象的过程称为反序列化。
用层次遍历序列化和反序列化二叉树,实现如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 class Solution : def Serialize (self, root ): if root is None : return "" queue = [root] result = [] while len (queue) > 0 : node = queue.pop(0 ) if node is None : result.append("#" ) else : result.append(str (node.val)) queue.append(node.left) queue.append(node.right) return ", " .join(result) def Deserialize (self, s: str ): if len (s) == 0 : return None item_list = s.split(", " ) head = TreeNode(int (item_list[0 ])) last_layer = [head] this_layer = [] for i in range (1 , len (item_list)): parent_node = last_layer[0 ] current_node = TreeNode(int (item_list[i])) if item_list[i] != "#" else None if i % 2 == 1 : parent_node.left = current_node else : parent_node.right = current_node if current_node is not None : this_layer.append(current_node) if i % 2 == 0 : last_layer.pop(0 ) if len (last_layer) == 0 : last_layer = this_layer this_layer = [] return head
用前序遍历完成二叉树的序列化和反序列化:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 class Solution : def Serialize (self, root ): if root is None : return "" stack = [root] result = [] while len (stack) > 0 : node = stack.pop() if node is None : result.append("#" ) continue result.append(str (node.val)) stack.append(node.right) stack.append(node.left) return ", " .join(result) def deserialize (self, root: TreeNode, item_list: List [str ] ): node_val = item_list.pop(0 ) if node_val == "#" : root.left = None else : node = TreeNode(int (node_val)) root.left = node self.deserialize(node, item_list) node_val = item_list.pop(0 ) if node_val == "#" : root.right = None else : node = TreeNode(int (node_val)) root.right = node self.deserialize(node, item_list) def Deserialize (self, s: str ): if len (s) == 0 : return None item_list = s.split(", " ) head = TreeNode(int (item_list.pop(0 ))) if len (item_list) > 0 : self.deserialize(head, item_list) return head
BM 40 重建二叉树
url:牛客
BM40
考察知识点:二叉树前序和中序遍历
这道题的一个核心就是要知道如果得知某个树的根节点,那么通过查找中序遍历中根节点的位置,可以知道该树左子树的所有节点和右子树的所有节点。比如中序遍历是[3,2,4,1,6,5,7],如果知道根节点是1,那么左子树有节点[3,2,4],右子树有节点[6,5,7]。当左子树拥有的节点为空时,那么根节点不存在左子树。
前序遍历就可以为我们提供根节点的信息,承接上个例子,前序遍历如果为[1,2,3,4,5,6,7],那么第一个根节点肯定是1。用1分割好中序遍历,可以得到左子树节点为[3,2,4],右子树有节点[6,5,7]。那对前序遍历进行过滤,可以知道左子树的前序遍历为[2,3,4],右子树的前序遍历为[5,6,7]。再看左子树的前序遍历知道左子树的根节点为2,再重复分割一下左子树的左子树的节点为[3],左子树的右子树节点为[4],这样就可以知道左子树的构型为:
flowchart TD
A[1]
B[2]
C[3]
D[4]
A-->B
B-->C
B-->D
A-->E[右子树]
如上图所示,如果我们以同样的方式递归把右子树的情况也获取出来,就可以重建得到最终的树了。
以上逻辑的Python代码实现如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 class Solution : def filter_list (self, root: TreeNode, preOrder: List [int ], vinOrder: List [int ] ): root_val = root.val for i in range (len (vinOrder)): if vinOrder[i] == root_val: break left_vinOrder = vinOrder[:i] right_vinOrder = vinOrder[i+1 :] left_preOrder = [v for v in preOrder if v in left_vinOrder] right_preOrder = [v for v in preOrder if v in right_vinOrder] return left_preOrder, left_vinOrder, right_preOrder, right_vinOrder def reConstruct (self, root: TreeNode, left_preOrder: List [int ], left_vinOrder: List [int ], right_preOrder: List [int ], right_vinOrder: List [int ] ): if len (left_preOrder) == 0 : root.left = None else : node_val = left_preOrder.pop(0 ) root.left = TreeNode(int (node_val)) self.reConstruct(root.left, *self.filter_list(root.left, left_preOrder, left_vinOrder)) if len (right_preOrder) == 0 : root.right = None else : node_val = right_preOrder.pop(0 ) root.right = TreeNode(int (node_val)) self.reConstruct(root.right, *self.filter_list(root.right, right_preOrder, right_vinOrder)) def reConstructBinaryTree (self , preOrder: List [int ], vinOrder: List [int ] ) -> TreeNode: if len (preOrder) == 0 : return None root = TreeNode(preOrder.pop(0 )) self.reConstruct(root, *self.filter_list(root, preOrder, vinOrder)) return root
进一步追问:有了先序遍历和中序遍历就能唯一确定一棵二叉树树吗?
这个我觉得是要分情况的,这个题目能做到这点很大程度上仰仗“pre
和 vin
均无重复元素” 这个条件。假设一下,如果先序遍历和中序遍历都是[1,1,1]的话,那这棵树似乎有3种合规的重建方式,不能唯一确定某一棵二叉树。如果加上没有重复元素这点,那就可以唯一确定。
那在没有重复元素的情况下,哪两种遍历能唯一确定一棵二叉树呢?
首先前序和中序是可以的,原因上面讨论过了。后序和中序也是可以的,只不过后序遍历从后面取根节点的值。不可以唯一确定的是前序遍历和后序遍历,可以举一个反例来说明。
flowchart TD
subgraph case 2
D[1] --> F[None]
D[1] --> E[2]
end
subgraph case 1
A[1] --> B[2]
A[1] --> C[None]
end
这两个case里,前序和后序遍历都是[1,2]和[2,1],但一个2在左子树上一个2在右子树上。因此前序和后序遍历是不能唯一确定一棵二叉树的。
那加上层序遍历呢?我的理解是如果这棵树是完全二叉树(若二叉树的深度为
h,除第 h 层外,其它各层的结点数都达到最大个数,第 h
层所有的叶子结点都连续集中在最左边的二叉树),只用层序遍历就可以唯一确定这棵树了。否则,则必须要结合中序遍历才能确定(这句话可能也存在漏洞,我没找到不能这样做的反例)。将上面那个两个case做一个表格:
层次遍历
1,2
1,2
前序遍历
1,2
1,2
中序遍历
2,1
1,2
后序遍历
2,1
2,1
可以发现层次遍历、前序遍历和后序遍历对这两个不同的case都产生同样的结果,因此这3个遍历方式任意组合即使在节点值不重复的条件下也不能重建唯一的二叉树。
BM 41 输出二叉树的右视图
url:牛客
BM41
考察知识点:二叉树重建,二叉树层次遍历
把之前的BM40那个二叉树重新写了一下,重建二叉树然后分层层次遍历就可以了,实现如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 class Solution : def reConstruct (self, root: TreeNode, preOrder: List [int ], inOrder: List [int ] ): for i in range (len (inOrder)): if inOrder[i] == root.val: break left_inOrder = inOrder[:i] left_preOrder = preOrder[:i] right_inOrder = inOrder[i+1 :] right_preOrder = preOrder[i:] if len (left_inOrder) == 0 : root.left = None else : val = left_preOrder.pop(0 ) left_node = TreeNode(int (val)) root.left = self.reConstruct(left_node, left_preOrder, left_inOrder) if len (right_inOrder) == 0 : root.right = None else : val = right_preOrder.pop(0 ) right_node = TreeNode(int (val)) root.right = self.reConstruct(right_node, right_preOrder, right_inOrder) return root def solve (self , preOrder: List [int ], inOrder: List [int ] ) -> List [int ]: if len (preOrder) == 0 : return [] root_val = preOrder.pop(0 ) root = TreeNode(int (root_val)) root = self.reConstruct(root, preOrder, inOrder) current_layer = [root] next_layer = [] result = [root.val] while len (current_layer) > 0 : node = current_layer.pop(0 ) if node.left is not None : next_layer.append(node.left) if node.right is not None : next_layer.append(node.right) if len (current_layer) == 0 and len (next_layer) > 0 : result.append(next_layer[-1 ].val) current_layer = next_layer next_layer = [] return result