Почему эта реализация слияния в отсортированном связанном списке всегда устанавливает оба списка в NULL, когда должен только один? - PullRequest
1 голос
/ 01 февраля 2020

Честно говоря, я занимался этим около 10 часов, пробуя метод за методом, чтобы заставить это работать. Я пытаюсь создать третий список, который состоит из двух списков, слитых вместе и отсортированных от низкого к высокому (без дубликатов), затем устанавливаю первый список в новый список, таким образом объединяя второй список с первым. Но всякий раз, когда я запускаю программу, я получаю listData == NULL, даже в тех тестовых случаях, когда newListCurr должен абсолютно добавлять элементы в newList. У меня всегда были проблемы со связанными списками, так что, возможно, я неправильно понимаю некоторые основы, но я не могу понять это. Метод не должен объявлять ни одного нового узла и может иметь временную сложность O (n), что делает это намного сложнее. Я пробовал пару подходов, например, пытаясь вставить их непосредственно в listData (первый список), но существует постоянная проблема с указателями курсора, которые фактически не влияют на их соответствующие listData.

РЕДАКТИРОВАТЬ: списки предполагается заказать до слияния.

Вот метод слияния, все остальное работает как задумано, это только метод слияния.

template <class ItemType>
void SortedList<ItemType>::merge(SortedList& list) {
    Node<ItemType> * curr1 = listData;
    Node<ItemType> * curr2 = list.listData;
    Node<ItemType> * newList = NULL;
    Node<ItemType> * newListCurr = newList;
    while(curr2 != NULL || curr1 != NULL) {
        if(curr2 == NULL) {
            newListCurr = curr1;
            curr1 = curr1->next;
            newListCurr = newListCurr->next;
        }
        else if(curr1 == NULL) {
            newListCurr = curr2;
            curr2 = curr2->next;
            newListCurr = newListCurr->next;
        }
        else if(curr1->info < curr2->info) {
            newListCurr = curr1;
            curr1 = curr1->next;
            newListCurr = newListCurr->next;
        } else if (curr2->info < curr1->info) {
            newListCurr = curr2;
            curr2 = curr2->next;
            newListCurr = newListCurr->next;
        }
        else if (curr1->info == curr2->info) {
            newListCurr = curr1;
            curr1 = curr1->next;
            curr2 = curr2->next;
            newListCurr = newListCurr->next;
        }
    }
    list.listData = NULL;
    listData = newList;
}

РЕДАКТИРОВАТЬ: Я нашел решение для тех, у кого может быть эта проблема. Мне нужно было установить newList и newListCurr на узел, прежде чем я смог изменить остальную часть newList с помощью newListCurr. Вот мой обновленный код:

template <class ItemType>
void SortedList<ItemType>::merge(SortedList& list) {
    Node<ItemType> * curr1 = listData;
    Node<ItemType> * curr2 = list.listData;
    Node<ItemType> * newList = NULL;
    Node<ItemType> * newListCurr = newList;
    if(curr2 != NULL || curr1!= NULL) {
        if(curr2 == NULL) {
            newListCurr = curr1;
            curr1 = curr1->next;
        } else if(curr1 == NULL) {
            newListCurr = curr2;
            curr2 = curr2->next;
        } else if(curr1->info < curr2->info) {
            newListCurr = curr1;
            curr1 = curr1->next;
        } else if (curr2->info < curr1->info) {
            newListCurr = curr2;
            curr2 = curr2->next;
        } else if (curr1->info == curr2->info) {
            newListCurr = curr1;
            curr1 = curr1->next;
            curr2 = curr2->next;
        }
        newList = newListCurr;
    }
    while(curr2 != NULL || curr1 != NULL) {
        if(curr2 == NULL) {
            newListCurr->next = curr1;
            curr1 = curr1->next;
            newListCurr = newListCurr->next;
        }
        else if(curr1 == NULL) {
            newListCurr->next = curr2;
            curr2 = curr2->next;
            newListCurr = newListCurr->next;
        }
        else if(curr1->info < curr2->info) {

            newListCurr->next = curr1;
            curr1 = curr1->next;
            newListCurr = newListCurr->next;
        } else if (curr2->info < curr1->info) {
            newListCurr->next = curr2;
            curr2 = curr2->next;
            newListCurr = newListCurr->next;
        }
        else if (curr1->info == curr2->info) {
            newListCurr->next = curr1;
            curr1 = curr1->next;
            curr2 = curr2->next;
            newListCurr = newListCurr->next;
        }
    }
    list.listData = NULL;
    listData = newList;
}

Ответы [ 3 ]

2 голосов
/ 01 февраля 2020

newList имеет значение NULL в верхней части и никогда не назначается заново. Когда вы устанавливаете self. listData = newList; в конце, newList по-прежнему равен NULL.

Возможно, вы думаете, что если вы установите newListCurr для чего-то, он устанавливает newList - это не так. Они являются независимыми указателями.

0 голосов
/ 01 февраля 2020

В исходном коде функции есть две проблемы. Первый - указатель newList не изменяется внутри функции и всегда равен NULL.

Node<ItemType> * newList = NULL;

Этот нулевой указатель назначен элементу данных listData

listData = newList;

Вторая проблема заключается в том, что вы всегда меняете указатель newListCurr вместо текущего значения указателя newListCurr->next, как, например, в этом операторе if

    if(curr2 == NULL) {
        newListCurr = curr1;
        ^^^^^^^^^^^^^^^^^^^^ 
        curr1 = curr1->next;
        newListCurr = newListCurr->next;
    }

В новой обновленной реализации функции вы избегаете этой ошибки, потому что теперь вы действительно изменяете элемент данных next текущего указателя newListCurr

    if(curr2 == NULL) {
        newListCurr->next = curr1;
        ^^^^^^^^^^^^^^^^^^^^^^^^^
        curr1 = curr1->next;
        newListCurr = newListCurr->next;

Тем не менее, новая реализация функции слишком сложна, поскольку тот же код фактически дублируется в первом операторе if.

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

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

Вот демонстрационная программа.

#include <iostream>

template <class ItemType>
class SortedList
{
private:
    struct Node
    {
        ItemType info;
        Node *next;
    } *listData = nullptr;

public:
    void insert( const ItemType &info )
    {
        Node **tail = &listData;

        while ( *tail ) tail = &( *tail )->next;

        *tail = new Node { info, nullptr };
    }

    void merge( SortedList<ItemType> & list )
    {
        Node *newListData = nullptr;

        Node **current = &newListData;
        Node **first   = &listData;
        Node **second  = &list.listData;

        while ( *first && *second )
        {
            if ( ( *second )->info < ( *first )->info )
            {
                *current = *second;
                *second = ( *second )->next;
                current =  &( *current )->next;
            }
            else
            {
                *current = *first;
                *first = ( *first )->next;
                current =  &( *current )->next;
            }
        }

        while ( *first )
        {
            *current = *first;
            *first = ( *first )->next;
            current =  &( *current )->next;
        }

        while ( *second )
        {
            *current = *second;
            *second = ( *second )->next;
            current =  &( *current )->next;
        }

        listData = newListData;
    }

    friend std::ostream & operator <<( std::ostream &os, const SortedList<ItemType> &list )
    {
        for ( const SortedList<ItemType>::Node *current = list.listData; current; current = current->next )
        {
            os << current->info << " -> ";
        }

        return os << "null";
    }
};

int main() 
{
    SortedList<int> list1;
    SortedList<int> list2;

    const int N = 10;

    for ( int i = 0; i < N; i++ )
    {
        if ( i % 2 == 0 ) list1.insert( i );
        else list2.insert( i );
    }

    std::cout << list1 << '\n';
    std::cout << list2 << '\n';

    list1.merge( list2 );

    std::cout << list1 << '\n';
    std::cout << list2 << '\n';

    return 0;
}

Вывод программы:

0 -> 2 -> 4 -> 6 -> 8 -> null
1 -> 3 -> 5 -> 7 -> 9 -> null
0 -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9 -> null
null
0 голосов
/ 01 февраля 2020

Я предполагал, что в конце список аргументов будет пуст, и два списка будут объединены в объекте, к которому будет вызван метод.

template <class ItemType>
void SortedList<ItemType>::merge(SortedList& list) {

    Node<ItemType>* newData{};
    mergeUtility(listData, list.listData, newData);
    listData = newData;
}



template <class ItemType>
void SortedList<ItemType>::mergeUtility(Node<ItemType>* & currentPtr1, Node<ItemType>* & currentPtr2, Node<ItemType>* & currentPtrNewList) {
    if( currentPtr1 && currentPtr2){
        if( currentPtr1 -> info < currentPtr2 -> info ){
            currentPtrNewList = currentPtr1;
            mergeUtility( currentPtr1 -> next , currentPtr2, currentPtrNewList -> next );

        }
        else{
            currentPtrNewList =  currentPtr2 ;
            mergeUtility( currentPtr1 , currentPtr2 -> next, currentPtrNewList -> next );

        }
    }
    elseif(currentPtr1){
        currentPtrNewList = currentPtr1;
        mergeUtility( currentPtr1 -> next , currentPtr2, currentPtrNewList -> next );
    }
    elseif(currentPtr2){
        currentPtrNewList = currentPtr2;
        mergeUtility( currentPtr1, currentPtr2 -> next, currentPtrNewList -> next );
    }

}
...