簡體   English   中英

反轉 C 中的雙向鏈表

[英]Reversing a doubly linked list in C

我目前正在嘗試反轉雙向鏈表並將其打印在 C 中。 不幸的是,我的代碼只打印一項,我不知道為什么。 我在 Stack Overflow 和在線查看了多種解決方案

基本上,我傳遞了一個看起來像這樣的 txt 文件。

Name 100
John 40

這是完整的代碼:

    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    
    typedef struct person{
      char* firstName;  
      char* age;
      struct person* previous; 
      struct person* next; 
    }Person;
    
    void print(Person*);
    
    void reverseList(Person*);
    
    Person* add (Person*, char*, char*);
    
    void print (Person* list) {
      while(list != NULL) {
        printf("%s %s\n",list->firstName,list->age);
        list = list->next;
      }
    }
    
    void reverseList (Person* list) {
      Person *current = list;
      Person* last = current;
    
      while(current != NULL) {
        Person *temp = NULL;
        temp = current->previous; 
        current->previous = current->next;
        current->next = temp;
        last = current;
        current = current->previous;
      }
      list = last;
    
      while(list != NULL) {
        printf("%s %s\n",list->firstName,list->age);
        list = list->next;
      }
    }
    
    Person* add (Person *list, char *firstName, char *age) {
      // allocate node
      Person* node = (Person*)malloc(sizeof(Person));
    
      // if node is empty
      if(node != NULL) {
        node->firstName=(char *)malloc(sizeof(char)*strlen(firstName)+1);
        
        // insert name
        strcpy(node->firstName, firstName);
    
        // allocate age & insert
        node->age = (char*)malloc(sizeof(char)*strlen(age)+1);
        strcpy(node->age, age);
        node->next = NULL;
    
        if(list == NULL) list = node;
        else {
          Person *curr = list;
          Person *pre = NULL;
          while(curr != NULL) {
            if(strcmp(curr->firstName, firstName) > 0) {
              if(pre == NULL) {
                node->next = list;
                list = node;
              } else {
                pre->next = node;
                node->next = curr;
              }
              return list;
            }
            pre = curr;
            curr = curr->next;
          }
          pre->next = node;
        }
        return list;
      }
      return list;
    }
    
    int main (int argc, char **argv) {
      FILE *in = fopen(argv[1], "r");
      char name[20];
      char num[20];
      Person *list=NULL;
     
      while(fscanf(in, "%s %s",name, num)==2) {
        list = add(list,name, num);
      }
    
      printf ("Original List\n***\n");
      print (list);
      
      printf ("Printing Reversed List\n***\n");
      reverseList(list);
      
      return 0;
    }

目標是,如果我通過如下列表:

John 10
Mary 5

它會打印

Mary 5
John 10

知道這里發生了什么嗎? 不知道我在誤解什么

正如我在評論中指出的那樣,您的問題不在您的reverseList() function 中(盡管它需要幫助),您的問題在於您的add() function 沒有設置任何previous的節點指針。 因此,當您到達列表末尾並嘗試反向打印列表時,您打印的只是當前節點,並且您的打印循環將退出,因為->next節點(原始->previous節點)是NULL

要解決此問題,您需要連接->previous指針。

編輯add()重寫以解決原始角落案例的排序錯誤)

Person *add (Person *list, char *firstName, char *age)
{
  size_t len;
  Person *node = malloc (sizeof *node),   /* allocate node, use dereference ptr for size */
         *iter = list,
         *prev = NULL;

  if (!node) {                            /* validate EVERY allocation */
    perror ("malloc-node");
    return list;
  }
  node->next = node->previous = NULL;     /* initialize next and previous NULL */

  len = strlen (firstName);                         /* get length of firstName */
  if (!(node->firstName = malloc (len + 1))) {      /* allocate/validate */
    perror ("malloc-node->firstName");
    free (node);                                    /* free node before return */
    return list;
  }
  memcpy (node->firstName, firstName, len + 1);     /* copy firstName to node */

  len = strlen (age);                               /* get length of age */
  if (!(node->age = malloc (len + 1))) {            /* allocate/validate */
    perror ("malloc-node->age");
    free (node->firstName);                         /* free node->firstName, and */
    free (node);                                    /* free node before return */
    return list;
  }
  memcpy (node->age, age, len + 1);                 /* copy age to node */

  if (!list)                              /* if list empty */
    return node;                          /* return node */

  /* iterate until iter->firstName sorts before firstName */
  while (iter && strcmp (firstName, iter->firstName) <= 0) {
    prev = iter;
    iter = iter->next;
  }

  if (!iter) {                            /* if new end node */
    prev->next = node;
    node->previous = prev;
  }
  else {                                  /* insert between nodes */
    node->next = iter->next;
    node->previous = iter;
    if (iter->next)                       /* protect when new next to last */
      iter->next->previous = node;
    iter->next = node;
  }

  /* if new first node, return iter, otherwise return list */
  return strcmp (list->firstName, firstName) >= 0 ? node : list;
}

您的reverseList()實際上不能“反轉”任何東西。 它所能做的就是迭代到列表中的最后一個節點,然后使用->previous指針以相反的順序打印列表。 因此,您不必要地交換指針。 只需迭代到最后,轉身並使用->previous指針迭代回到開頭反向打印列表,例如

void reverseList (Person *list) {

  if (!list) {                  /* validate list not empty */
    puts ("(list empty)");
    return;
  }
  
  while (list->next != NULL)    /* iterte to last node */
    list = list->next;

  while (list) {    /* print in reverse using ->previous pointers */
    printf ("%s %s\n", list->firstName, list->age);
    list = list->previous;
  }
}

不幸的是,您無法更改 function 原型。 要實際反轉void function 中的行列列表,您必須傳遞列表指針的地址,以便可以使用新的第一個節點更新該地址處的節點。 如果您確實想實際反轉列表,您可以執行以下操作:

void reverseList (Person **list) {
  
  Person *current = *list;
  Person *last = current;
  
  if (!current || !current->next)
    return;
  
  do {
    Person *temp = current->previous; 
    current->previous = current->next;
    current->next = temp;
    last = current;
  } while ((current = current->previous));
  *list = last;
}

您可以將上述內容稱為: reverseList(&list); . 然后你只需調用print (list); 再次反向打印列表....

此外,如果您可以更改原型並將列表指針的地址傳遞給您的add() function,您將無需跟蹤precurr ,您可以簡單地使用當前節點的地址和指向當前節點的指針。 請參閱Linus 了解指針

最后,不要忘記釋放您分配的 memory。 在釋放節點之前,您必須釋放firstNameage 一個簡單的刪除列表 function 刪除所有當前分配的與列表關聯的 memory 可以寫成:

/** delete all nodes in list */
void del_list (Person *list)
{
    while (list) {
        Person *victim = list;
        list = list->next;
        free (victim->firstName);
        free (victim->age);
        free (victim);
    }
}

如果您還有其他問題,請告訴我。


完整示例

由於我在評論中提供的示例代碼鏈接只能使用一個月,因此我將在此處包含示例以供后代使用。 例子是:

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

typedef struct person{
  char* firstName;  
  char* age;
  struct person* previous; 
  struct person* next; 
}Person;

void print(Person*);

// void reverseList(Person*);

Person* add (Person*, char*, char*);

void print (Person* list) {
  while(list != NULL) {
    printf("%s %s\n",list->firstName,list->age);
    list = list->next;
  }
}

void prnptrs (Person *list) {
    while (list) {
        printf ("%p  %p  %p\n", (void*)list->previous, (void*)list, (void*)list->next);
        list = list->next;
    }
}
#ifndef PASSPTR2PTR
void reverseList (Person *list) {

  if (!list) {                  /* validate list not empty */
    puts ("(list empty)");
    return;
  }
  
  while (list->next != NULL)    /* iterte to last node */
    list = list->next;

  while (list) {    /* print in reverse using ->previous pointers */
    printf ("%s %s\n", list->firstName, list->age);
    list = list->previous;
  }
}
#else
void reverseList (Person **list) {
  
  Person *current = *list;
  Person *last = current;
  
  if (!current || !current->next)
    return;
  
  do {
    Person *temp = current->previous; 
    current->previous = current->next;
    current->next = temp;
    last = current;
  } while ((current = current->previous));
  *list = last;
}
#endif

Person *add (Person *list, char *firstName, char *age)
{
  size_t len;
  Person *node = malloc (sizeof *node),   /* allocate node, use dereference ptr for size */
         *iter = list,
         *prev = NULL;

  if (!node) {                            /* validate EVERY allocation */
    perror ("malloc-node");
    return list;
  }
  node->next = node->previous = NULL;     /* initialize next and previous NULL */

  len = strlen (firstName);                         /* get length of firstName */
  if (!(node->firstName = malloc (len + 1))) {      /* allocate/validate */
    perror ("malloc-node->firstName");
    free (node);                                    /* free node before return */
    return list;
  }
  memcpy (node->firstName, firstName, len + 1);     /* copy firstName to node */

  len = strlen (age);                               /* get length of age */
  if (!(node->age = malloc (len + 1))) {            /* allocate/validate */
    perror ("malloc-node->age");
    free (node->firstName);                         /* free node->firstName, and */
    free (node);                                    /* free node before return */
    return list;
  }
  memcpy (node->age, age, len + 1);                 /* copy age to node */

  if (!list)                              /* if list empty */
    return node;                          /* return node */

  /* iterate until iter->firstName sorts before firstName */
  while (iter && strcmp (firstName, iter->firstName) <= 0) {
    prev = iter;
    iter = iter->next;
  }

  if (!iter) {                            /* if new end node */
    prev->next = node;
    node->previous = prev;
  }
  else {                                  /* insert between nodes */
    node->next = iter->next;
    node->previous = iter;
    if (iter->next)                       /* protect when new next to last */
      iter->next->previous = node;
    iter->next = node;
  }

  /* if new first node, return iter, otherwise return list */
  return strcmp (list->firstName, firstName) >= 0 ? node : list;
}

/** delete all nodes in list */
void del_list (Person *list)
{
    while (list) {
        Person *victim = list;
        list = list->next;
        free (victim->firstName);
        free (victim->age);
        free (victim);
    }
}

int main (int argc, char **argv) {
  FILE *in = argc > 1 ? fopen(argv[1], "r") : stdin;
  char name[20];
  char num[20];
  Person *list=NULL;
 
  while(fscanf(in, "%s %s",name, num)==2) {
    list = add(list,name, num);
  }
  fclose (in);

  printf ("Original List\n***\n");
  print (list);
  puts ("\nPointers\n***");
  prnptrs (list);
  
  printf ("\nPrinting Reversed List\n***\n");
#ifndef PASSPTR2PTR
  reverseList(list);
#else
  reverseList(&list);
  print (list);
#endif
  del_list (list);
  
  return 0;
}

(1) 反向打印列表和 (2) 實際反轉列表的兩個版本都有條件地包含在PASSPTR2PTR的定義中。 如果未定義,它將簡單地傳遞一個指向列表的指針,反向打印列表,如果定義了PASSPTR2PTR ,它將傳遞列表指針的地址並執行實際的列表反轉。

您在 pastebin 上發布的測試數據文件是:

$ cat dat/ypzgttvk.txt
John 12345
Sam 54321
Lion 43222

在這兩種情況下,output 將是:

$ ./bin/lld_reverse_add dat/ypzgttvk.txt
Original List
***
John 12345
Lion 43222
Sam 54321

Pointers
***
(nil)  0xb014a0  0xb01580
0xb014a0  0xb01580  0xb01510
0xb01580  0xb01510  (nil)

Printing Reversed List
***
Sam 54321
Lion 43222
John 12345

其中顯示了原始列表,列表中每個節點的所有previous current next節點的地址,最后是列表的 output 反轉。

如果您還有其他問題,請告訴我。

代碼實際上似乎工作正常。 我認為您的測試用例編碼不正確,也許您忘記設置mary->previous = john (或john->next = mary

我跑了

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

typedef struct person{
  char *firstName;  
  int age; 
  struct person *previous;
  struct person *next;
}Person;

void reverseList (Person *list) {
  Person* current = list;
  Person* last = current;

  while(current != NULL) {
    Person* temp = NULL;
    temp = current->previous;
    current->previous = current->next;
    current->next = temp;
    last = current;
    current = current->previous;
  }
  list = last;

  while(list != NULL) {
    printf("%s %d\n",list->firstName,list->age);
    list = list->next;
  }
}


int main(void) {
    struct person *john = (struct person*)malloc(sizeof(Person));
    struct person *mary = (struct person*)malloc(sizeof(Person));
    john->next = mary;
    mary->previous = john;

    john->age = 10;
    john->firstName = "john";

    mary->age = 5;
    mary->firstName = "mary";

    reverseList(john);

}

結果是

mary 5
john 10

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM