【问题标题】:Deep copy of graph structure图结构的深拷贝
【发布时间】:2011-01-14 02:33:54
【问题描述】:

我在 C 语言中有一个图形结构,并且想要对其进行深层复制(包括节点和边)。

结构如下:

struct li_list {
    struct li_node n;
};

struct li_node {
    struct li_node *next, *prev;
};

struct gr_graph {
    struct li_list nodes;
    int nodecount;
};

struct gr_node {
    struct li_node node;
    struct gr_graph *graph;
    int pred_count, succ_count;
    struct li_list pred, succ;
};

struct gr_edge {
    struct li_node succ, pred;
    struct gr_node *from, *to;
    unsigned long marks;
};

这些结构本身并不存在,而是“继承”在另一个结构中,如下所示:

struct ex_node {
    struct gr_node _; // "Superclass"
    int id;
    struct ex_node *union_find_parent;
    ...
}

有没有一种优雅的解决方案来创建这样一个结构的深层副本,包括更新对副本的引用?

注意:嵌套结构的成员不指向它包含的根结构,而是指向它们相关的嵌套结构(例如,ex_node._.pred.n.next 指向ex_edge._.pred)。这意味着当必须更新这些时,需要进行繁琐的指针运算。

到目前为止我的解决方案是

  1. Memcopy 所有结构
  2. 遍历所有副本
  3. 为所有包含引用的字段调用一堆宏(由于 C 中缺少 RTTI,我可能不会解决这个问题)
  4. 宏使用
    • offsetof计算根结构体的地址
    • 检索复制的等价物的地址
    • offsetof 使指针指向正确的嵌套结构

有没有更简单的方法来做到这一点?我也怕我添加更多字段时忘记添加宏调用。

【问题讨论】:

    标签: c


    【解决方案1】:

    我不认为你可以做一个深拷贝本身,因为指针会有一个内存地址分配给指针,我能想到的最好的深拷贝方法是简单地分配一个新的图形结构并复制数据(不是指针)并通过mallocing 新指针从那里构建它并调整ex_node 结构中的指针。那将是一个更彻底的解决方案...

    希望这会有所帮助, 最好的祝福, 汤姆。

    【讨论】:

    • 这比我概述的解决方案更好吗?
    • @Meinersbur:我认为'offsetof'是评估结构成员的字节偏移量,并将字节数作为size_t值返回,所以我不确定是否使用offsetof这样做的方法...顺便说一句,我并没有不同意您的解决方案,我的回答是您正在做的事情的抽象视图...
    【解决方案2】:

    听起来不错。我的 0.02 美元:

    • 不知道为什么需要li_listli_node。此外,您不需要li_node 的数据成员吗?
    • 整体结构看起来有点复杂(当然,我不知道你的要求)和 C++ 风格设计的味道(请原谅我,如果我错了)
    • memcpy 不是必需的。一个简单的分配就足够了。
    • 为每个带有指针成员的结构定义一个函数指针成员,这样就可以做到:

    所以:

    struct foo {
       int datum;
       int *p;
       foo_copy pfoo;
    };
    
    typedef void (*foo_copy)(const struct foo *src, struct foo *dst);
    
    void foo_cp(const struct foo *src, struct foo *dst)
    { 
        *dst = *src; // copy non-pointer data
        dst->p = malloc(sizeof *dst->p);
        dst->p = *src->p;
    }
    
    
    // somewhere else
    struct foo s;
    // initalize
    struct foo *t = malloc(sizeof *t);  
    s.copy(&s, &t);
    

    嵌套类型调用适当的成员复制方法...

    【讨论】:

    • li_listli_node 的“容器”,n 是哨兵### li_list、gr_graph 来自图书馆;只是 ex_* 在我身边。它旨在以这种方式使用。此处描述了该技术:stackoverflow.com/questions/326202/… ### 您的解决方案仅适用于树,而不适用于图形。
    • @Meinersbur:你是说周期吗?
    • 不只是循环,还有 DAG:被引用两次的元素将被复制两次。
    • "memcpy is not required": 实际上,所有 ex_* 都存储在一个块中。为了复制数据,整个块只是被内存复制,而不是单个结构。
    【解决方案3】:

    memcpy 所有结构并创建一个排序列表,其中每个条目包含原始结构的地址和结构副本的地址。

    现在遍历所有副本。对于所有复制结构中的每个指针变量,在排序列表中搜索指针并将其替换为其副本的地址。

    【讨论】:

    • 我有一个更简单的解决方案(这对我来说已经足够了):每个ex_* 都有一个成员copy,它存储对副本的引用。当我有对原始的参考时,我也有对副本的参考,而无需查找(排序的)列表。
    • 对于为您定义的结构,它可以工作。但是采用图书馆提供的结构。假设您定义的结构没有直接引用它?在这种情况下,您需要在某处存储对此类结构副本的引用。
    【解决方案4】:

    是的,有一个使用生成树和装饰器模式的优雅解决方案。

    -首先,构建图的生成树。您可以使用 DFS(深度优先搜索) 或 BFS(广度优先搜索)来实现这一点。使用装饰器模式给 每个访问过的节点都有一个唯一的标识符。

    -下一步,(或同时)从头到尾遍历生成树 并通过分配新节点和连接开始构建你的第二棵树 形成生成树的边。

    -最后,再通过一次生成树,并使用同步的 标识符,连接新图中剩余的缺失边,使它们与 旧图的连通性。 (例如,如果 graph1 中的 node5 有连接到 node7 和 node 11 的边,那么 使用 graph2 的排序将其 node5 连接到其 node7 和 11。)

    【讨论】:

      猜你喜欢
      • 2022-01-15
      • 2011-08-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-09
      • 2012-07-05
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多