我正在尝试使用C交换双链表中的两个节点 . 如果我传入一些值,例如列表的头部和尾部,以及介于两者之间的某些值,它会工作 . 然而,在其他人看来,一个人的 Value 似乎被另一个人所覆盖,我被抛入了一个循环中 .
节点/列表:
struct node //node for linked list
{
unsigned long int *id;
char *firstname, *lastname, *department;
float *gpa;
struct node *next, *prev;
};
struct linked_list //doubly linked_list data structure
{
struct node *head, *tail;
};
我可以成功地将节点添加到列表中并将尾部移动到新添加的节点 .
void *add_node(struct node **tail, unsigned long int *id, char *first, char *last, char *dept, float *gpa) //create a node, add to tail
{
struct node *newStudent = (struct node*)malloc(sizeof(struct node));
newStudent->firstname = (char*)malloc(strlen(first)+1);
newStudent->lastname = (char*)malloc(strlen(last)+1);
newStudent->department = (char*)malloc(strlen(dept)+1);
newStudent->id = (unsigned long int*)malloc(sizeof(unsigned long int));
newStudent->gpa = (float*)malloc(sizeof(float));
*(newStudent->id) = *id;
*(newStudent->gpa) = *gpa;
strcpy(newStudent->firstname, first);
strcpy(newStudent->lastname, last);
strcpy(newStudent->department, dept);
newStudent->next = NULL;
if(tail) //not the first node in the list
{
newStudent->prev = *tail;
(*tail)->next = newStudent;
*tail = newStudent;
}
else //head of the list
return newStudent;
}
最后,我的交换功能:
void *_swap(struct node **x, struct node **y, struct linked_list **list)
{
struct node *temp = (struct node*)malloc(sizeof(struct node));
memcpy(temp, *x, sizeof(struct node));
if( (*y)->prev ) /// if y has a previous...
{
(*x)->prev = (*y)->prev;
(*y)->prev->next = *x;
}
else
(*x)->prev = NULL;
if( (*y)->next ) /// if y has a next...
{
(*x)->next = (*y)->next;
(*y)->next->prev = *x;
}
else
(*x)->next = NULL;
if( temp->prev) /// if original x has a previous...
{
(*y)->prev = temp->prev;
temp->prev->next = *y;
}
else
(*y)->prev = NULL;
if(temp->next) /// if original x has a next...
{
(*y)->next = temp->next;
temp->next->prev = *y;
}
else
(*y)->next = NULL;
free(temp);
if((*list)->head == *x && (*list)->tail == *y)
{
(*list)->head = *y;
(*list)->tail=*x;
}
else if((*list)->head == *y && (*list)->tail == *x)
{
(*list)->head = *x;
(*list)->tail=*y;
}
else if((*list)->head == *x)
(*list)->head = *y;
else if((*list)->head == *y)
(*list)->head = *x;
else if((*list)->tail == *x)
(*list)->tail = *y;
else if((*list)->tail == *y)
(*list)->tail = *x;
printf("%s %s %s %s %s\n\n\n\n", (*list)->head->firstname, (*list)->head->next->firstname, (*list)->head->next->next->firstname, (*list)->head->next->next->next->firstname, (*list)->head->next->next->next->next->firstname);
}
当我打电话给temp-> next-> prev = * y;它有时似乎覆盖了x的值,在这种情况下是x,而不是简单地将linked_list指针重新分配给y .
我能够很好地构建我的列表:
struct linked_list *list = (struct linked_list*)malloc(sizeof(struct linked_list));
list->head = (struct node*)malloc(sizeof(struct node));
list->tail = (struct node*)malloc(sizeof(struct node));
unsigned long int *id = malloc(sizeof(unsigned long int));
*id = 343232;
float gpa = 3.2;
list->head = add_node(NULL, id, "Matthew", "D", "CECS", &gpa);
list->tail = list->head;
add_node(&(list->tail), id, "John", "X", "PNY", &gpa);
add_node(&(list->tail), id, "Rebecca", "H", "ECE", &gpa);
1 回答
你的代码中有很多东西会跳出来 .
你分配了很多东西,经常是不必要和无用的 . 正如rcgldr所指出的,交换函数不应该分配新节点 . 毕竟,在交换之后,列表由相同的节点组成,只是以不同的顺序 . 没有新节点 .
您的"client code",即使用链接列表函数的函数,在您的示例中可能是
main
,不应该显式分配内存 . 也不应该手动填充节点 . 它应该只调用add_node
和delete_node
,你也应该编码以释放所有分配的内存 .没有必要在您的情况下传递指针指针 . 传递指向节点和列表结构的指针就足够了 . 这允许您更改结构的字段 . 指向struct的指针只有在你想改变结构句柄本身时才有意义,例如通过重新分配它,但你不这样做 . (指向指针通常用于单链表,其中头不存储在结构中 . 即使在那里,将单个指针包装在结构中也是有用的,这样就不需要指针指针了 . . )
所有逻辑都应该在你的函数中发生 . 不要修改'main
中的
next和
prev` 指针;这就是功能的用途 . 当你调用一个函数并从它返回时,某些"invariants"应该成立,例如:当列表为空时,
head
和tail
都是NULL
.否则,
head
指向第一个节点; 'head-> previs
NULL. The
tailpoints to the last node; ´tail->next
是NULL
.当节点
nd
具有上一个节点时,则nd->prev->next == nd
.同样,当节点
nd
具有下一个节点时,则为nd->next->prev == nd
.您甚至可以编写一个完整性检查函数来强制执行函数进入和退出时的这些invarants .
您为所有字段分配数据 . 内存分配对于字符串是有意义的,字符串是字符数组,其长度你对标量变量
id
和gpa
有意义 . 您可以将它们声明为非指针并仅指定它们 . (分配内存并通过指针访问它们并没有错,但直接访问要简单得多 . )某些函数返回
void *
,void指针 . 那不是你想要的 . 您的函数应该是void
,即没有返回值,或者它们应该返回指向节点的指针 . (void指针是一种合法的数据类型,它指的是指向任何数据类型的指针,你不能解除引用 . 它用于通用函数,如qsort
,不应该在你的代码中使用 . 你不是在编写泛型函数,但功能为您的具体链表 . )您可以将交换视为删除节点并在各自的旧前任之后重新插入它们 . 您仍然需要注意捕获节点相邻的情况 .
这是一个示例实现,试图尊重我上面提到的要点: