fucking-algorithm icon indicating copy to clipboard operation
fucking-algorithm copied to clipboard

东哥带你刷二叉树(序列化篇) :: labuladong的算法小抄

Open labuladong opened this issue 2 years ago • 16 comments

文章链接点这里:东哥带你刷二叉树(序列化篇)

评论礼仪 见这里,违者直接拉黑。

labuladong avatar Feb 05 '22 07:02 labuladong

这篇没人吗

gp868 avatar Apr 16 '22 10:04 gp868

这篇是不是新加的所以没人

Violet981 avatar Apr 17 '22 00:04 Violet981

这篇感觉看的不是很懂啊🤣

gp868 avatar Apr 18 '22 02:04 gp868

前文构造篇不是正好用上了? 序列化就是遍历出先序、中序的序列,或者遍历出后序、中序的序列,用分隔符分好 反序列化就是构造

leibnizl avatar Apr 27 '22 15:04 leibnizl

这题感觉就是考二叉树的遍历和构造,其实想清楚了也没什么难的,就是那些小细节折磨人

Maxiah avatar May 07 '22 02:05 Maxiah

python前序重建二叉树AC代码:

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
import collections
from typing import List
NULL = 1001
class Codec:

    def serialize(self, root):
        """Encodes a tree to a single string.

        :type root: TreeNode
        :rtype: str
        """
        ans = []

        def traverse(node):
            if not node:
                ans.append(NULL)
                return
            ans.append(node.val)
            traverse(node.left)
            traverse(node.right)

        if not root:
            return ""
        traverse(root)
        return ",".join([str(ele) for ele in ans])

    def deserialize(self, data):
        """Decodes your encoded data to tree.

        :type data: str
        :rtype: TreeNode
        """
        if data == "":
            return None
        data = [int(ele) for ele in data.split(",")]

        def deserivalize_pre(data: List) -> TreeNode:
            if not data:
                return None
            val = data.popleft()
            if val == NULL:
                return None
            root = TreeNode(val)
            root.left = deserivalize_pre(data)
            root.right = deserivalize_pre(data)
            return root
        return deserivalize_pre(collections.deque(data))

# Your Codec object will be instantiated and called as such:
# ser = Codec()
# deser = Codec()
# ans = deser.deserialize(ser.serialize(root))

lemonadeseason avatar May 10 '22 07:05 lemonadeseason

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Codec {
// Encodes a tree to a single string.
    String NULL = "#";
    String SEP = ",";
    public String serialize(TreeNode root) {
        StringBuilder sb = new StringBuilder();
        serializeHelp(root, sb);
        return sb.toString();
    }

    public void serializeHelp(TreeNode root, StringBuilder sb) {
        if (root == null) {
            sb.append(NULL).append(SEP);
            return;
        }
        sb.append(root.val).append(SEP);
        serializeHelp(root.left, sb);
        serializeHelp(root.right, sb);
    }

    // Decodes your encoded data to tree.
    public TreeNode deserialize(String data) {
        if (data.length() == 0) {
            return null;
        }
        String[] nodes = data.split(",");
        TreeNode root = deserializeHello(nodes);
        return root;
    }

    int nodesStart = 0;
    public TreeNode deserializeHello(String[] nodes) {
        if (nodesStart > nodes.length) {
            return null;
        }
        if (nodes[nodesStart].equals("#")) {
            nodesStart++;
            return null;
        }
        int rootVal = new Integer(nodes[nodesStart]);
        nodesStart++;
        TreeNode root = new TreeNode(rootVal);
        root.left = deserializeHello(nodes);
        root.right = deserializeHello(nodes);
        return root;
    }
}

// Your Codec object will be instantiated and called as such:
// Codec ser = new Codec();
// Codec deser = new Codec();
// TreeNode ans = deser.deserialize(ser.serialize(root));

lxw665 avatar May 13 '22 06:05 lxw665

js版本: 在反序列化时,使用指针优化时间复杂度

/**
 * Definition for a binary tree node.
 * function TreeNode(val) {
 *     this.val = val;
 *     this.left = this.right = null;
 * }
 */

/**
 * Encodes a tree to a single string.
 *
 * @param {TreeNode} root
 * @return {string}
 */
var serialize = function (root) {
  let res = [];
  const traverse = (root) => {
    if (root === null) return res.push("Null");
    res.push(root.val);
    traverse(root.left);
    traverse(root.right);
  };
  traverse(root);
  return res.toString();
};

/**
 * Decodes your encoded data to tree.
 *
 * @param {string} data
 * @return {TreeNode}
 */
var deserialize = function (data) {
  const nodes = data.split(",");
  let p = 0;
  const deTraverse = (nodes) => {
    // if (nodes[0] === "Null") {
    //   nodes.shift();
    if (nodes[p] === "Null") {
      p++;
      return null;
    }
    // const root = new TreeNode(parseInt(nodes[0]));
    // nodes.shift();
    const root = new TreeNode(parseInt(nodes[p]));
    p++;

    root.left = deTraverse(nodes);
    root.right = deTraverse(nodes);
    return root;
  };

  return deTraverse(nodes);
};

gaoxiaoduan avatar May 30 '22 03:05 gaoxiaoduan

序列化&反序列化 | 等同于加密&解密。 按照算法加密,在按照算法解密。 本质是 按照定义的结构去加 去解

LeroyPine avatar May 30 '22 11:05 LeroyPine

C++版(感觉就像再考字符串操作,查了一堆字符串string类的操作 :joy:

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Codec
{
public:
    string StringBuilder;
    // 通过前序遍历来进行序列化 间隔符位 ,  空指针为 #
    void preordercoding(TreeNode *root)
    {
        if (root == nullptr)
        {
            StringBuilder.append("#");
            StringBuilder.append(",");
            return;
        }
        StringBuilder.append(to_string(root->val));
        StringBuilder.append(",");
        preordercoding(root->left);
        preordercoding(root->right);
    }
    // Encodes a tree to a single string.
    string serialize(TreeNode *root)
    {
        StringBuilder.clear();
        preordercoding(root);
        return StringBuilder;
    }
    
    // 反序列化
    TreeNode *preorderdecoding(string &data)
    {
        // 字符串已经空了,返回空指针
        if (data.empty())
            return nullptr;
        // 发现是 , 删掉
        if (data[0] == ',')
            data.erase(data.begin());
        // 发现是 # 删掉 并 返回空指针
        if (data[0] == '#')
        {
            data.erase(data.begin());
            return nullptr;
        }
        // 数值获取 以及 删掉字符串中的数值
        size_t sz;
        TreeNode *root = new TreeNode(stoi(data, &sz));
        data.erase(0, sz);
        // 递归
        root->left = preorderdecoding(data);
        root->right = preorderdecoding(data);
        // 返回该层完成的树
        return root;
    }
    // Decodes your encoded data to tree.
    TreeNode *deserialize(string data)
    {
        return preorderdecoding(data);
    }
};

// Your Codec object will be instantiated and called as such:
// Codec ser, deser;
// TreeNode* ans = deser.deserialize(ser.serialize(root));

mortal-94 avatar Jun 07 '22 10:06 mortal-94

前序的反序列如何判断root.left的长度?

xmh294450886 avatar Jun 08 '22 08:06 xmh294450886

解题思路

Java 后序遍历的序列化与反序列化

代码

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Codec {

    /**
     * 分隔符
     */
    final String SPLIT = ",";

    /**
     * 空节点
     */
    final String NULL = "NULL";

    // Encodes a tree to a single string.
    public String serialize(TreeNode root) {
        StringBuilder sb = new StringBuilder();
        serialize(root, sb);
        return sb.toString();
    }

    private void serialize(TreeNode root, StringBuilder sb) {
        if (root == null) {
            sb.append(NULL).append(SPLIT);
            return;
        }

        serialize(root.left, sb);
        serialize(root.right, sb);

        // 后序遍历位置
        sb.append(root.val).append(SPLIT);
    }

    // Decodes your encoded data to tree.
    public TreeNode deserialize(String data) {
        LinkedList<String> nodes = new LinkedList<>();
        for (String nodeVal : data.split(SPLIT)) {
            nodes.addLast(nodeVal);
        }
        return deserialize(nodes);
    }

    private TreeNode deserialize(LinkedList<String> nodes) {
        if (nodes.isEmpty()) {
            return null;
        }

        // 后序遍历的最后一个节点,即根节点
        String rootVal = nodes.removeLast();
        if (NULL.equals(rootVal)) {
            return null;
        }

        // 构造根节点
        TreeNode root = new TreeNode(Integer.parseInt(rootVal));
        // 先构造右子树,再构造左子树
        root.right = deserialize(nodes);
        root.left = deserialize(nodes);
        return root;
    }
}

// Your Codec object will be instantiated and called as such:
// Codec ser = new Codec();
// Codec deser = new Codec();
// TreeNode ans = deser.deserialize(ser.serialize(root));

Sm1lence avatar Jun 16 '22 02:06 Sm1lence

解题思路

Java 层序遍历序列化与反序列化

代码

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Codec {

    /**
     * 分隔符
     */
    final String SPLIT = ",";

    /**
     * 空节点
     */
    final String NULL = "NULL";

    // Encodes a tree to a single string.
    public String serialize(TreeNode root) {
        if (root == null) {
            return "";
        }
        StringBuilder sb = new StringBuilder();

        // 新建队列
        Queue<TreeNode> nodes = new LinkedList<>();
        nodes.offer(root);

        while (!nodes.isEmpty()) {
            // 弹出当前节点
            TreeNode currentNode = nodes.poll();

            // 层序遍历开始
            if (currentNode == null) {
                sb.append(NULL).append(SPLIT);
                continue;
            }
            sb.append(currentNode.val).append(SPLIT);
            // 层序遍历结束

            nodes.offer(currentNode.left);
            nodes.offer(currentNode.right);
        }

        return sb.toString();
    }

    // Decodes your encoded data to tree.
    public TreeNode deserialize(String data) {
        if (data.isEmpty()) {
            return null;
        }
        String[] nodeValues = data.split(SPLIT);
        // 数组的第一个值,就是根节点的值
        TreeNode root = new TreeNode(Integer.parseInt(nodeValues[0]));

        // 新建队列 存放根节点
        Queue<TreeNode> nodes = new LinkedList<>();
        nodes.offer(root);

        for (int i = 1; i < nodeValues.length; ) {
            // 弹出当前节点
            TreeNode parent = nodes.poll();
            // 获取左节点
            String left = nodeValues[i++];
            if (!NULL.equals(left)) {
                parent.left = new TreeNode(Integer.parseInt(left));
                nodes.offer(parent.left);
            } else {
                parent.left = null;
            }

            // 获取右节点
            String right = nodeValues[i++];
            if (!NULL.equals(right)) {
                parent.right = new TreeNode(Integer.parseInt(right));
                nodes.offer(parent.right);
            } else {
                parent.right = null;
            }
        }

        return root;
    }
}

// Your Codec object will be instantiated and called as such:
// Codec ser = new Codec();
// Codec deser = new Codec();
// TreeNode ans = deser.deserialize(ser.serialize(root));

Sm1lence avatar Jun 16 '22 03:06 Sm1lence

Python两种方法,附解释和代码

前序遍历

class Codec:

    def serialize(self, root):
        """
        用一个list记录,最后转为string导出:前序遍历,空节点计作N,然后用,连接
        """
        res = []
        def dfs(node):
            if not node:
                res.append("N")
                return
            res.append(str(node.val))
            dfs(node.left)
            dfs(node.right)
        
        dfs(root)
        return ",".join(res)

    def deserialize(self, data):
        """
        转化为list,然后用i遍历
        先确定根节点 root,然后遵循前序遍历的规则,递归生成左右子树
        """
        vals = data.split(",")
        self.i = 0
        
        def dfs():
            if vals[self.i] == "N":
                self.i += 1
                return None
            node = TreeNode(int(vals[self.i]))
            self.i += 1
            node.left = dfs()
            node.right = dfs()
            return node
        
        return dfs()

层序遍历


class Codec:

    def serialize(self, root):
        """
        用queue,把root添加进来,如果有值就加入res[],并且更新左右子树,如果是空就跳过。
        """
        if not root:
            return ""
        res = []
        queue = collections.deque()
        queue.append(root)
        while queue:
            node = queue.popleft()
            if not node:
                res.append("N")
                continue
            res.append(str(node.val))
            queue.append(node.left)
            queue.append(node.right)
        
        return ",".join(res)
        

    def deserialize(self, data):
        if not data:
            return None
        vals = data.split(",")
        root = TreeNode(int(vals[0]))
        queue = collections.deque()
        queue.append(root)
        i = 1
        while queue and i < len(vals):
            node = queue.popleft()
            if vals[i] != "N":
                left = TreeNode(int(vals[i]))
                node.left = left
                queue.append(left)
            i += 1
            if vals[i] != "N":
                right = TreeNode(int(vals[i]))
                node.right = right
                queue.append(right)
            i += 1
        
        return root

tanlandy avatar Jun 19 '22 18:06 tanlandy

Golang 层序遍历

前面一个忘记贴格式了

type Codec struct {
    
}

func Constructor() Codec {
    return Codec{}
}

// Serializes a tree to a single string.
func (this *Codec) serialize(root *TreeNode) string {
    if root == nil {
        return ""
    }
    queue := []*TreeNode{root}
    s := []string{strconv.Itoa(root.Val)}
    for len(queue) != 0 {
        n := len(queue)
        for i := 0; i < n; i++ {
            node := queue[i]
            if node.Left != nil {
                s = append(s, strconv.Itoa(node.Left.Val))
                queue = append(queue, node.Left)
            } else {
                s = append(s, "")
            }
            if node.Right != nil {
                s = append(s, strconv.Itoa(node.Right.Val))
                queue = append(queue, node.Right)
            } else {
                s = append(s, "")
            }
        }
        queue = queue[n:]
    }
    return strings.Join(s, ",")
}

// Deserializes your encoded data to tree.
func (this *Codec) deserialize(data string) *TreeNode {    
    if data == "" {
        return nil
    }
    s := strings.Split(data, ",")
    i := 0
    root := &TreeNode{Val: this.Atoi(s[i])}
    i++
    queue := []*TreeNode{root}
    for len(queue) != 0 {
        n := len(queue)
        for j := 0; j < n; j++ {
            node := queue[j]
            if s[i] != "" {
                node.Left = &TreeNode{Val: this.Atoi(s[i])}
                queue = append(queue, node.Left)
            } else {
                node.Left = nil
            }
            i++
            if s[i] != "" {
                node.Right = &TreeNode{Val: this.Atoi(s[i])}
                queue = append(queue, node.Right)
            } else {
                node.Right = nil
            }
            i++
        }
        queue = queue[n:]
    }
    return root
}

func (this *Codec) Atoi(s string) int {
    v, _ := strconv.Atoi(s)
    return v
}

treeforest avatar Jul 22 '22 02:07 treeforest

前序遍历+非递归的序列化+反序列化

记录一下反序列化使用前序+非递归方式的写法,这种反序列化时候的写法好像和二叉树中序非递归有点像

比较神奇,还没想通为什么前序序列化要用中序反序列化

class Codec {
public:
#define SEP ","
#define END "#"
#define MAX_STACK_SIZE 1000

    // Encodes a tree to a single string.
    string serialize(TreeNode* root) {
        // stack 空间预开辟
        s.resize(MAX_STACK_SIZE);
        // 栈顶指针
        int top = 0;
        // 根节点入栈
        s[top++] = root;
        TreeNode* node;
        string res;
        // 深度优先非递归(前序遍历)
        while (top != 0){
            node = s[--top];
            if (node == nullptr)
            {
                res += END SEP;
                continue;
            }
            res += to_string(node->val) + SEP;
            s[top++] = node->right;
            s[top++] = node->left;
        }
        return res;
    }

    // Decodes your encoded data to tree.
    TreeNode* deserialize(string data) {
        // stack 空间预开辟
        s.resize(MAX_STACK_SIZE);
        int top = 0;
        // 分割字符串
        stringstream fin(data);
        string item;
        vector<TreeNode*> preorder;
        while(getline(fin, item, SEP[0]))
        {
            if (item.compare(END) == 0)
            {
                preorder.emplace_back(nullptr);
                continue;
            }
            TreeNode* tmp = new TreeNode(stoi(item));
            preorder.emplace_back(tmp);
        }
        int i = 0;
        // 根节点
        TreeNode* pre = preorder[i++];
        while(top != 0 || pre != nullptr){
            // 按照前序排列,先构造左子树直到左子树遍历到叶子
            if (pre != nullptr) {
                s[top++] = pre;
                pre->left = preorder[i++];
                pre = pre->left;
            }
            // 回退之后从下向上简历右子树
            else {
                pre = s[--top];
                pre->right = preorder[i++];
                pre = pre->right;
            }
        } 
        return preorder[0];
    }
    
	// 用vector模拟stack,避免动态开辟空间
    vector<TreeNode*> s;

};

qianhang1994 avatar Jul 27 '22 08:07 qianhang1994

滴滴滴打卡

LeeHaruYa avatar Aug 12 '22 06:08 LeeHaruYa

打卡

yonggui-yan avatar Sep 05 '22 15:09 yonggui-yan