在C ++中将结构元素地址设置为null(单链表)

时间:2017-12-12 00:33:46

标签: c++ pointers struct singly-linked-list nullptr

我有这个结构:

struct Node {
    int num;
    Node *next;

    Node(int, Node*);
};

在班级Collection内。

当我尝试删除列表的最后一个元素时,使用此函数:

void Collection::remove(int num){
   Node *target = find(num);
   if (target == nullptr) return;
   n--;
   Node *temp = target ->next;
   if (temp == nullptr) {
       delete target;  
       target = nullptr;       // This is where the problem occurs
       return;
   } 
   target->num = temp->num;
   target->next = temp->next;
   delete temp;
}

之前的节点*next仍然指向现在为空的地址位置,如何将target内存位置设置为null

有关详细信息,请参阅我的函数find()

Collection::Node* Collection::find(int num) {
    Node *temp = head;
    while (temp != nullptr && temp->num != num) temp = temp->next;
    return temp;
}

3 个答案:

答案 0 :(得分:2)

您实际上在正常删除逻辑上使用了轻微的变体,这在大多数情况下有效。这种变化是,而不是删除当前元素,您将数据从以下元素移动到其中,然后删除该后续元素。这意味着您永远不必备份前一个元素,这是一个很难的事情(没有存储额外的信息或再次通过列表运行)与单链表。

不幸的是,赢得工作的一种情况是你在删除列表中的最后一个元素时。在这种情况下,没有以下元素可以移动数据,因此您需要删除当前元素并调整前一个元素以成为列表的新结尾。

而且,既然你需要这样做,你也可以恢复所有情况下的正常逻辑: - )

使用该逻辑,您基本上需要在要删除的元素之前访问元素。然后将其next指针设置为绕过您要删除的指针。

您还需要处理删除列表中 first 元素的特殊情况。假设你有一个head成员指向第一个元素而curr/prev分别指向你要删除的节点及其前身,伪代码将是:

if curr is head:
    set head to curr.next
else
    set prev.next = curr.next
free curr

在不涉及太多更改的情况下轻松完成此操作的一种方法是修改find方法,以便使用它来获取上一个节点:

Node* Collection::find(
    int num,
    Node **pPrev = nullptr
) {
    // Initially store null as previous item.

    if (pPrev != nullptr) *pPrev = nullptr;

    // Cycle through list until found or not there.

    Node *temp = head;
    while ((temp != nullptr) && (temp->num != num)) {
        // Update previous before advancing.

        if (pPrev != nullptr) *pPrev = temp;
        temp = temp->next;
    }
    return temp;
}

请注意,我在代码而不是Node中使用了Collection::Node,因为您的问题有两种变体,我不确定您是否在<? em> namespace 名为Collection。根据您的实际使用情况,根据需要进行调整。

在任何情况下,将第二个参数默认为nullptr将允许您完全按照当前的方式调用它(使用一个参数,它不会尝试存储前一个节点指针)。 / p>

但是,如果你想要那个前一个指针,你可以使用这样的东西来删除:

Node *prev;
Node *curr = find(num, &prev);    // Get previous as well.
if (curr != nullptr) {            // Only delete if actually found.
    if (prev == nullptr)          // Null means current is head.
        head = curr->next;
    else                          // Otherwise adjust previous.
        prev->next = curr->next;
    delete curr;                  // Regardless of head-ness, delete.
}

答案 1 :(得分:1)

对我来说,这一切看起来都像是旧式的C:)

要实际更改Collection :: remove函数之外的内存,您应该修改find​​函数以实际返回指针指针:

Node** find(int num) {
     Node **temp = &head;

     while (*temp != NULL && (*temp)->num != num)
     {
         temp = &((*temp)->next);
     }
     return temp;

}

指针指针可以改变原始记忆:

int main(int argc, char *argv[])
{
   head = new Node(1, new Node(2, new Node(3, NULL)));

   Node **elem = find(2);


   if (*elem)
    {
        delete *elem;
        *elem = NULL;

        cout << "elem 2 deleted" << endl;
    }


   elem = find(2);
   if (*elem)
   {
      delete *elem;
      *elem = NULL;

       cout << "elem 2 deleted" << endl;
   }



   cout << "Hello World!" << endl;
   return 0;
}

您的Collection :: remove将如下所示:

void Collection::remove(int num){
   Node **target = find(num);
   if (*target == nullptr) return;
   n--;
   Node *temp = (*target)->next;
   if (temp == nullptr) {
       delete *target;
       *target = nullptr;
       //cout << "elem " << num << " deleted " << endl;
       return;
   }
   (*target)->num = temp->num;
   (*target)->next = temp->next;
   delete temp;
}

但似乎你的删除功能不会像它想象的那样工作。如果一个将等于num参数,它将仅删除列表的最后一个元素。 为了使其正常工作,您可能需要使此列表向前和向后迭代,以使前一个元素指向下一个元素。或者你可以改变find函数来返回前一个元素。

答案 2 :(得分:0)

将指针设置为nullptr并不一定会改变其值。如果您取消引用nullptr,则表明您正在煽动未定义的行为(see here)。在这种情况下,未定义的行为意味着您仍然可以引用该内存并插入它。但下次它可能会使你的程序崩溃。你在这里做的很糟糕:

if (temp == nullptr) {
       delete target;  
       target = nullptr;       // You delete target
} 
target->num = temp->num;       // Then you immediately dereference it.

您需要在删除target之前设置下一个

target->next = temp->next;
delete temp;