【问题标题】:Binary Tree Transfer二叉树传输
【发布时间】:2011-04-06 03:14:19
【问题描述】:

如何有效地在两个不同的系统之间传输二叉树(不是平衡树),同时保留其完整结构?

【问题讨论】:

  • 是二叉搜索树还是只是二叉树?
  • 请提供用于存储树的内存结构的详细信息。例如。您使用的是连续的内存块吗?您是否为树中的每个单独数据块调用malloc?根指针在哪里?
  • +1 只是因为我认为它不值得被否决。问题没有歧义。

标签: c data-structures binary-tree


【解决方案1】:

显而易见的方法是将二叉树转换为节点数组,将原始树中的每个指针替换为数组中节点的索引。然后,您可以传输该数组,并在另一端重建具有相同结构的树。

【讨论】:

    【解决方案2】:

    下面给出的这个结构

        [x]
       /   \
     [L]   [R]
       \
       [P]  
    


    可以很容易地翻译成

    (X,(L,-,(P,-,-)),(R,-,-))
    

    另外,阅读Eric Lippert 的帖子。

    注意:我觉得,类似的东西应该适用于任意树。有cmets吗?

    【讨论】:

    • 对于任意树,不需要'-'。 (X,(L,(P)),(R)) 可以。还是不会?
    【解决方案3】:

    定义序列化函数。

    void serialize( FILE *f, my_tree *node, _Bool is_root ) {
        if ( node == NULL ) {
            fputc( no_child, f );
            return;
        }
    
        if ( ! is_root ) fputc( data_prefix, f );
        write_data( f, node->data );
        fputc( data_terminator, f );
    
        write_data( node->left_child );
        write_data( node->right_child );
    }
    
    void deserialize_node( FILE *f, my_tree *node ) {
        node->data = read_data_field( f );
    
        if ( fgetc( f ) != no_child ) {
             node->left_child = calloc( 1, sizeof( my_tree ) );
             deserialize( f, node->left_child, false );
        }
    
        if ( fgetc( f ) != no_child ) {
             node->right_child = calloc( 1, sizeof( my_tree ) );
             deserialize( f, node->right_child, false );
        }
    }
    

    想想看,这个简单的方案(其中data_terminatorno_child 必须是单个字符)允许data_terminatorno_child 相等。

    【讨论】:

      【解决方案4】:

      这样做的主要问题是您必须将内存表示中的指针或引用替换为可用于明确表示指向的节点的其他内容。

           foo
          /   \
       cat     zebra
          \
           dog
      

      这样做的一种方法是用指针交换键 - 更像是数组索引而不是正确的指针。

      1 2 "foo"
      3 _ "cat"
      _ _ "zebra"
      _ _ "dog"
      

      在此表示中,第一个字段是左孩子的行号(从 0 开始计数,即根节点),第二个字段是右孩子,第三个字段是值。树按字母顺序排列。这看起来很简单,但实际上很难做到。

      类似的方法是将密钥放在每个条目中,而不是依赖于位置。这种方法可以使用原始指针作为键,而读入的代码必须构建一个转换/符号表来在键和新指针之间切换。

      另一种解决方法是使用 lisp-esque 树: (foo (cat () (dog () ()) (zebra () () ))

      格式化以便于查看:

      (foo
         (cat
            ()
            (dog
               ()
               ()
            )
         )
         (zebra
              ()
              ()
         )
      )
      

      这可以通过简单的顺序遍历轻松生成。它也可以用一个非常简单的递归体面解析器读入。您还可以通过省略 nil() 或您为 NULL 指针选择的任何内容来更改此设置以减小序列化格式中叶节点的大小。

      另一种方法与第一种方法类似,是将所有树存储在一块内存中,可以转储到磁盘并从磁盘读取。 this 中的指针将相对于此内存块的开头,而不是绝对指针。这对于同一类型机器(使用相同 CPU 内存宽度)上的两个程序共享树(或其他图)来说是一种快速的方法,但可能难以实现。

      lisp-esqe 版本非常容易实现,但不容易扩展到不是树的事物,其中可能存在循环引用或特定节点的多个父节点,尽管它可以是完毕。它也不容易扩展以处理在特定文件中存储多个结构。

      线位置索引版本适用于大多数类型的图形,但在特定文件中存储多个结构需要稍微改变这种格式。

      无论您选择什么,您都需要确保您可以处理所有可能作为节点数据出现的值。例如,如果节点数据可能包含")\n,那么它可能会导致我展示的某些格式出现问题,并且需要对这些字符进行转义。不过,您可以为字段添加长度前缀或使用常量结构布局来解决此问题。

      如果您计划在不同机器类型之间共享数据,您还需要确保所有二进制字段都以字节序一致的方式存储。您还希望这些数据具有一致的大小(使用 stdint.h 类型而不是 int 和 long)以及浮点数等事物的规范表示。

      【讨论】:

        【解决方案5】:

        方法一:我们可以遍历树两次:

        1. 第一次搞定InOrder遍历
        2. 第二次获取PostOrder遍历

        现在通过在目的地使用这两个列表,我们可以重建二叉树,如下所示:

        public class ConstructBinaryTreeFromInorderAndPostorder {
        
            int index;
        
            public TreeNode buildTree( List<Integer> inOrder, List<Integer> postOrder) {
                index = postOrder.size() - 1;
                if (postOrder.size() == 1)
                    return new TreeNode(postOrder.get(0));
        
                return constructTree(inOrder,postOrder, 0, postOrder.size() - 1);
            }
        
        
            public TreeNode constructTree(List<Integer> inOrder, List<Integer> postOrder, int start, int end) {
        
                if (start > end) {
                    return null;
                }
                TreeNode root = new TreeNode(postOrder.get(index--));
        
                if (start == end) {
                    return root;
                }
                int indexInInorder = search(inOrder, start, end, root.val);
        
                root.right = constructTree(inOrder, postOrder, indexInInorder + 1, end);
                root.left = constructTree(inOrder, postOrder, start, indexInInorder - 1);
        
        
                return root;
            }
        
        
            public int search(List<Integer> inOrder, int strt, int end, int value) {
                int i = 0;
                for (i = strt; i <= end; i++) {
                    if (inOrder.get(i) == value)
                        return i;
                }
                return i;
            }
        
            public static void main(String[] args) {
                List<Integer> inorder = Arrays.asList(2, 1, 3);
                List<Integer> postOrder = Arrays.asList(2, 3, 1);
                System.out.println(new ConstructBinaryTreeFromInorderAndPostorder().buildTree(inorder,postOrder ));
            }
        }
        

        获取InOrder遍历:

        public class InorderTraversal {
            void inOrderTraversal2(TreeNode node) {
                if (node == null) {
                    return;
                }
                inOrderTraversal2(node.left);
                System.out.println(node.val);
                inOrderTraversal2(node.right);
            }
        }
        

        获取PostOrder遍历:

        public class PostOrderTraversal {
        
            void postOrderTraversal(TreeNode node) {
                if (node == null) {
                    return;
                }
                postOrderTraversal(node.left);
                postOrderTraversal(node.right);
                System.out.println(node.val);
            }
        }
        

        方法 2: 我们可以通过存储Preorder traversalnull 指针的标记来节省空间。 让null 指针的标记为'-1'

        Input:
             12
            /
          13
        Output: 12 13 -1 -1
        
        Input:
              20
            /   \
           8     22 
        Output: 20 8 -1 -1 22 -1 -1 
        
        Input:
                 20
               /    
              8     
             / \
            4  12 
              /  \
             10  14
        Output: 20 8 4 -1 -1 12 10 -1 -1 14 -1 -1 -1 
        
        Input:
                  20
                 /    
                8     
              /
            10
            /
           5
        Output: 20 8 10 5 -1 -1 -1 -1 -1 
        
        Input:
                  20
                    \
                     8
                      \   
                       10
                         \
                          5   
        Output: 20 -1 8 -1 10 -1 5 -1 -1 
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2015-10-24
          • 2022-01-06
          • 1970-01-01
          • 2017-04-14
          • 1970-01-01
          • 1970-01-01
          • 2012-01-09
          相关资源
          最近更新 更多