[英]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,您將無需跟蹤pre
和curr
,您可以簡單地使用當前節點的地址和指向當前節點的指針。 請參閱Linus 了解指針
最后,不要忘記釋放您分配的 memory。 在釋放節點之前,您必須釋放firstName
和age
。 一個簡單的刪除列表 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.