在双向链表中交换节点时出现问题

Trouble swapping nodes in doubly linked list

我正在尝试使用 C 交换双向链表中的两个节点。如果我传入一些值,例如列表的头部和尾部,以及介于两者之间的一些值,它就会工作。然而,在其他人身上,一个的价值似乎被另一个覆盖,我陷入了一个循环。

Node/List:

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 的值,在这种情况下,而不是简单地将 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);

您的代码中跳出了很多东西。

  • 你分配了很多东西,通常是不必要的和无用的。正如 rcgldr 指出的那样,交换功能不应分配新节点。毕竟,列表由相同的节点组成,只是在交换后顺序不同。没有新的节点。

  • 您的 "client code",即使用链表函数的函数,在您的示例中可能是 main,不应显式分配内存。它也不应该手动填充节点。它应该只调用 add_nodedelete_node,您还应该对其进行编码以释放所有分配的内存。

  • 在你的例子中没有必要将指针传递给指针。将指针传递给节点和列表结构就足够了。这允许您更改结构的字段。只有当你想改变结构句柄本身时,指向结构指针的指针才有意义,例如通过重新分配它,但你不这样做。 (指向指针的指针通常用于单链表,其中头部不存储在结构中。即使在那里,将单个指针包装在结构中可能很有用,这样就不需要指向指针的指针.)

  • 所有逻辑都应该发生在你的函数中。不要修改'main'中的nextprev指针;这就是功能的用途。当您调用一个函数并从中调用 return 时,某些 "invariants" 应该成立,例如:

    • 当列表为空时,headtail都是NULL
    • 否则,head指向第一个节点; ´head->previsNULL. Thetailpoints to the last node; ´tail->nextNULL.
    • 当节点nd有前一个节点时,则nd->prev->next == nd
    • 同样,当节点nd有下一个节点时,则nd->next->prev == nd

    您甚至可以编写完整性检查函数来在函数进入和退出时强制执行这些不变量。

  • 您为所有字段分配了数据。内存分配对字符串有意义,字符串是您事先不知道其长度的字符数组。对于标量变量 idgpa 没有意义。您可以将它们声明为非指针并直接分配给它们。 (分配内存并通过指针访问它们并没有错,但直接访问要简单得多。)

  • 你的一些函数returnvoid *,空指针。那不是你想要的。您的函数应该是 void 即没有 return 值,或者它们应该 return 指向节点的指针。 (void 指针是一种合法的数据类型,指的是指向任何数据类型的指针,您不能取消引用它。它用于通用函数,例如 qsort,不应在您的代码中使用。您是不是编写通用函数,而是编写具体链表的函数。)

您可以将交换视为移除节点并在它们各自的旧前任之后重新插入它们。您仍然需要注意捕捉节点相邻的情况。

这是一个尝试尊重我上面提到的要点的示例实现:

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

typedef unsigned long int ulong;

struct node
{
    ulong id;
    char *name;
    float gpa;
    struct node *next;
    struct node *prev;
};

struct list
{
    struct node *head;
    struct node *tail;
};



/*
 *      Create a new, unconnected node
 */
struct node *node_new(ulong id, const char *name, float gpa)
{
    struct node *node = malloc(sizeof(*node));  // Error checking!

    node->prev = NULL;
    node->next = NULL;
    node->id = id;
    node->gpa = gpa;

    node->name = malloc(strlen(name) + 1);
    strcpy(node->name, name);

    return node;
}

/*
 *      Create a list
 */
struct list *list_new()
{
    struct list *list = malloc(sizeof(*list));  // Error checking!

    list->head = list->tail = NULL;
    return list;
}

/*
 *      Add a student to list
 */
struct node *list_add(struct list *list,
    ulong id, const char *name, float gpa)
{
    struct node *node = node_new(id, name, gpa);

    node->prev = list->tail;
    if (list->tail == NULL) {
        list->head = list->tail = node;
    } else {
        list->tail->next = node;
        list->tail = node;
    }

    return node;
}

/*
 *      Delete a node from the list.
 */
void list_delete(struct list *list, struct node *node)
{
    if (node->prev) node->prev->next = node->next;
    else list->head = node->next;

    if (node->next) node->next->prev = node->prev;
    else list->tail = node->prev;

    free(node->name);
    free(node);
}

/*
 *      Find student by id; return NULL if not found.
 */
struct node *list_find_by_id(const struct list *list, ulong id)
{
    struct node *node = list->head;

    while (node) {
        if (node->id == id) return node;
        node = node->next;
    }

    return NULL;
}

/*
 *      Extract a node without deleting
 */
void list_remove(struct list *list, struct node *node)
{
    if (node->prev) node->prev->next = node->next;
    else list->head = node->next;

    if (node->next) node->next->prev = node->prev;
    else list->tail = node->prev;

    node->prev = node->next = NULL;
}

/*
 *      Insert node after prev or at the front when prev is NULL
 */
void list_insert_after(struct list *list,
    struct node *node, struct node *prev)
{
    if (prev) {
        node->next = prev->next;
        prev->next = node;
    } else {
        node->next = list->head;
        list->head = node;
    }
    node->prev = prev;
    if (node->next) node->next->prev = node;
}

/*
 *      Swap two nodes' positions in the list
 */
void list_swap(struct list *list, struct node *x, struct node *y)
{
    if (x == y) return;

    struct node *xprev = x->prev;
    struct node *yprev = y->prev;

    if (xprev == y) {            
        list_remove(list, x);
        list_insert_after(list, x, yprev);
    } else if (yprev == x) {            
        list_remove(list, y);
        list_insert_after(list, y, xprev);
    } else {
        list_remove(list, x);
        list_remove(list, y);

        list_insert_after(list, x, yprev);
        list_insert_after(list, y, xprev);
    }
}

/*
 *      Print list
 */
void list_print(const struct list *list)
{
    const struct node *node = list->head;

    while (node) {
        printf("%8lu  %-20s  %8.1f\n", node->id, node->name, node->gpa);
        node = node->next;
    }
    printf("\n");
}

/*
 *      Delete a list and all its nodes
 */
void list_destroy(struct list *list)
{
    while (list->head) list_delete(list, list->head);
    free(list);
}

/*
 *      Example client code using the list
 */
int main()
{
    struct list *list = list_new();

    list_add(list, 342232, "Matthew",   3.2);
    list_add(list, 342856, "John",      1.9);
    list_add(list, 342109, "Rebecca",   6.4);
    list_add(list, 342834, "Shirley",   2.6);
    list_add(list, 343009, "Simon",     1.4);
    list_add(list, 342170, "Antonio",   3.5);

    list_print(list);

    struct node *simon = list_find_by_id(list, 343009);
    struct node *becky = list_find_by_id(list, 342109);

    if (simon && becky) {
        list_swap(list, simon, becky);
        list_print(list);
    }

    list_destroy(list);

    return 0;
}