1

我正在实现一个链表,它需要一个函数,当给定链表的头部和一个 cstring 时,它会找到并删除一个值为 cstring 的节点。

typedef struct node
{
  char entry[21];
  struct node* next;
} node;


/*returns true if node with phrase value found, otherwise false*/
bool findAndRemove(node* root, char phrase[21])
{
    if(root != NULL)
    {
        node* previous = NULL;
        while(root->next != NULL)
        {
            if(strcmp(root->entry, phrase) == 0)//found
            {
                if(previous == NULL)//node to delete is at head
                {
                    node* tmp = root;
                    root = root->next;
                    free(tmp);
                    return true;
                }
                previous->next = root->next;
                free(root);
                return true;
            }
            previous = root;
            root = root->next;
        }
        return false;
    }
}

它工作正常,但是在删除头部时会打印出一些垃圾。发生了什么,我该如何解决这个问题?我有任何内存泄漏吗?出于好奇,“根”或“头”这个词更常用于链表中的第一个节点?

4

3 回答 3

3

首先要意识到的是,从链表中删除一个元素需要更改一个指针值:指向us的指针。这可以是head指向第一个列表元素的外部指针,也可以是列表->next内的一个指针。在这两种情况下,指针都需要更改;它的新值应该成为->next要删除的节点的指针的值。

为了改变某个对象(从函数内部),我们需要一个指向它的指针。我们需要改变一个指针,所以我们需要一个指向指针的指针

bool findAndRemove1(node **ptp, char *phrase)
{
    node *del;

    for( ;*ptp; ptp = &(*ptp)->next) {
        if( !strcmp((*ptp)->entry, phrase) ) { break; } //found
        }

      /* when we get here, ptp either
      ** 1) points to the pointer that points at the node we want to delete
      ** 2) or it points to the NULL pointer at the end of the list
      **    (in the case nothing was found)
      */
    if ( !*ptp) return false; // not found

    del = *ptp;
    *ptp = (*ptp)->next;
    free(del);
    return true;
}

条件的数量if甚至可以通过在循环中做脏活来减少到一个,然后从循环中返回,但这有点小技巧:

bool findAndRemove2(node **ptp, char *phrase)
{

    for( ;*ptp; ptp = &(*ptp)->next) {
        node *del;
        if( strcmp((*ptp)->entry, phrase) ) continue; // not the one we want

          /* when we get here, ptp MUST
          ** 1) point to the pointer that points at the node we want to delete
          */
        del = *ptp;
        *ptp = (*ptp)->next;
        free(del);
        return true;
        }
    return false; // not found
}

但是如果列表不是唯一的,我们想删除所有满足条件的节点呢?我们只是稍微改变一下循环逻辑并添加一个计数器:

unsigned searchAndDestroy(node **ptp, char *phrase)
{
    unsigned cnt;

    for( cnt=0 ;*ptp; ) {
        node *del;
        if( strcmp((*ptp)->entry, phrase) ) { // not the one we want
             ptp = &(*ptp)->next;
             continue; 
             }
          /* when we get here, ptp MUST point to the pointer that points at the node we wish to delete
          */
        del = *ptp;
        *ptp = (*ptp)->next;
        free(del);
        cnt++;
        }
    return cnt; // the number of deleted nodes
}

更新:和一个驱动程序来测试它:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>

typedef struct  list {
        struct list *next;
        char entry[20];
        } node;

void node_add( node **ptp, char *str)
{
node *new;

for (   ; *ptp; ptp = &(*ptp)->next) {
        if (strcmp ((*ptp)->entry, str) < 0) continue;
        }
new = malloc (sizeof *new);
strcpy(new->entry, str);
new->next = *ptp;
*ptp = new;
}

int main (void)
{
node *root = NULL;
unsigned cnt;

node_add (& root, "aaa" );
node_add (& root, "aaa" );
node_add (& root, "bbb" );
node_add (& root, "ccc" );
node_add (& root, "aaa" );
cnt = seachAndDestroy( &root, "bbb" );
printf("Cnt(bbb) := %u\n", cnt );
cnt = seachAndDestroy( &root, "ccc" );
printf("Cnt(ccc) := %u\n", cnt );
cnt = seachAndDestroy( &root, "aaa" );
printf("Cnt(aaa) := %u\n", cnt );
printf("Root now = %p\n", (void*) root );

return 0;
}

和输出:

plasser@pisbak:~/usenet$ ./a.out
Cnt(bbb) := 1
Cnt(ccc) := 1
Cnt(aaa) := 3
Root now = (nil)
于 2013-09-29T10:48:45.563 回答
1

您正在更改函数内部的根,因此您需要传递一个双指针:

bool findAndRemove(node** root, char phrase[21])
{
    node* iterate = *root;
    if(root != NULL && *root != NULL)
    {
        node* previous = NULL;
        while(iterate->next != NULL)
        {
            if(strcmp(iterate->entry, phrase) == 0)//found
            {
                if(previous == NULL)//node to delete is at head
                {
                    node* tmp = iterate;
                    *root = iterate->next;
                    free(tmp);
                    return true;
                }
                previous->next = iterate->next;
                free(iterate);
                return true;
            }
            previous = iterate;
            iterate = iterate->next;
        }
        return false;
    }
}
于 2013-09-29T10:39:16.010 回答
0

您通过指向第一个节点来构造一个列表。

然后删除第一个节点,但不要更新指向列表的指针以指向第二个节点

只需让您的函数检查您是否正在删除第一个节点,并始终返回指向最终列表的第一个指针的指针。或者,不是node *root参数,而是传递,node **root以便您可以修改函数中的引用(尽管我不喜欢这种工作方式)。

于 2013-09-29T10:38:00.857 回答