【问题标题】:Serialize and Deserialize Trie-like Data Structure序列化和反序列化类似 Trie 的数据结构
【发布时间】:2019-01-31 23:10:10
【问题描述】:

我正在尝试序列化和反序列化一个类似 Trie 的数据结构,该结构在每个节点中都有数据/字符。所以要形成一个完整的词,需要从根到叶节点的遍历。

序列化和反序列化应该是前序遍历,即在 DFS 方法中处理孩子。

# 标记该节点的遍历结束,即类似 trie 的节点不再有子节点。

这是我尝试过的。

public class SerializeDeserialize {

    public static void main(String[] args) {
        // prepare TrieNode Tree
        TrieNodeSD root = buildTrienodeTree();
        StringBuilder sb = new StringBuilder();
        serialize(root, sb);
        sb.deleteCharAt(sb.length()-1);
        System.out.println(sb.toString());
        System.out.println();
        TrieNodeSD newRoot = deserialize(sb.toString().split(","), new int[] {0});
        StringBuilder newsb = new StringBuilder();
        serialize(newRoot, newsb);
        newsb.deleteCharAt(newsb.length()-1);
        System.out.println(newsb.toString());
    }

    private static void serialize(TrieNodeSD node, StringBuilder sb) {
        if (node == null) return;
        sb.append(node.character + ",");
        if (node.characters != null && node.characters.size() > 0) {
            for (Character c : node.characters.keySet()) {
                serialize(node.characters.get(c), sb);
            }
        }
        sb.append("#,");
    }

    // DOESN'T WORK!!
    private static TrieNodeSD deserialize(String[] data, int[] t) {
        if (t[0] >= (data.length-1) || data[t[0]].equals("#")) return null;
        TrieNodeSD node = new TrieNodeSD(data[t[0]].charAt(0));
        t[0] = t[0] + 1;
        TrieNodeSD child = deserialize(data, t);
        if (child != null) node.characters.put(child.character, child);
        return node;
    }

    private static TrieNodeSD buildTrienodeTree() {
        TrieNodeSD root = new TrieNodeSD('A');

        root.characters.put('B', new TrieNodeSD('B'));
        root.characters.get('B').characters.put('E', new TrieNodeSD('E'));
        root.characters.get('B').characters.put('F', new TrieNodeSD('F'));
        root.characters.get('B').characters.get('F').characters.put('K', new TrieNodeSD('K'));

        root.characters.put('C', new TrieNodeSD('C'));

        root.characters.put('D', new TrieNodeSD('D'));
        root.characters.get('D').characters.put('G', new TrieNodeSD('G'));
        root.characters.get('D').characters.put('H', new TrieNodeSD('H'));
        root.characters.get('D').characters.put('I', new TrieNodeSD('I'));
        root.characters.get('D').characters.put('J', new TrieNodeSD('J'));

        return root;
    }
}

class TrieNodeSD {
    Map<Character, TrieNodeSD> characters;
    char character;
    public TrieNodeSD(char c) {
        this.characters = new HashMap<Character, TrieNodeSD>();
        this.character = c;
    }
    @Override
    public String toString() { return this.character + "";  }
}

序列化以预购格式提供输出(例如A,B,E,#,F,K,#,#,#,C,#,D,G,#,H,#,I,#,J,#,#,#)。

PROBLEM: 在反序列化过程中,代码没有正确处理所有子级,也没有将它们与正确的父级关联。

有人可以建议如何修复deserialize 方法中的处理或帮助我指点我缺少什么吗?

【问题讨论】:

    标签: java recursion serialization deserialization trie


    【解决方案1】:

    终于找到了反序列化Trie-Like数据结构的预序列化形式的方法。

    import java.util.HashMap;
    import java.util.Map;
    
    /**
     *                              A<br>
     *                  /           |           \<br>
     *                  B           C           D<br>
     *              /       \           /   /       \   \<br>
     *              E       F           G   H       I   J<br>
     *                      |<br>
     *                      K<br>
     * 
     *
     */
    public class SerializeDeserialize {
    
        public static void main(String[] args) {
            StringBuilder sb = new StringBuilder();
            StringBuilder newsb = new StringBuilder();
    
            // prepare TrieNode Tree
            TrieNodeSD root = buildTrienodeTree();
    
            // serialize tree into string
            serialize(root, sb);
            sb.deleteCharAt(sb.length() - 1);
            System.out.println(sb.toString());
            System.out.println();
    
            // construct tree again from serialized string
            TrieNodeSD newRoot = deserialize(sb.toString().split(","), new int[] { 0 });
    
            // Verify : again serialize above de-serialized tree to match both
            // trees serialized format.
            serialize(newRoot, newsb);
            newsb.deleteCharAt(newsb.length() - 1);
            System.out.println(newsb.toString());
        }
    
        private static void serialize(TrieNodeSD node, StringBuilder sb) {
            if (node == null) return;
            sb.append(node.character + ",");
            if (node.characters != null && node.characters.size() > 0) {
                for (Character c : node.characters.keySet()) {
                    serialize(node.characters.get(c), sb);
                }
            }
            sb.append("#,");
        }
    
        private static TrieNodeSD deserialize(String[] data, int[] t) {
            if (t[0] >= (data.length - 1) || data[t[0]].equals("#")) return null;
            TrieNodeSD node = new TrieNodeSD(data[t[0]].charAt(0));
            while (true) {
                t[0] = t[0] + 1;
                TrieNodeSD child = deserialize(data, t);
                if (child != null) node.characters.put(child.character, child);
                else break;
            }
            return node;
        }
    
        private static TrieNodeSD buildTrienodeTree() {
            TrieNodeSD root = new TrieNodeSD('A');
    
            root.characters.put('B', new TrieNodeSD('B'));
            root.characters.get('B').characters.put('E', new TrieNodeSD('E'));
            root.characters.get('B').characters.put('F', new TrieNodeSD('F'));
            root.characters.get('B').characters.get('F').characters.put('K', new TrieNodeSD('K'));
    
            root.characters.put('C', new TrieNodeSD('C'));
    
            root.characters.put('D', new TrieNodeSD('D'));
            root.characters.get('D').characters.put('G', new TrieNodeSD('G'));
            root.characters.get('D').characters.put('H', new TrieNodeSD('H'));
            root.characters.get('D').characters.put('I', new TrieNodeSD('I'));
            root.characters.get('D').characters.put('J', new TrieNodeSD('J'));
    
            return root;
        }
    }
    
    class TrieNodeSD {
        Map<Character, TrieNodeSD> characters;
        char character;
    
        public TrieNodeSD(char c) {
            this.characters = new HashMap<Character, TrieNodeSD>();
            this.character = c;
        }
    
        @Override
        public String toString() {
            return this.character + "";
        }
    }
    

    样例运行: 先序遍历序列化给定的 Trie-Like 数据结构,使用序列化后的字符串构造类似 Trie-data 的数据结构,即反序列化和最后再次序列化它以验证序列化的形式是否与实际树匹配。

    A,B,E,#,F,K,#,#,#,C,#,D,G,#,H,#,I,#,J,#,#,#
    
    A,B,E,#,F,K,#,#,#,C,#,D,G,#,H,#,I,#,J,#,#,#
    

    【讨论】:

      【解决方案2】:

      不太确定你的trie data structure,但如果你指的是trie,那肯定有一些误解。

      trie in wiki 有明确的规范。

      ...与二叉搜索树不同,树中的没有节点存储与该节点关联的key;相反,它在树中的 位置 定义了与之关联的键。一个节点的所有后代都有一个与该节点关联的字符串的公共前缀,而根与empty字符串相关联...

      (来自wiki的内容,我只是添加了重点)

      问题:在反序列化过程中,代码没有正确处理所有的孩子,并且没有将它们与正确的父母相关联。

      即使对于节点中有键的树结构,您的解决方案仍然无法正常工作,因为您使用 map 而不是 fixed-sized 数组忽略了子级的 大小这对于反序列化序列化数据非常重要。

      使用map 使得无法确定哪个节点是父节点以及哪个节点是子节点。

      至于binary search tree 或真正的trie tree,它们的结构是预定义的,然后您可以通过它序列化反序列化树,因为它们是确定性的。


      也许Radix tree 是你真正想要的。

      顺便说一句,您实际上可以直接在*Nodeserialisedeserialise

      例如序列化可以是这样的:

      @Override
      public String toString() {
          List<String> resultList = new ArrayList<>();
          for (TrieNode child : children) {
              if (child == null) resultList.add("#");
              else resultList.add(child.toString());
          }
          return resultList.stream().collect(Collectors.joining(","));
      }
      

      【讨论】:

      • 感谢 Trie 数据结构定义,我已经更新了我的问题和要求。是的,可以选择在 Node 类本身中添加序列化和反序列化,但我已经选择了序列化和反序列化逻辑远离对象类或 POJO 的设计选择。这为更新序列化和反序列化逻辑提供了灵活性,而不会干扰使用 POJO 而不是序列化或反序列化的客户端。我在序列化方面没有问题,但在反序列化预购遍历序列化字符串方面有问题。
      • @JRG 这不是问题。但正如我在答案中提到的,您的问题在于您用于存储数据的map,这使得树反序列化变得不可能,因为没有确定父节点和子节点的线索。
      • @JRG如果坚持使用map,可以尝试添加分隔符{}来区分父子节点。
      • 是的,如果您看到我的问题,我使用 # 表示分支结束。 # 和 position 帮助我重建了整个 Trie-Like 数据结构。您可以在 IDE 中运行我的解决方案。
      猜你喜欢
      • 1970-01-01
      • 2010-10-18
      • 1970-01-01
      • 1970-01-01
      • 2013-01-01
      • 1970-01-01
      • 2014-11-28
      • 1970-01-01
      • 2015-07-02
      相关资源
      最近更新 更多