从二叉树开始刷起(二)—— 初识BFS、DFS,实践序列化与反序列化

在上一篇从二叉树开始刷起(一)—— 熟悉递归套路中我们看了几道二叉树前、中、后序遍历的题,在这一节的开始,我觉得有必要先了解一下二叉树的层序遍历应该怎么做。以便引出后面的话题。

首先看一道普通的层序遍历的题:

102. 二叉树的层序遍历

image-20211011103411744

这道题刚读完题目其实是需要注意一点,就是其输出其实是一个二维的数组,所以在安排输出的时候我们也需要注意一下,那我们分析一下这道题应该怎么做吧。

首先我们来看一下还是传统递归的方式,其实也就是DFS的解法,从最开始的根节点开始,对每一层我们都把对应那一层的根节点加入到对应层数组中去,同时对下一层的左右子树执行同样的操作。来一起看一下代码:

class Solution:
    def levelOrder(self, root: TreeNode) -> List[List[int]]:
        # DFS解法
        res = []
  
        def dfs(index,root):
         	if not root:return
            # 如果某个节点还没有被访问过,就先给她安排一个位置
            if len(res) < index:
                res.append([])
            res[index-1].append(root.val)
            if root.left:
                # 要突破到下一层
                dfs(index+1, root.left)
            if root.right:
                dfs(index+1, root.right)
            
        dfs(1,root)
        return res

咱再来看看迭代的方法怎么做,或者说我们说的BFS的方法应该是什么样的。层序遍历采用BFS的方法会比较好做,只不过本题区别于传统BFS全部往一个队列里边丢的情况,需要单独分开每一层的结果(需要使用一个临时的数组和一个保存结果的数组),对于本题,BFS做的话其关键,还是理解,树这个数据结构是如何构造的,可以理解成一个三个key的json的格式,[val:root_val,left:{val,{}},right:{val…}],这样我们初始化构造队列的时候就是将整棵树全部放进去了,这时候队列里边也只有一个元素;循环体中我们对应的根节点元素取出来之后,整下的事便是将左右子树加入队列,这时队列中便变成了两个元素,依此类推,总之,队列中存放的永远是当前层的所有节点。(这里队列实现方式也可以用python中的双端队列,在其他类型题中可能更加方便)

class Solution:
    def levelOrder(self, root: TreeNode) -> List[List[int]]:
        # 先将整个树放到队列里边去,其实就是初始化一个队列,队列(列表)里边只有一个元素,就是那个树
        if not root:return []
        queue = [root]
        # 声明结果list
        res = []
        while (queue):
            # 循环读取队列,看队列里边有几个元素
            size = len(queue)
            tmp = []
            for _ in range(size):
                cur_tree = queue.pop(0)
                tmp.append(cur_tree.val)
                if cur_tree.left:
                    queue.append(cur_tree.left)
                if cur_tree.right:
                    queue.append(cur_tree.right)

            res.append(tmp)
        return res

OK,这里的做题套路需要反复思考咀嚼,有必要的话还需要多做几遍,形成肌肉记忆都好。后面咱会用到。

再来看这道题,也是一个大餐。

297. 二叉树的序列化与反序列化

image-20211011112703160

image-20211011112721979

这道题要求实现二叉树的序列化与反序列化,其实就是要求咱把二叉树编码成一个字符串然后再解码回来。

我们先来看看用递归的方法怎么写:

class Codec:
	# DFS,递归写法,前序遍历
    def serialize(self, root):
        """Encodes a tree to a single string.
        
        :type root: TreeNode
        :rtype: str
        """
        if not root:return "None"
        return str(root.val) + "," + self.serialize(root.left) +  "," + self.serialize(root.right)

    def deserialize(self, data):
        """Decodes your encoded data to tree.
        
        :type data: str
        :rtype: TreeNode
        """
        def dfs(data_list):
            root_val = data_list.pop(0)
            if root_val == "None":
                return
            root = TreeNode(root_val)
            root.left = dfs(data_list)
            root.right = dfs(data_list)
            return root
        if not data:return []
        data_list = data.split(",")
        return dfs(data_list)

总体来说还是比较常规的套路,以此我们还可以写出后序遍历的版本,但是中序遍历的版本就写不出来了。

注意后序遍历版本有一个小细节,解码的时候需要先解右子树:

class Codec:
    # dfs,后序遍历
    def serialize(self, root):
        if not root: 
            return 'None'
        return str(self.serialize(root.left)) + "," + str(self.serialize(root.right)) +  "," + str(root.val)

    def deserialize(self, data):
        def dfs(tree_list):
            val = tree_list.pop(-1)
            if val == "None":
                return None
            root = TreeNode(val)
            root.right = dfs(tree_list)
            root.left = dfs(tree_list)
            return root

        tree_list = data.split(",")
        if not tree_list: return None
        root = dfs(tree_list)
        return root

那我们再看迭代的话应该怎么写:

class Codec:
    def serialize(self, root):
        """Encodes a tree to a single string.
        
        :type root: TreeNode
        :rtype: str
        """
        res = ""
        if not root: return res
        # 基本要素:根节点元素所代表的树全部入队列
        queue = [root]

        # 判断条件是队列不为空
        while queue:
            # 每次都只看队首元素,作为当前树
            cur_tree = queue.pop(0)

            if cur_tree:
                # 执行完有效操作就继续将子树入队列
                res += str(cur_tree.val) + "," 
                queue.append(cur_tree.left)
                queue.append(cur_tree.right)
            else:
                res += "None" + ","
        return res

    def deserialize(self, data):
        """Decodes your encoded data to tree.
        
        :type data: str
        :rtype: TreeNode
        """
        if not data:return []
        data_list = data.split(",")
        queue = data_list
        root = TreeNode(data_list.pop(0))
        queue = [root]

        while queue:
            # 每次都只看队首元素,作为当前树
            cur_tree = queue.pop(0)
            if data_list:
                left = data_list.pop(0)
                if left != "None":
                    # 执行完有效操作就继续将子树入队列
                    cur_tree.left = TreeNode(left)
                    queue.append(cur_tree.left)
                else:
                    cur_tree.left = None

            if data_list:
                right = data_list.pop(0)
                if right != "None":
                    cur_tree.right = TreeNode(right)
                    queue.append(cur_tree.right)
                else:
                    cur_tree.right = None
            
        return root

编码相对容易一些,解码的时候需要认真确认往队列中添加元素的时机,以及添加的内容。

以上两种方法的时间复杂度均为O(n),因为每个元素都只被访问了一遍;空间复杂度也均为O(n)。这道题值得时常温习。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值