【问题标题】:Deep Copy Linked List - O(n)深拷贝链表 - O(n)
【发布时间】:2011-02-20 14:54:44
【问题描述】:

我正在尝试深度复制链接列表。我需要一个在线性时间 O(n) 中执行的算法。这是我现在所拥有的,但我无法弄清楚它出了什么问题。我的应用程序崩溃了,我怀疑是内存泄漏,我还没有弄清楚。这就是我现在拥有的

 struct node {
    struct node *next;
    struct node *ref;
 };


struct node *copy(struct node *root) {
    struct node *i, *j, *new_root = NULL;

    for (i = root, j = NULL; i; j = i, i = i->next) {
        struct node *new_node;
        if (!new_node) 
        {
            abort();
        }
        if (j) 
        {
            j->next = new_node;
        }
        else 
        {
            new_root = new_node;
        }

        new_node->ref = i->ref;
        i->ref = new_node;
    }
    if (j) 
    {
            j->next = NULL;
    }
    for (i = root, j = new_root; i; i = i->next, j = j->next)
        j->ref =i->next->ref;

      return new_root;
}

谁能指出我哪里出了问题?

【问题讨论】:

  • 假设它是 C++ 是否正确?
  • 是的。 C++ 或 Ansi C 。我可以在其中任何一个中使用实现
  • 发布一个完整的可编译示例,以便人们可以帮助您。也许你也应该尝试在你的代码中使用像 valgrind 这样的工具。
  • ref 是干什么用的?这是树形结构吗?是否需要复制引用节点,或者目标列表中的引用节点是否将指向源列表中的相同引用节点?
  • ref 持有对列表中随机节点的引用。理想情况下,引用节点应该指向新复制列表中的节点,并且不应该依赖于原始源列表。

标签: c++ c algorithm linked-list


【解决方案1】:

仅此一段:

    struct node *new_node;
    if (!new_node) 
    {
        abort();
    }

对于随机发生的abort() 似乎很好。 new_node 未分配,将包含一个随机值。 !new_node 表达式可能已经是致命的(在某些系统上)。

作为一般提示,您应该只需要 1 个 for 循环。一些代码预先建立new_root

但真正的深拷贝还需要克隆ref 指向的任何内容。在我看来,第二个循环将原始内容中的某些内容分配到副本中。但我不确定,ref 是什么?

【讨论】:

  • 我只是想检查 new_node 是否为 NULL 。有更好的方法吗?
  • 我认为你需要 C++ 中的struct node *new_node = new node();。不确定这是否是 C 中的有效语法。
  • 我正在尝试克隆 ref 指向的内容,但我很困惑的是如何在我的重复列表中克隆它?
【解决方案2】:

我立即注意到的一件事是您从未为 new_node 分配空间。由于不保证自动变量被初始化,new_node 将被设置为之前在该内存中的任何值。您可能应该从以下内容开始:

struct node *new_node = (new_node *) malloc(sizeof(struct node));

在 C 中,或者如果您使用的是 C++:

node* new_node = new node;

复制列表非常简单。但是,ref 指针指向新列表中相对于源列表的相同节点的要求将很难以任何有效的方式实现。首先,您需要一些方法来识别相对于它们指向的源列表的节点。您可以在每个节点中放置某种标识符,例如在第一个节点中设置为 0 的 int,在第二个节点中设置为 1 等。然后在复制列表后,您可以再次通过列表进行设置参考指针。这种方法的问题(除了向每个节点添加另一个变量)是它会使算法的时间复杂度从 O(n) 跳到 O(n^2)。

【讨论】:

  • 那么我是否必须使用 malloc() 为 new_node 分配内存以保证空间分配??
  • 是的,指针只是一个地址。除非你让它指向某事。在这种情况下,您需要分配一些东西。
【解决方案3】:

这是可能的,但需要一些工作。我将假设 C++,并省略 struct node 中的 struct 关键字。

您需要做一些簿记来跟踪“ref”指针。在这里,我将它们转换为原始列表中的数字索引,然后返回到新列表中的指针。

node *copy_list(node const *head)
{
    // maps "ref" pointers in old list to indices
    std::map<node const *, size_t> ptr_index;
    // maps indices into new list to pointers
    std::map<size_t, node *>       index_ptr;

    size_t length = 0;
    node       *curn; // ptr into new list
    node const *curo; // ptr into old list

    node       *copy = NULL;

    for (curo = head; curo != NULL; curo = curo->next) {
        ptr_index[curo] = length;
        length++;

        // construct copy, disregarding ref for now
        curn = new node;
        curn->next = copy;
        copy = curn;
    }

    curn = copy;
    for (size_t i=0; i < length; i++, curn = curn->next)
        index_ptr[i] = curn;

    // set ref pointers in copy
    for (curo = head, curn = copy; curo != NULL; ) {
        curn->ref = index_ptr[ptr_index[curo->ref]];

        curo = curo->next;
        curn = curn->next;
    }

    return copy;
}

这个算法在 O(n lg n) 中运行,因为它将所有 n 列表元素存储在一个 std::map 中,它有 O (lg n) 插入和检索复杂度。它可以通过使用哈希表来实现线性化。

注意:未经测试,可能包含错误。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-10
    • 2011-06-08
    • 1970-01-01
    • 2018-04-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多