为单连锁列表增加价值

Adding value to singly-linked list

本文关键字:增加 列表 单连锁      更新时间:2023-10-16

我在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循环,因为许多Google结果都使用了while循环。我要做的是尝试在列表中添加一个值。如果该值已经在该列表中,则不会添加。我正在尝试以下方式:

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

当我寻求帮助时,我被告知我应该将*ptr初始化为head->next,以开始在虚拟节点后开始遍历节点的列表。我使用了Visual Studio的调试器,发现将其初始化为head->next后,该程序从未进入for循环,我认为这是因为虚拟节点后的节点失败了IF语句ptr != NULL。我不确定为什么这样做,就像我有Node *ptr = head时一样,它会遍历列表,尽管以下行if (ptr->data == val)将每个条目都视为重复条目)。

我可能理解这是错误的,但是head节点不应该具有NULL的值吗?因此,它不应通过for循环中的if语句。

for循环之后的三行似乎也有问题,因为当程序到达那里时,它只是崩溃了,未能在列表中添加任何值一次踩踏。

我在这里可能有很多误解,因此任何帮助都将不胜感激。

尝试这样的东西:

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);
}

您的主要设计缺陷似乎就是这样:您将进入列表的尽头并从那里拿走,但结束是无效的,因此您需要回溯一次以找到并非零的东西。例如(基本示例)

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->next将失败。这意味着,当添加元素时,如果该元素不在列表中 - 您的函数失败(设计而不是偶然)。

head

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

add(典型)

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指针设置为NULLPTR,直到添加节点为止。在这种情况下,您根本没有(或需要)虚拟节点。

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

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

...仅:

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

列表末尾的infinity可确保如果列表中的其他任何内容都不大于search_item,则虚拟节点将是(尽管这显然需要您确保以infinity作为其值将项目插入列表中)。

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