【问题标题】:Why does this implementation of merge on a sorted linked list always set both lists to NULL when only one should?为什么排序链表上的这种合并实现总是将两个列表都设置为NULL,而只有一个应该设置为NULL?
【发布时间】:2020-01-31 21:30:04
【问题描述】:

老实说,我已经在这工作了大约 10 个小时,尝试了一种又一种方法来让它发挥作用。我正在尝试创建一个第三个列表,它将 2 个列表合并在一起并从低到高排序(没有重复),然后将第一个列表设置为新列表,从而将第二个列表合并到第一个列表中。但是每当我运行程序时,我都会得到 listData == NULL,即使在 newListCurr 绝对应该向 newList 添加元素的测试用例中也是如此。我一直对链表有困难,所以也许我误解了一些基本原理,但我一生都无法弄清楚这一点。该方法不需要声明单个新节点,并且只能具有 O(n) 的时间复杂度,这使得这变得更加困难。我尝试了几种方法,例如尝试将它们直接插入 listData(第一个列表),但存在一个一致的问题,即 curr 指针实际上并未影响它们各自的 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;
}

编辑:我为可能遇到同样问题的人找到了解决方案。在使用 newListCurr 修改 newList 的其余部分之前,我需要将 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;
}

【问题讨论】:

  • 我看到Node&lt;ItemType&gt; * newList = NULL;,没有任何改变,然后是listData = newList;。为什么你认为newList 不是NULL
  • 通常想回答问题的人会从问题中获取代码,将其粘贴到文本文件中,编译并运行它以查看自己的错误所在。他们通常会使用各种尺寸、形状和用途的调试工具。让这件事尽可能简单符合提问者的最大利益。独立代码的片段使这变得困难。没有要运行的程序,并且在制作程序的过程中可能会意外修复错误或插入新错误。代码中的行号使这很难。在编译代码之前需要删除它们。
  • 要遍历您的列表,您需要一个指向指针的指针,而不仅仅是一个指针。或者创建一个递归函数并使用对指针的引用。另一件事,你没有提到你的旧两个列表是否已订购。
  • @Kevin 是的,但我将 newListCurr 设置为 newList,然后使用 newListCurr 添加节点。 newList 只是作为对第三个列表头部的引用而存在。除非我做错了。您如何建议添加到 newList 同时保持对其第一个节点的引用?
  • 几乎所有问题都可以归结为main 和一些支持功能。强烈建议在此处提出问题之前隔离错误,因为通常情况下,隔离错误可以让您在不提出问题的情况下识别和修复它。创建一个main,它建立两个排序列表,尝试合并它们,并产生错误的结果。如果这样做没有向您揭示解决方案,请询问有关此简单程序的问题。使用minimal reproducible example 获取灵感。

标签: c++ merge linked-list singly-linked-list


【解决方案1】:

newList 在顶部设置为 NULL,并且永远不会重新分配。当你在最后设置self. listData = newList; 时,newList 仍然是NULL。

也许你认为如果你将newListCurr 设置为某个东西,它就会设置newList——它没有。它们是独立的指针。

【讨论】:

  • 好吧,这似乎是我的缺点。如何保留指向 newList 中第一个节点的指针并将节点添加到末尾?
  • 我会在循环中检查 newList 是否为 NULL,如果不是,则在循环结束时将其设置为 newListCurr。我没有运行/测试这个想法——只是一个猜测。您需要设置一次,以便在此基础上进行构建。
  • 我让它工作了!非常感谢,采纳了您的想法并使其与我的代码一起使用,但是在我可以构建它之前设置一次的概念是我所缺少的。我之前只运行一次逻辑以获取第一个节点,然后更改循环以修改 newListCurr->next 而不是 newListCurr 本身。我会用工作代码更新帖子!
【解决方案2】:

原来的函数代码有两个问题。第一个是指针newList 在函数内没有被改变并且总是等于NULL。

Node<ItemType> * newList = NULL;

这个空指针被分配给数据成员listData

listData = newList;

第二个问题是你总是在改变指针 newListCurr 而不是指针 newListCurr-&gt;next 的当前值,例如在这个 if 语句中

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

在新的更新函数实现中,您避免了这个错误,因为现在您确实在更改当前指针 newListCurr 的数据成员 next

    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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-03-02
    • 1970-01-01
    • 2019-09-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多