Кодирование функции для копирования связанного списка в C++

мне нужно реализовать вспомогательную функцию с именем copyList, имеющую один параметр, указатель на ListNode. Эта функция должна возвращать указатель на первый узел копию связанного списка. Другими словами, Мне нужно закодировать функцию на C++, которая берет узел заголовка связанного списка и копирует весь связанный список, возвращая указатель на новый узел заголовка. Мне нужна помощь в реализации данной функции, и это то, что у меня есть сейчас.

Listnode *SortedList::copyList(Listnode *L) {

    Listnode *current = L;  //holds the current node

    Listnode *copy = new Listnode;
    copy->next = NULL;

    //traverses the list
    while (current != NULL) {
       *(copy->student) = *(current->student);
       *(copy->next) = *(current->next);

        copy = copy->next;
        current = current->next;
    }
    return copy;
}

кроме того, это структуры Listnode я работаю с:

struct Listnode {    
  Student *student;
  Listnode *next;
};

Примечание: еще один фактор, с которым я сталкиваюсь с этой функцией, - это идея возврата указателя на локальную переменную.

7 ответов


первый вопрос, который вам нужно задать себе, - это семантика копирования. В частности, вы используете Student* как содержимое узла. Что означает копирование содержимого узла? Должны ли мы скопировать указатель так, чтобы два списка указывали на (общий доступ) те же экземпляры student, или вы должны выполнить глубокая копия?

struct Listnode {    
  Student *student; // a pointer?  shouldn't this be a `Student` object?
  Listnode *next;
};

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

Я думаю, что Вы код должен выглядеть как:

Listnode *SortedList::copyList(Listnode *L) {

    Listnode *current = L;

    // Assume the list contains at least 1 student.
    Listnode *copy = new Listnode;
    copy->student = new Student(*current->student);
    copy->next = NULL;

    // Keep track of first element of the copy.
    Listnode *const head = copy;

    // 1st element already copied.
    current = current->next;

    while (current != NULL) {
       // Allocate the next node and advance `copy` to the element being copied.
       copy = copy->next = new Listnode;

       // Copy the node contents; don't share references to students.
       copy->student = new Student(*current->student);

       // No next element (yet).
       copy->next = NULL;

       // Advance 'current' to the next element
       current = current->next;
    }

    // Return pointer to first (not last) element.
    return head;
}

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

copy->student = current->student;

вместо

copy->student = new Student(*current->student);

Это отлично вопрос: так как вы сделали большую часть работы самостоятельно, гораздо лучше, чем большинство "пожалуйста, сделайте мою домашнюю работу за меня" вопросы.

несколько пунктов.

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

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

что-то вроде (псевдо-код (но C++ - как) для домашней работы, извините):

# Detect empty list early.

if current == NULL:
    return NULL;

# Do first node as special case, maintain pointer to last element
# for appending, and start with second original node.

copy = new node()
last = copy

copy->payload = current->payload
current = current->next

# While more nodes to copy.

while current != NULL:
    # Create a new node, tracking last.

    last->next = new node()
    last = last->next

    # Transfer payload and advance pointer in original list.

    last->payload = current->payload
    current = current->next

# Need to terminate new list and return address of its first node

last->next = NULL
return copy

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


Я пытался сделать то же самое. Мои требования были:

1. Каждый узел является очень простым и простым классом (я отошел от модели структуры).
2. Я хочу создать глубокую копию, а не просто указатель на старый список.

Способ, которым я решил это сделать, заключается в следующем коде C++:

template <class T>
Node <T> * copy(Node <T> * rhs)
{
    Node <T> * current = new Node<T>();
    Node <T> * pHead = current;
    for (Node <T> * p = rhs; p; p = p->pNext)
    {
        Node <T> * prev = current;
        prev->data = p->data;
        if (p->pNext != NULL)
        {
            Node <T> * next = new Node<T>();
            prev->pNext = next;
            current = next;
        }
        else
        {
            prev->pNext = NULL;
        }
    }
    return pHead;
}

это работает хорошо, без ошибок. Поскольку "голова" - это особый случай, существует необходимость в моей реализации "текущий" указатель.


заявление copy->next = current->next неправильный. Вы должны сделать

Create the first node copy here
copy->student = current->student;
copy->next = NULL;
while(current->next!=NULL)
{
    Create new node TEMP here
    copy->next = TEMP;
    TEMP->student = current->student;
    TEMP->next = NULL;
    copy = TEMP;
}

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

Listnode *startCopyNode = copy;

while (current != NULL) {
   *(copy->student) = *(current->student);
    copy->next = new Listnode;
    copy = copy->next;
    current = current->next;
}

copy->next = NULL;
return startCopyNode;

Не забудьте delete узлы связанного списка.


@pat, я думаю, вы получите seg_fault, потому что вы создаете память только один раз. Вам нужно создать память (в основном вызвать "new") для каждого узла. Узнайте, где вам нужно использовать ключевое слово "new", чтобы создать память для всех узлов.

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


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

другим фактором, с которым я сталкиваюсь с этой функцией, является идея возврата указателя на локальную переменную.

вы не возвращаете указатель на локальную переменную; когда ты позвонила new, вы выделили память на куча и возвращают указатель на это (что, конечно, означает, что вам нужно не забыть вызвать delete чтобы освободить его, когда вы закончите с новым списком, с за пределами функции).