根据给定的子字符串删除多个字符串

时间:2020-03-05 09:25:38

标签: c data-structures linked-list

我正在尝试创建一个包含名称(例如 Andy,Barry,Matilda )的代码,并且当我输入某个子字符串(例如,子字符串为 y ,因此将删除 Andy and Barry 。剩下的唯一一个是 Matilda )。谁能给我帮助?

我的代码是:

void del(char key[]) 
{
    if(head == NULL)
    {
        printf("There's no data\n");
    }
    else{
        curr = head;
        while(curr != NULL && strcmp(curr->name, key) != 0)
        {
            curr = curr->next;
        }
        if(curr == NULL)
        {
            printf("Node is not in the list\n");
        }
        if(curr == head & curr == tail)
        {
            free(curr);
            head = tail = NULL;
        }
        else if(curr == head)
        {
            head = head->next;
            free(curr);
            head->prev = NULL;
        }
        else if(curr == tail)
        {
            tail = tail->prev;
            free(curr);
            tail->next = NULL;
        }
        else
        {
            curr->prev->next = curr->next;
            curr->next->prev = curr->prev;
            free(curr);
        }
    }
}

2 个答案:

答案 0 :(得分:0)

您的代码正确删除了一个列表节点,除非找不到key-在这种情况下,缺少return之后的printf("Node is not in the list\n");

要删除多个节点,我们必须遍历列表,如果找到并删除了匹配项,则也要继续。要仅在未发现任何消息的情况下打印消息Node is not in the list,我们可以使用在循环后进行测试的标志。因此,您可以替换外部else块e的内容。 G。与

      void *next;
      int deleted = 0;
      for (curr = head; curr; curr = next)
      { next = curr->next;
       if (strstr(curr->name, key))
       { deleted = 1;
        // the rest of this block is your code unchanged
        if(curr == head & curr == tail)
        {
            free(curr);
            head = tail = NULL;
        }
        else if(curr == head)
        {
            head = head->next;
            free(curr);
            head->prev = NULL;
        }
        else if(curr == tail)
        {
            tail = tail->prev;
            free(curr);
            tail->next = NULL;
        }
        else
        {
            curr->prev->next = curr->next;
            curr->next->prev = curr->prev;
            free(curr);
        }
       }
      }
      if (!deleted) printf("Node is not in the list\n");

答案 1 :(得分:0)

您没有提供很多信息来进行下去,但是我认为这对社区来说是新的。从您提供的内容来看,很明显,您至少有一个双向链接列表,其中有一个字符串作为数据成员。同样清楚的是,您已经将headtail指针声明为 global 变量(这不是一个好习惯,因为您应该将函数所需的任何信息作为参数,但对于本学习练习,它提供的简化程度最小)

根据您的描述,希望您的del函数执行此操作,您想遍历链接列表以测试name成员是否包含子字符串key,如果是,则您想要从列表中删除该节点。

您有两个主要问题:

  1. 您使用了错误的字符串函数来检查name成员中的子字符串。 strcmp()只会找到完整的name与您的key匹配的节点。相反,您希望strstr()key内的任何地方都可以匹配name
  2. 通过尝试仅对当前节点使用 pointer 遍历列表,而不是同时使用 pointer 和<<当前节点的em> address 。参见Linus on Understanding Pointers

要纠正您的拳头问题,只需将strcmp()中与strstr()匹配的key更改为name,例如

        ...
        if (strstr(curr->name, key) != NULL) {  /* strstr, not strcmp, to find key */
            ...

要解决第二个问题,您需要重新处理del函数。为此,您必须了解在删除多个节点时进行迭代与找到要删除的单节点进行迭代有何不同。循环查找要删除的单节点时,只需在每次迭代中前进到列表中的下一个节点,直到找到该节点,然后删除该节点即可。

当可能从列表中删除多个节点时,您将无法执行此操作。为什么?当您删除具有匹配子字符串的节点时,列表中的下一个节点将取代它。删除后,您不能仅前进到下一个节点,因为在删除第一个节点之后,现在是当前节点的节点也可能包含您要查找的子字符串。如果您只是盲目地前进到下一个节点,并且删除后的当前节点还包含子字符串,则将跳过该节点的删除。

从代码的角度来看,这意味着您需要在else下添加if子句,例如

        if (strstr(curr->name, key) != NULL) {  /* strstr, not strcmp, to find key */
            ...
        else
            ...

在您的if子句下,删除后下一个节点将替换当前节点,因此您不会再前进到列表中的下一个节点。这样,将在下一次迭代时检查新的当前节点是否有匹配的子字符串。如果else不匹配,则只能前进到key子句下的下一个节点。

当同时使用 pointer 和当前节点的地址遍历当前节点时,不必处理特殊情况。您将始终将当前地址的内容设置为列表中的下一个节点。 ({head不会更改,因为您已将该地址的结构设置为删除时的下一个)。唯一需要检查的是检查是否要删除tail节点。在这种情况下,您将需要更新tail节点以指向上一个节点,因为您将删除tail当前指向的节点。否则,您需要做的就是更新移至当前地址的节点的->prev指针,以指向列表中的上一个节点。

请记住,您的del函数将减少为:

void del (char key[])
{
    if (head == NULL) {                         /* check empty */
        puts ("list-empty");
        return;
    }
    node_t  **ppnode = &head,                   /* address of current node */
            *curr = head;                       /* pointer to current node */

    while (curr) {
        if (strstr(curr->name, key) != NULL) {  /* strstr, not strcmp, to find key */
            *ppnode = curr->next;               /* fill address w/next node */
            if (curr != tail)                   /* if not tail */
                (*ppnode)->prev = curr->prev;   /* set ->prev to prev node */
            else    /* otherwise */
                tail = curr->prev;              /* update tail pointer */
            free (curr);                        /* free node */
            curr = *ppnode;                     /* set new current node */
        }
        else {  /* node to keep */
            ppnode = &curr->next;               /* set address to addres of next */
            curr = curr->next;                  /* advance to next node */
        }
    }
}

在不了解您的代码的情况下,我们所能做的就是编写一个简短的示例,将您的字符串作为列表中的节点添加(使用固定的name来简化内容)。像这样的简短示例:

int main (void) {

    add ("Andy");           /* add nodes to list */
    add ("Barry");
    add ("Matilda");

    prnfwd();               /* print forward and reverse */
    prnrev();
    putchar ('\n');

    del ("y");              /* delete nodes containing substring "y" */

    prnfwd();               /* print forward and reverse */
    prnrev();

    del_list();             /* free allocated memory */
}

添加节点,在两个方向上遍历列表,调用del ("y");删除字符串包含子字符串"y"的所有节点(请注意,它与字符'y'不同) ),然后再次双向遍历列表,在释放列表中的所有内存之前输出剩​​余的内容。

使用/输出示例

给定示例字符串的结果将是:

$ ./bin/lldglobaldelkey
 Andy Barry Matilda
 Matilda Barry Andy

 Matilda
 Matilda

该示例的完整实现是:

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

#define MAXNM 64

typedef struct node_t {
    char name[MAXNM];
    struct node_t *prev, *next;
} node_t;

node_t *head, *tail;

/** add node at end of list, update tail to end */
node_t *add (const char *s)
{
    node_t *node = malloc (sizeof *node);   /* allocate node */
    if (!node)                              /* validate allocation */
        return NULL;

    strcpy (node->name, s);                 /* initialize new node */
    node->prev = node->next = NULL;

    if (!head)                              /* if 1st node, node is head/tail */
        head = tail = node;
    else {                                  /* otherwise */
        node->prev = tail;                  /* set prev to tail */
        tail->next = node;                  /* add at end, update tail pointer */
        tail = node;
    }

    return node;    /* return new node */
}

/* print list forward */
void prnfwd (void)
{
    if (!head) {                            /* check empty */
        puts ("list-empty");
        return;
    }

    for (node_t *n = head; n; n = n->next)  /* iterate over nodes - forward */
        printf (" %s", n->name);
    putchar ('\n');
}

/* print list reverse */
void prnrev (void)
{
    if (!head) {                            /* check empty */
        puts ("list-empty");
        return;
    }

    for (node_t *n = tail; n; n = n->prev)  /* iterate over nodes - reverse */
        printf (" %s", n->name);
    putchar ('\n');
}

/** delete all nodes in list */
void del_list (void)
{
    node_t *n = head;

    if (!head) {                            /* check empty */
        puts ("list-empty");
        return;
    }

    while (n) {                             /* iterate over nodes - forward */
        node_t *victim = n;                 /* save ptr to node to delete */
        n = n->next;                        /* advance to next */
        free (victim);                      /* delete node */
    }

    head = tail = NULL;                     /* set pointers NULL */
}

void del (char key[])
{
    if (head == NULL) {                         /* check empty */
        puts ("list-empty");
        return;
    }
    node_t  **ppnode = &head,                   /* address of current node */
            *curr = head;                       /* pointer to current node */

    while (curr) {
        if (strstr(curr->name, key) != NULL) {  /* strstr, not strcmp, to find key */
            *ppnode = curr->next;               /* fill address w/next node */
            if (curr != tail)                   /* if not tail */
                (*ppnode)->prev = curr->prev;   /* set ->prev to prev node */
            else    /* otherwise */
                tail = curr->prev;              /* update tail pointer */
            free (curr);                        /* free node */
            curr = *ppnode;                     /* set new current node */
        }
        else {  /* node to keep */
            ppnode = &curr->next;               /* set address to addres of next */
            curr = curr->next;                  /* advance to next node */
        }
    }
}

int main (void) {

    add ("Andy");           /* add nodes to list */
    add ("Barry");
    add ("Matilda");

    prnfwd();               /* print forward and reverse */
    prnrev();
    putchar ('\n');

    del ("y");              /* delete nodes containing substring "y" */

    prnfwd();               /* print forward and reverse */
    prnrev();

    del_list();             /* free allocated memory */
}

内存使用/错误检查

在您编写的任何动态分配内存的代码中,对于任何分配的内存块,您都有2个职责:(1)始终保留指向起始地址的指针因此,(2)不再需要它时可以释放

当务之急是使用一个内存错误检查程序来确保您不尝试访问内存或不在分配的块的边界之外/之外写,尝试读取或基于未初始化的值进行条件跳转,最后,以确认您释放了已分配的所有内存。

对于Linux,valgrind是正常选择。每个平台都有类似的内存检查器。它们都很容易使用,只需通过它运行程序即可。

$ valgrind ./bin/lldglobaldelkey
==10704== Memcheck, a memory error detector
==10704== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==10704== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==10704== Command: ./bin/lldglobaldelkey
==10704==
 Andy Barry Matilda
 Matilda Barry Andy

 Matilda
 Matilda
==10704==
==10704== HEAP SUMMARY:
==10704==     in use at exit: 0 bytes in 0 blocks
==10704==   total heap usage: 4 allocs, 4 frees, 1,264 bytes allocated
==10704==
==10704== All heap blocks were freed -- no leaks are possible
==10704==
==10704== For counts of detected and suppressed errors, rerun with: -v
==10704== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

始终确认已释放已分配的所有内存,并且没有内存错误。

我希望这与您的实现接近。仔细研究一下,如果您还有其他问题,请告诉我。