如何删除C中链表中的某个元素

时间:2014-12-30 07:31:16

标签: c

我正在尝试删除链接列表中的某个元素。

当我在屏幕上打印所有元素时,它们都有一定的顺序(参见案例2)。在案例7中,我可以根据该订单选择要删除的元素。

案例7中的代码不起作用。这是我的代码:

#include "stdio.h"
#include "ctype.h"
#include "stdlib.h"
#include "math.h"
#include "string.h"
#define SIZE 100
double dummy = sin(0.0);

struct sputnik {
  char nazvanie[30];
  char nazvanie_main[30];

  int year;
  float d;
  int period;
  struct sputnik *next;
};

int main(void) {
  char choice;
  int punkt;
  int i, count = 0;
  struct sputnik *head = NULL;
  struct sputnik *prev, *current;
  int res, kolvo, j, number;
  struct sputnik a[SIZE];
  system("clear");

  while (1) {
    printf("Menu: ");
    printf("1- Create table with sputniks  \n 2-All sputniks \n 3-Write      4-Read \n 5-Add \n 6-Change \n 7-Delete \n 8-Exit \n");
    scanf("%d", &punkt);

    while (getchar()!='\n') 
      continue;

    switch(punkt) {
      case 1:
        while (1) {
          printf("Create new table? (N-new; O-old)");
          choice = toupper(getchar());
          if (choice == 'N') {
            count = 0;
            prev = head;
            while (prev != NULL) {
              current = prev->next;
              free(prev);
              prev = current;
            }
            head = NULL;
          }
          if (choice != 'N' && choice != 'O') {
            while (getchar() != '\n')
              continue;
            continue;
          }
          while (getchar()!='\n')
            continue;
          break;
        }
        for ( ; ; count++) {
          current = (struct sputnik *)malloc(sizeof(struct sputnik));
          if (head == NULL) {
            head = current;
          } else {
            prev->next = current;
          }
          current->next = NULL;
          printf("Name %d sputnika:", count + 1);
          gets(current->nazvanie);
          printf("Name planet:");
          gets(current->nazvanie_main);
          printf("Open year:");
          scanf("%d", &current->year);
          while (getchar() != '\n')
            continue;
          printf("Diameter:");
          scanf("%f", &current->d);
          while (getchar() != '\n')
            continue;
          printf("Period:");
          scanf("%d", &current->period);
          while (getchar() != '\n')
            continue;
          prev = current;
          printf("Finish vvod?: y/n: \n");
          if (toupper(getchar()) == 'Y') {
            count++;
            break;
          } else {
            while (getchar() != '\n')
              continue;
            continue;
          };
        }
        break;
      case 2:
        if (head == NULL) {
          printf ("No data \n");
        } else {
          printf(" Sputniks: \n");
        }
        current = head;
        i = 0;
        while (current != NULL) {
          printf("%d sputnik - %s planet %s god %d diametr %4.3f period %d\n", ++i, current->nazvanie, current->nazvanie_main, current->year, current->d, current->period);
          current = current->next;
        }
        break;
      case 3:
        break;
      case 4:
        break;
      case 5:
        break;
      case 6:
        break;
      case 7:
        int nummer;
        printf("Number for sputnik to delete:\n");
        scanf("%d", &nummer);
        while (getchar() != '\n')
          continue;
        current = head;
        i = 0;
        while (current != NULL) {
          if (i == nummer - 1) {
            prev = current;
            free(current);
            current = prev->next;
          } else {
            current = current->next;
            i = i + 1;
          }
        }
        break;
      case 8:
        prev = head;
        while (prev != NULL) {
          current = prev->next;
          free(prev);
          prev = current;
        }
        printf("Finish \n");
        return 0;
        break;
      default:
        printf ("Dont right choose!\n");
        break;
    }
  } 
  return 0; 
}

3 个答案:

答案 0 :(得分:2)

您当前的算法已完全破解。

  • 您永远不会将删除节点之前的节点与后面的节点链接起来。
  • 您的算法根本不考虑删除头节点。
  • 即使在删除目标后,您也会毫无疑问地在列表的其余部分进行游戏。

简而言之,这需要完成。

有很多方法可以做到这一点,许多方法使用至少一对指针(一个prev和一个当前)并将它们推向列表,这似乎是你尝试过的,以及几个答案正在尝试解决的问题。与众不同,我将向您展示如何使用单个指针指针来完成此操作。这样做的另一个好处就是不需要特殊情况的头指针检查。

包括基本错误检查,它是这样完成的:

int nummer;
printf("Number for sputnik to delete:\n");
if (scanf("%d", &nummer) == 1 && nummer > 0)
{
    struct sputnik** pp = &head;
    while (--nummer && *pp)
        pp = &(*pp)->next;;
    if (*pp)
    {
        struct sputnik *p = *pp;
        *pp = p->next;
        free(p);
    }
}
while (getchar() != '\n')
    continue;

这将遍历链接列表中的实际指针;不只是他们的价值观。结果当我们到达指向我们打算删除的节点的指针时,我们使用列表中的指针来实现这一点(如果请求是针对节点的话,包括头指针) 1))。这允许我们将指针更新为其后继地址,然后删除节点并完成。

当涉及单链表操作时,指针指针算法通常提供令人惊讶的优雅解决方案和通常简洁的代码。盯着它看一段时间,或许可以将它与不同的两个/三个指针方法进行比较。

祝你好运。

答案 1 :(得分:0)

您的删除逻辑不正确;

struct sputnik *prev = NULL, *current = NULL;
current = head;

if (number == 1) {
  head = head->next;
  free(current);
  current = NULL;
  return;
}

while (i < (number - 1)) {
  current = current->next;
  i++;
} 

prev = current;
current = current->next;

if (current->next == NULL) {
  free(current);
  current = NULL;
  prev->next = NULL;
  return;
} else {
  prev->next = current->next;
  free(current);
  current = NULL;
  return;
}

答案 2 :(得分:0)

删除项目时,应正确重新链接列表。并且也不要尝试从已删除的元素(prev=current;current=prev->next; free(prev);

中读取

因此,您的case 7可能看起来像这段代码:

        int nummer;
        printf(" Number for sputnik to delete:\n");
        scanf ("%d",&nummer);
        while(getchar()!='\n') continue;
        current=head; prev=NULL;
        i=0;
        while(current!=NULL) {
          if (i==nummer-1){
              if (prev==NULL) {
                // move head pointer if first element should be removed
                head=current->next;
              } else {
                prev->next = current->next; // relink list items
              }
              free(current); // free allocated memory
              break;
          }
          else 
          {
            prev=current; current=current->next; i=i+1; // go to next item
          }
        }
        break;