【问题标题】:Adding value to singly-linked list为单链表增加价值
【发布时间】:2014-01-31 22:34:56
【问题描述】:

我正在学习 C++ 数据结构课程,最近几天的家庭作业一直困扰着我。它必须处理单链表,我们必须编写的方法之一就是向列表中添加值。

首先,我们创建虚拟节点head

template<typename T>
SLList<T>::SLList()
{
  head = new Node; // Node() is ok too
}

如果我是正确的,这不应该是一个值,不是吗?所以头部应该是NULL。到目前为止,这是我(在我教授的帮助下)成功创建的。

template<typename T>
void SLList<T>::add(const T& val) {
    bool duplicate = false;
    //Node *ptr = head->next;
    //cout << head->data;

    for (Node *ptr = head->next; ptr != NULL; ptr = ptr->next) {
    //while (ptr == NULL) {
        //ptr = ptr->next;
        // if ptr is initialized as head, the first node, then ptr != NULL
        // if ptr is initialized as head->next, which it should in order to
        // traverse from the node AFTER the head, then ptr == NULL
        // not exactly sure why.
        if (ptr->data == val) {
            duplicate = true;
            break;
        }
    }
    //Node *temp = new Node(val); // create new node with new element.
    //temp->next = ptr->next;
    //ptr->next = temp;

    if (duplicate) {
        cout << "Duplicate entry found: " << val << endl;
    }
}

我尝试过使用forwhile 循环,因为很多谷歌结果在他们的答案中使用了while 循环。我想要做的是尝试向列表中添加一个值。如果该值已经在该列表中,则不会添加它。我正在尝试这样做:

SLList<int> iList;
iList.add(5);
iList.add(6);
iList.add(6);

当我寻求帮助时,有人告诉我应该将 *ptr 初始化为 head-&gt;next 以开始遍历虚拟节点之后的节点列表。我使用了 Visual Studio 的调试器,发现在将其初始化为 head-&gt;next 后,程序从未进入 for 循环,我假设这是因为虚拟节点之后的节点未能通过 if 语句 ptr != NULL。我不确定为什么会这样,因为当我有 Node *ptr = head 时,它会很好地遍历列表(尽管下面的行 if (ptr-&gt;data == val) 会认为每个条目都是重复条目)。

我可能理解错了,但 head 节点的值不应该是 NULL 吗?所以它不应该在for循环中传递if语句。

for 循环之后的三行似乎也有问题,因为当程序到达那里时,它只是崩溃并且无法将任何值添加到列表中,但我想我会采取一步一个脚印。

这里可能有很多我误解了,所以任何帮助都将不胜感激。

【问题讨论】:

  • head 当然不应该为 NULL。 new 运算符不返回空指针。只是它的数据值不会有好的数据,因为它只是一个虚拟节点。
  • @ooga 哦,我明白了。所以有数据,只是没有好的数据?我只是想 NULL 将与 0 相同。
  • head 本身不能为 NULL(与 0 相同)。它必须指向一个有效的Node 对象,因此head 将有一个有效的(非空)地址。但是它指向的Node 中的数据是任意的,因为它只是一个“虚拟节点”,正如你所说的。

标签: c++ linked-list


【解决方案1】:

试试这样的:

template<typename T>
void SLList<T>::add(const T& val) {
    Node<T>* prev = head;
    for (Node<T>* ptr = head->next; ptr; ptr = ptr->next) {
        if (ptr->data == val) {
            std::cout << "Duplicate: " << val << '\n';
            return;
        }
        prev = ptr;
    }
    prev->next = new Node<T>(val);
}

【讨论】:

  • 我将此与 Elayeek 上面的答案结合起来,所以我猜我们正在检查 foo 循环中是否有 ptr 值。如果是这样,我们运行重复检查,然后prev 变成任何ptrhead = ptr 也是如此?
【解决方案2】:

您的主要设计缺陷似乎是这样的: 您将到达列表的末尾并从那里取出它,但末尾为空,因此您需要回溯一次以找到不为空的内容。 例如(基本示例)

Node<T>* last = head;
for(Node<T>* ptr = head->next; ptr != NULL ; ptr = ptr->next){
    last = ptr;
    //do something
}

所以当循环结束时,您知道如果ptr==NULL 那么last 是您列表的尾部,并且您想要添加的任何内容都必须跟随它。

这也是for循环后三行代码的问题: 如果我对您的理解正确,在没有重复的情况下,您将迭代直到ptrNULL。所以退出for时,ptr-&gt;next会失败。 这意味着在添加元素时,如果该元素不在列表中 - 您的功能会失败(设计使然,而不是意外)。

【讨论】:

  • 所以不是让head 只是浮动,而是将它分配给last 并通过循环,对吗?然后如果ptr != NULL 它将进入for 循环并将ptr 分配给last 并不断更新直到ptr == NULL?那么如果是这样(结合下面ooga的答案)假设没有重复,prev-&gt;next(我相信这真的会是ptr-&gt;next)将创建一个具有该值的新节点?我有可能让这个复杂化了。
  • 嗯,ooga 的回答其实只是一个完整的解决方案。我只给了你部分解决方案,因为我在解释这个概念。但它是 prev->next(在我的解决方案中,last->next)因为 ptr 总是在 last 之前(ooga 的解决方案中的 prev)所以如果 ptr 是列表末尾之后的一个,last(或 prev)是最后一个节点。如果节点只是头部,它将跳过for循环并添加一个新节点(在ooga的解决方案中,我的评论主要是解释性的)
【解决方案3】:

头部

当列表为空时,head 必须为 NULL。这发生在创建或添加和删除相等数量的节点之后。在所有其他情况下,head 必须为非 NULL,并且必须指向第一个添加的节点。

添加(典型)

Node * ptr = new Node;
ptr->next = head;
head = ptr;

添加(仅不重复)

bool const exists = std::find( list, element );
if( ! exists ) {
    Node * ptr = new Node;
    ptr->next = head;
    head = ptr;
}

【讨论】:

  • 你显然没有读过这个问题,因为它表明 head 是一个虚拟节点,当然不能为空。
  • 我很难真正理解代码的作用。你能解释一下吗?此外,关于检查列表是否为空,if (iList == NULL) { add head to iList) 之类的东西会起作用吗?
【解决方案4】:

这里有几个选项。最常见的是您将head 指针设置为nullptr,直到您添加一个节点。在这种情况下,您根本就没有(或不需要)一个虚拟节点。

另一种可能性是创建最初包含一个虚拟节点的列表。这可以在一定程度上简化其余代码,因为它避免了将第一个节点添加到空列表的任何特殊情况代码。您还可以将虚拟节点用作“哨兵”,其中包含一些在列表的其余部分中不允许的数据项。在适当的情况下,这可以通过允许您只查看包含的数据而不是检查指示列表结束的空指针来简化列表遍历。一个典型的例子是您按排序顺序保存的浮点数列表,其中虚拟节点包含infinity。在这种情况下,查找数字可以简化为:

while (ptr != nullptr && ptr->data < search_item) {
    // ...
    ptr=ptr->next;
}

...只是:

while (ptr->data < search_item) {
    // ...
    ptr = ptr->next;
}

列表末尾的infinity 确保如果列表中没有其他内容大于search_item,则虚拟节点将是(尽管这显然要求您确保不要使用@ 将项目插入列表中987654327@ 作为它的值)。

但是,这与您在问题中建议的用法不同-它要求虚拟节点始终保留为列表中的 last 项,因此您永远不要在其后插入任何内容。然而,至少根据我的经验,这比总是位于列表开头的虚拟节点更常见。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-10-18
    • 2017-09-16
    • 2014-06-30
    • 2022-08-03
    • 1970-01-01
    • 1970-01-01
    • 2018-02-10
    • 2013-10-31
    相关资源
    最近更新 更多