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

leetcode H297

方法一:DFS(深度优先遍历)

二叉树的DFS序列化很简单,无非就是先序遍历还是后序遍历的选择问题,唯一需要注意的就是输出的字符串的格式问题,这会直接影响到反序列化的操作

1、序列化

以后我统一使用先序遍历,因为这样根节点就是第一个元素,一路往后即可

    def serialize(self, root):
        if not root:
        #  注意n后面有一个逗号
            return 'n,'
        left = self.serialize(root.left)
        right = self.serialize(root.right)
        #  注意我的返回方式,有个逗号隔开了,先序遍历
        #  虽然这个操作放在最后的但是是先序遍历!!
        return str(root.val)+','+left+right

2、反序列化

首先要将传进来的字符串data以‘,’为分割线割成列表,然后一个个的操作,反序列化需要使用到一个新的辅助函数。
整个反序列化最难的点就是递归的逻辑,这才是将递归用到了极致,就完全不管中间是如何实现的,传进来一个值,先判断是不是‘n’,如果不是,就以这个值创建一个 ’根‘ 节点node,这个时候创建’node‘的那个值已经被弹出去了,剩下的第一个就是左子树的值了,所以直接将剩下的整个data(具体实现是m_data,这样更好理解)传进help,help会一路向左走到头就像先序遍历二叉树那样一直走到某个node.left=None,然后返回一路试探性往右走,其实就是先序遍历的过程。
但是难就难在这,反着先序遍历(指的是,通过先序遍历的方式创建一个本不存在的二叉树)。

注意,pop的时候不用判断列表是否为空,因为列表必定以n结尾,n的话就不会继续向下递归了,换句话说当列表为空的时候刚好递归结束!

还是先记下来把,上来先设置递归出口,然后左子树,右子树,最后返回创建出来的node

    def deserialize(self, data):
        data = data.split(',')
        def help(m_data):
            #  使用pop(0)弹出第一个元素,注意是弹出
            val = m_data.pop(0)
            if val == 'n': return None
            #  这一要int一下,不int也能通过。。。。
            val = int(val)
            node = TreeNode(val)
            #  因为先序遍历中->左->右,所以反序列化先以val创建node
            #  然后定义左子树,再定义右子树!
            node.left = help(m_data)
            node.right = help(m_data)
            return node
        return help(data)

整体代码:

    def serialize(self, root):
        if not root:
            return 'n,'
        left = self.serialize(root.left)
        right = self.serialize(root.right)
        #  注意我的返回方式,有个逗号隔开了,先序遍历
        #  虽然这个操作放在最后的但是是先序遍历!!
        return str(root.val)+','+left+right

    def deserialize(self, data):
        data = data.split(',')

        def help(m_data):
            #  使用pop(0)弹出第一个元素,注意是弹出
            val = m_data.pop(0)
            if val == 'n': return
            node = TreeNode(val)
            node.left = help(m_data)
            node.right = help(m_data)
            return node
        return help(data)

这里有个小tips:
其实就是局部变量与全局变量的问题,在一个函数中创建另一个函数,新创建的函数中不能对原来函数中的变量进行改变,比如重新赋值什么的,但是可以对原来函数中的列表进行一些操作(增删改查)啥的,但是同样的,不可以重新定义它!

方法二:BFS(广度,也就是宽度优先遍历)

广度优先遍历比深度优先遍历更直观,他就是直接记录下了整棵数的样子,一层一层排下来,而且每一个位置都保留了值(空的就是n),值得多少只与整个二叉树得深度有关。
看了一下怎么感觉BFS的反序列化更麻烦了,还是记下来吧,记住了这一题就是你得了

1、序列化

BFS的序列化就是二叉树的宽度优先遍历,利用了队列,先进先出,虽然不知道哪些是哪一层的,但是后续的遍历会把关系都一一对应上,实际上的宽度优先遍历就是横着把所有的值一个个送进去,常规的遍历对于空节点是不操作的,也就是没有else的那一步,但是这里需要记下来,而且记录的仅仅是父节点非空的None,看起来是这样的:
看起来的样子
横着把所有的数送进去(包括n),没有的数不送,出来的就是
“dbeacnfnnnnngnn”

    def serialize(self, root):
        s = ""
        queue = []
        queue.append(root)
        while queue:
            root = queue.pop(0)
            if root:
                s += str(root.val)
                # 如果root.left为空也会被进队列
                queue.append(root.left)
                queue.append(root.right)
            else:
                s += "n"
            s += ","
        return s

2、反序列化

反序列化其实不用知道哪个数具体属于那一层也能还原,应为指向具体数据的指针i与每个节点一一对应,只要不乱就会一直对应下去,理解不了就tm记下来,就像二叉树的遍历一样,用多了自然就理解了

 def deserialize(self, data):
        tree = data.split(',')
        print(tree)
        #  如果根节点就是空得直接返回
        if tree[0] == "n":
            return None
        queue = []
        root = TreeNode(int(tree[0]))
        queue.append(root)
        i = 1
        while queue:
            #  cur是弹出来的节点,不是字符串的值
            cur = queue.pop(0)
            #  如果cur为空,那他就没有子节点,直接continue
            #  continue i不加2只因为在BFS的时候空节点就没有子节点!
            #  所以当cur到这个空节点的时候,i已经指向下一个非空节点的
            #  子节点的值了
            if not cur:
                continue
            cur.left = TreeNode(int(tree[i])) if tree[i] != "n" else None
            cur.right = TreeNode(int(tree[i + 1])) if tree[i + 1] != "n" else None
            #  注意是+2,因为i的后面一个是当前节点的右节点,两个才是下一个节点的左节点
            i += 2
            queue.append(cur.left)
            queue.append(cur.right)
        return root
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值