глубокое копирование Linkedlist без разрушения оригинального списка и дополнительного места для хранения (с использованием ANSI C) - PullRequest
1 голос
/ 18 декабря 2009

этот связанный список отличается от обычных связанных списков тем, что помимо следующего указателя, он также имеет другой указатель, который указывает на другой узел, кроме себя в связанном списке.

так, каков наилучший способ глубокого копирования этого связанного списка без уничтожения оригинала и без лишних пробелов?

Мой подход состоял в том, чтобы просто сделать цикл O (n ^ 2), но он должен быть более разумным.

Ответы [ 5 ]

10 голосов
/ 18 декабря 2009

Эта реализация полностью не проверена, но идея очень проста.

#include <stdlib.h>

struct node {
    struct node *next;
    struct node *what;
    void *data;
};

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 = malloc(sizeof(struct node));
        if (!new_node) abort();
        if (j) j->next = new_node;
        else new_root = new_node;
        new_node->data = i->data;
        i->data = new_node;
    }
    if (j) j->next = NULL;

    for (i = root, j = new_root; i; i = i->next, j = j->next)
        j->what = i->what->data;

    for (i = root, j = new_root; i; i = i->next, j = j->next)
        i->data = j->data;

    return new_root;
}
  1. После ->next в исходном списке создайте новый список, который отражает его. Укажите ->data на каждом узле в старом списке, чтобы указать на соответствующий ему узел в новом списке.
  2. Пройдите по обоим спискам параллельно и используйте ранее искаженный ->data, чтобы выяснить, куда попал ->what в новом списке.
  3. Пройдите через оба списка параллельно и восстановите ->data в исходное состояние.

Обратите внимание, что это 3 линейных прохода, поэтому его время равно O (n), и оно не использует никакой памяти сверх того, что необходимо для создания нового списка.

3 голосов
/ 17 января 2014

Вот протестированная C ++ реализация решения ephemient, которая использует следующий указатель для чередования узлов в первом проходе вместо временного захвата данных для этой цели. Сложность и использование пространства остаются такими же (O (N) (3 прохода) и O (1) пространство), что и реализация эфементов

struct Link {
    int data;
    shared_ptr<Link> next;
    weak_ptr<Link> random;
};

shared_ptr<Link> DeepCopy(Link* original) {
    if (!original) return nullptr;

    // 1st Pass:
    // make new nodes and interleave them between the nodes of the original list
    // by changing the next pointers ie:
    //      [1]->[copy of 1]->[2]->[copy of 2]->[3]->[copy of 3] ....
    // during this pass, we will also set the data of new nodes to match the old node
    // being copied.
    Link* node = original;
    while (node) {
        shared_ptr<Link> new_node = make_shared<Link>();
        new_node->data = node->data;
        new_node->next = node->next;
        node->next = new_node;
        node = node->next->next.get(); // skipping the "copy of" node just inserted
    }

    // 2nd Pass:
    // now go through and set the random ptr of the new nodes correctly.
    // i refers to a node on the original list and j the matching node on the new
    // list
    shared_ptr<Link> new_head = original->next; // i.e "copy of 1" is head of new list
    for (Link *i = original; i; i=i->next->next.get()) {
        Link *j = i->next.get(); // new nodes interleave original nodes
        j->random = i->random.lock()->next;
    }

    // 3rd Pass:
    // Restore the original list
    Link* new_node = new_head.get();
    node = original;
    while (node) {
        node->next = new_node->next;

        if (node->next)
            new_node->next = node->next->next;

        node = node->next.get();
        new_node = new_node->next.get();
    }

    return new_head;
}
0 голосов
/ 26 июля 2014

Вот версия c # для глубокой копии связанного списка со случайным указателем: временная сложность O (N) с постоянным пространством (т. Е. Только пространство, используемое для создания глубокой копии) (обратите внимание, что того же можно достичь с помощью карты с добавлением O(n) пробел)

(вы также можете обратиться к моему блогу: http://codingworkout.blogspot.com/2014/07/deep-copy-with-random-pointer.html) Суть изменений:

  1. В первой итерации создайте копиюзаданный исходный список такой, что исходный узел указывает на копии
  2. Во второй итерации обновляют случайные указатели скопированного списка
  3. отсоединяют их

    public LinkedListWithRandomPointerNode DeepCopy () {if (this._first == null) {return null;}

            LinkedListWithRandomPointerNode i1 = this._first, i2 = null;
            while(i1 != null)
            {
                //cre new node
                i2 = new LinkedListWithRandomPointerNode();
                i2.e = i1.e;
                i2.next = i1.next;
                //insert it after i1
                i1.next = i2;
                i1 = i2.next;
            }
            LinkedListWithRandomPointerNode firstNodeOfCopiedList = this._first.next;
    
            i1 = this._first; i2 = i1.next;
            while(i2 != null)
            {
                if(i1.random != null)
                {
                    i2.random = i1.random.next;
                }
                if(i2.next == null)
                {
                    break;
                }
                i1 = i2.next;
                i2 = i1.next;
            }
    
            i1 = this._first; i2 = i1.next;
            while (i2 != null)
            {
                i1.next = i2.next;
                i1 = i1.next;
                if (i1 != null)
                {
                    i2.next = i1.next;
                    i2 = i2.next;
                }
                else
                {
                    i2.next = null;
                    break;
                }
            }
            return firstNodeOfCopiedList;
        }
    

, где

public class LinkedListWithRandomPointerNode
    {
        public int e;
        public LinkedListWithRandomPointerNode next;
        public LinkedListWithRandomPointerNode random;
    }

Модульные испытания

[TestMethod]
        public void LinkedListWithRandomPointerTests()
        {
            var n = this.DeepCopy();
            Assert.IsNotNull(n);
            var i1 = this._first; var i2 = n;
            while(i1 != null)
            {
                Assert.IsNotNull(i2);
                Assert.IsTrue(i1.e == i2.e);
                if(i1.random != null)
                {
                    Assert.IsNotNull(i2.random);
                    Assert.IsTrue(i1.random.e == i2.random.e);
                }
                i1 = i1.next;
                i2 = i2.next;
            }
        }
        LinkedListWithRandomPointerNode _first = null;
        public LinkedListWithRandomPointer()
        {
            this._first = new LinkedListWithRandomPointerNode() { e = 7 };
            this._first.next = new LinkedListWithRandomPointerNode() { e = 14 };
            this._first.next.next = new LinkedListWithRandomPointerNode() { e = 21 };
            this._first.random = this._first.next.next;
            this._first.next.next.random = this._first;
        }
0 голосов
/ 25 января 2011

Это небольшое улучшение по сравнению с ответом @ephemient, который не использует дополнительный указатель what. Вот эскиз реализации в Scala. Комментарии предполагают список вроде:

+-----------+
|           |
|           v
A --> B --> C----+
^     |     ^    |
|     |     |    |
+-----+     +----+

и Node как:

class Node {
  var data: Node = null
  var next: Node = null

  def traverse(fn: Node => Unit) {
    var node = this
    while (node != null) {
      fn(node)
      node = node.next
    }
  }
}

Здесь метод клонирования.

def clone(list: Node): Node = {
  if (list == null) return null

  var cloneHead: Node = null

  // first, create n cloned nodes by traversing the original list in O(n) time
  // this step mutates the data pointers of original list
  list traverse { orig =>
    // for each node in original list, create a corresponding cloned node
    val newNode = new Node

    if (orig == list) {
      cloneHead = newNode // store the head node of the cloned list
    }

    // The next pointer of cloned node points to the node pointed to by
    // the corresponding orig node's data pointer i.e. A'.next and A'.data points to C
    newNode.next = orig.data

    // The data pointer on orig node points to the cloned node. i.e. A.data points to A'
    orig.data = newNode
  }

  // then, fix up the data pointers of cloned list by traversing the original 
  // list in O(n) time
  list traverse { orig =>
    clone = orig.data // since the data pointer on orig node points to 
                      // the corresponding cloned node

    // A'.next points to C and C.data points to C', so this sets A'.data to C'
    // this establishes the data pointers of the cloned nodes correctly
    clone.data = if (clone.next == null) null else clone.next.data
  }

  // then, fix up the data pointers of original list and next pointers of cloned list
  // by traversing the original list in O(n) time
  list traverse { orig =>
    clone = orig.data // since the data pointer on orig node still 
                      // points to the corresponding cloned node

    // A.data points to A' and A'.next points to C, so this sets A.data to C, 
    // restoring the original list
    orig.data = if (orig.data == null) null else orig.data.next

    // A.next points to B and B.data points to B', so this sets A'.next to B'
    // since we are working on linked list's next pointers, we don't have to worry
    // about back pointers - A will be handled by this traversal before B, 
    // so we know for sure that that B.data is not "fixed" yet 
    // (i.e. it still points to B') while A'.next is being set
    clone.next = if (orig.next == null) null else orig.next.data
  }

  cloneHead
}
0 голосов
/ 18 декабря 2009

Вы можете выполнить поиск по каждому узлу и объединить пути обратно, когда встретите уже посещенный узел.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...