【问题标题】:function swapNode does not work in doubly linked list函数 swapNode 在双向链表中不起作用
【发布时间】:2020-01-20 13:01:07
【问题描述】:

函数 swapNode 交换列表中的 2 个节点。函数创建node* temp存储临时数据,然后交换node* Anode* B的数据。我不明白为什么它不起作用。下面是我的代码:

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
struct node;
struct list;

typedef struct node node;
typedef struct list list;

struct node
{
    int point;
    char name[30];
    node *next;
    node *prev;
};

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

node *allocateNewNode(int point, char name[30], node *prev, node *next);
list *createList();
bool insertHead(list *listNode, int point, char name[30]);
bool compareName(char a[30], char b[30]);
bool swapNode(list *listNode, char nameA[30], char nameB[30]);

int main()
{
    list *listNode = createList();

    insertHead(listNode, 10, "abc def");
    insertHead(listNode, 9, "qwe rty");
    insertHead(listNode, 8, "ui op");
    insertHead(listNode, 30, "fgh jkl");
    insertHead(listNode, 1234, "akaka");

    swapNode(listNode, "ui op", "abc def");

    node *temp = listNode->head;
    while (temp != NULL)
    {
        printf("%-20s%d\n", temp->name, temp->point);
        temp = temp->next;
    }
    free(temp);
    printf("\n%d", listNode->count);
    return 0;
}

node *allocateNewNode(int point, char name[30], node *prev, node *next)
{
    node *newNode = (node *)malloc(sizeof(node));
    newNode->point = point;
    strcpy(newNode->name, name);
    newNode->next = next;
    newNode->prev = prev;
    return newNode;
}

list *createList()
{
    list *listNode = (list *)malloc(sizeof(list));
    listNode->count = 0;
    listNode->head = NULL;
    listNode->tail = NULL;
    return listNode;
}

bool insertHead(list *listNode, int point, char name[30])
{
    node *newNode = allocateNewNode(point, name, NULL, listNode->head);
    if (listNode->head)
        listNode->head->prev = newNode;
    listNode->head = newNode;
    if (listNode->tail == NULL)
        listNode->tail = newNode;
    ++listNode->count;
    return true;
}
bool compareName(char a[30], char b[30])
{
    for (int i = 0; i < 31; i++)
    {
        if (a[i] != b[i])
            return false;
        if (a[i] == '\0')
            break;
    }

    return true;
}

bool swapNode(list *listNode, char nameA[30], char nameB[30])
{
    node *A = NULL, *B = NULL;
    node *temp = listNode->head;

    for (int i = 0; i < listNode->count - 1; i++)
    {
        if (compareName(temp->name, nameA))
            A = temp;
        else if (compareName(temp->name, nameB))
            B = temp;
        temp = temp->next;
        if (A || B)
            break;
    }
    if (!A || !B)
        return false;
    else if (A == B)
        return false;

    *temp = *A;
    *A = *B;
    *B = *temp;

    if (A->prev)
        A->prev->next = A;
    if (A->next)
        A->next->prev = A;
    if (A->prev)
        A->prev->next = A;
    if (A->next)
        A->next->prev = A;
    free(temp);
    return true;
}

感谢您的帮助

【问题讨论】:

  • 你能说更多关于它不起作用吗?你在看什么是不对的?请解释一下。
  • 它就像我们在函数 main 中注释行 swapNode(listNode, "ui op", "abc def"); 时一样工作。它只是打印出来,就像我们只使用 insertHead 函数一样,没有任何变化
  • 大声笑,好吧,除了涉及swapNode 之外,这并没有真正说明问题。那么,在那个不正确的功能中会发生什么?您是使用调试器跟踪步骤,还是使用断点查看变量值?顺便说一句,您的程序中(至少)有 4 个内存泄漏。
  • 我使用了调试器,我看到node *Anode *B 的数据交换了,但链接仍然像以前一样。你能告诉我这是怎么发生的吗?
  • 对不起,我的错,我会吸取经验的

标签: c data-structures swap doubly-linked-list


【解决方案1】:

swapNodeAB 中最初是NULL。当找到任何一个节点时,搜索两个匹配节点的循环会提前终止:

        if (A || B)
            break;

当循环终止时,AB 中最多有一个为非 NULL,因此AB 中至少有一个为 NULL。这会导致函数返回false

    if (!A || !B)
        return false;

为避免这种情况,您应该将循环更改为在 AB 均非 NULL 时中断:

        if (A && B)
            break;

此外,循环仅检查列表的count - 1 元素,因此它忽略了最后一个元素:

    for (int i = 0; i < listNode->count - 1; i++)

要检查所有元素,您需要将其更改为:

    for (int i = 0; i < listNode->count; i++)

或者,您可以忽略 listNode-&gt;count 并改为检查 temp 指针:

    while (temp != NULL)

这会起作用,因为temp 被初始化为listNode-&gt;head,对于空列表,这将是NULL,对于非空列表,列表中最后一个元素的next 成员是@ 987654345@,所以temp = temp-&gt;next; 将在检查最后一个元素时将temp 设置为NULL


swapNode 中还有其他问题,涉及到节点在被发现后的实际交换。执行此操作的原始代码看起来完全错误:

    *temp = *A;
    *A = *B;
    *B = *temp;

temp 指针要么是 NULL,要么指向 AB 节点之后的节点。

    if (A->prev)
        A->prev->next = A;
    if (A->next)
        A->next->prev = A;
    if (A->prev)
        A->prev->next = A;
    if (A->next)
        A->next->prev = A;

AB 位于列表的头部或尾部时,代码不会改变listNode-&gt;headlistNode-&gt;tail

    free(temp);

当所有功能都应该做的是交换节点时,为什么要在这里释放temp

交换节点 AB 的代码需要能够处理既不位于列表末尾的节点,也不能够处理位于列表末尾的节点,并且 AB 位于以任一顺序相邻节点。这是处理所有这些的序列:

    /* update list head pointer */
    if (listNode->head == A)
        listNode->head = B;
    else if (listNode->head == B)
        listNode->head = A;

    /* update list tail pointer */
    if (listNode->tail == A)
        listNode->tail = B;
    else if (listNode->tail == B)
        listNode->tail = A;

    /* update ->prev->next pointers */
    if (A->prev != NULL && A->prev != B)
        A->prev->next = B;
    if (B->prev != NULL && B->prev != A)
        B->prev->next = A;

    /* update ->next->prev pointers */
    if (A->next != NULL && A->next != B)
        A->next->prev = B;
    if (B->next != NULL && B->next != A)
        B->next->prev = A;

    /* update A->prev and B->prev pointers */
    if (A->prev == B)
    {
        A->prev = B->prev;
        B->prev = A;
    }
    else if (B->prev == A)
    {
        B->prev = A->prev;
        A->prev = B;
    }
    else
    {
        temp = A->prev;
        A->prev = B->prev;
        B->prev = temp;
    }

    /* update A->next and B->next pointers */
    if (A->next == B)
    {
        A->next = B->next;
        B->next = A;
    }
    else if (B->next == A)
    {
        B->next = A->next;
        A->next = B;
    }
    else
    {
        temp = A->next;
        A->next = B->next;
        B->next = temp;
    }

【讨论】:

  • 对不起,我的错误,当我看到它不起作用时,(A || B) 是我从(A &amp;&amp; B) 修复的。所以我认为这不是问题
  • 我认为temp 不是列表的节点,所以免费就可以了吗?
  • @Becker 你不应该释放temp,因为你没有从列表中删除元素,你只是在改变它们在列表中的顺序。
【解决方案2】:

使用调试器,您会看到函数 swapNode 返回到

    if (!A || !B)
        return false;

如果您单步执行for 循环,则当至少设置了AB 之一时,即找到第一个匹配节点时,您可以从循环中看到break

        if (A || B)
            break;

改成

        if (A && B)
            break;

【讨论】:

    猜你喜欢
    • 2019-08-16
    • 1970-01-01
    • 1970-01-01
    • 2015-05-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多