Becker
Becker

Reputation: 301

function swapNode does not work in doubly linked list

Function swapNode swaps 2 nodes in list. The function create node* temp to store temporary data then swaps the data of node* A and node* B. I can not understand why it does not work. Below here is my code:

#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;
}

tks for your help

Upvotes: 0

Views: 63

Answers (2)

Ian Abbott
Ian Abbott

Reputation: 17403

In swapNode, A and B are initially NULL. The loop that searches for the two matching nodes terminates early when either node is found:

        if (A || B)
            break;

At most one of A and B will be non-NULL when the loop terminates so at least one of A and B will be NULL. This causes the function to return false:

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

To avoid that, you should change the loop to break when both A and B are non-NULL:

        if (A && B)
            break;

Also, the loop is only checking count - 1 elements of the list, so it ignores the final element:

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

To check all elements, you need to change that to:

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

Alternatively, you could ignore listNode->count and check the temp pointer instead:

    while (temp != NULL)

That will work because temp is initialized to listNode->head, which will be NULL for an empty list, and for a non-empty list, the next member of the final element on the list is NULL, so temp = temp->next; will set temp to NULL when the final element has been checked.


There are other problems in swapNode related to the actual swapping of the nodes after they have been found. The original code to do that looks totally wrong:

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

The temp pointer will either be NULL or will point to a node after the A and B nodes.

    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;

The code does not alter listNode->head or listNode->tail when A or B is at the head or tail of the list.

    free(temp);

Why is it freeing temp here when all the function is supposed to be doing is swapping nodes?

The code to swap the nodes A and B needs to be able to deal with neither, either or both nodes being at the end(s) of the list, and with A and B being adjacent nodes in either order. Here is a sequence to handle all that:

    /* 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;
    }

Upvotes: 3

Bodo
Bodo

Reputation: 9855

Using a debugger you would see that function swapNode returns at

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

If you would step through the for loop you could see that you break from the loop when at least one of A and B is set, i.e. when the first matching node is found.

        if (A || B)
            break;

Change this to

        if (A && B)
            break;

Upvotes: 1

Related Questions