简体   繁体   中英

Reversing a linked list list

I was trying to reverse a linked list but I kept comming across an issue. I finally figured out was wrong and fixed it, however I do not understand why my last approach doesn't work. The code below is the one that successfully reverses a linked list.

void reverse_list(list** head)
{
    list* currNode = *head;
    list* prevNode = NULL;
    list* tmpNode;

    while(1)
    {
        tmpNode = currNode->next;
        currNode->next = prevNode;
        if(tmpNode == NULL)
        {
            *head = currNode;
            break;
        }
        prevNode = currNode;
        currNode = tmpNode;
    }

}

However, the code below does not work. Both use double pointers, but in this one I dereferenced head twice to get to the actual object and assigned it with *currNode. When I run this code, It ends up going into an infinite loop and its missing the last item. For example, if the items were 1,2,3,4,5, then the reverse would be 5,4,3,2, and it keeps printing the same list. I don't understand why this approach isn't working since I'm accessing the actual object (by derefrening twice) and assigning it with a new object (*currNode).

void reverse_list(list** head)
{
    list* currNode = *head;
    list* prevNode = NULL;
    list* tmpNode;

    while(1)
    {
        tmpNode = currNode->next;
        currNode->next = prevNode;
        if(tmpNode == NULL)
        {
            **head = *currNode;
            break;
        }
        prevNode = currNode;
        currNode = tmpNode;
    }

}

The code below has the same issue as the one above. I followed the same approach as with the above code only this one uses a single pointer.

void reverse_list(list* head)
{
    list* currNode = head;
    list* prevNode = NULL;
    list* tmpNode;

    while(1)
    {
        tmpNode = currNode->next;
        currNode->next = prevNode;
        if(tmpNode == NULL)
        {
            *head = *currNode;
            break;
        }
        prevNode = currNode;
        currNode = tmpNode;
    }

}

Any help to understand this would greatly be appreciated. Thank you!

Different levels of indirection.

Given list** head , *head is the value pointed to by head , a pointer to a Node , a list * . We dereference one more time and **head dereferences to list * and then to list . We're all out of pointers and and are accessing the object at the end of the line. So

**head = *currNode;

dereferences both pointers all the way back to the objects. This is assigning values to objects, not addresses to pointers to objects. Rather than changing the linkage of the list by updating pointers, whatever list head ultimately pointed at has been changed to match currNode , breaking the integrity of the list.

*head = currNode;

only derefences head one level closer to the object. Here we are operating on pointers and changing linkage.

In the final example we have

*head = *currNode;

and this is similar to the first failed attempt. It is assigning values rather than changing the pointers.

For starters all the three functions in general can invoke undefined behavior when they are called for empty lists due to these lines

list* currNode = head;
//...
tmpNode = currNode->next;

because there is used a null pointer to access memory.

In the second function in this if statement

    if(tmpNode == NULL)
    {
        **head = *currNode;
        break;
    }

the original first node is overwritten by the last node

**head = *currNode;

So after exiting the function you will have that the original pointer head still points to the originally first node that was overwritten by the last node of the list. So as a result one actual data will be lost and there will be a memory leak.

As for the third function then it is enough to point to that the function accepts the pointer to the head node by value.

void reverse_list(list* head)

That is the function deals with a copy of the value of the original variable head. Changing the copy will not influence on the original pointer. The original pointer will still point to the first node.

The function can look simpler the following way

void reverse( list **head )
{
    list *current = *head;
    *head = NULL;
        
    while ( current != NULL )
    {
        struct Node *tmp = current;
        current = current->next;
        tmp->next = *head;
        *head = tmp;
    }
}

Here is a demonstrative program.

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

typedef struct Node
{
    int data;
    struct Node *next;
} list;

int push_front( list **head, int data )
{
    list *new_node = malloc( sizeof( list ) );
    int success = new_node != NULL;
    
    if ( success )
    {
        new_node->data = data;
        new_node->next = *head;
        *head = new_node;
    }
    
    return success;
}

void display( const list *head )
{
    for ( ; head != NULL; head = head->next )
    {
        printf( "%d -> ", head->data );
    }
    
    puts( "null" );
}

void reverse( list **head )
{
    list *current = *head;
    *head = NULL;
        
    while ( current != NULL )
    {
        list *tmp = current;
        current = current->next;
        tmp->next = *head;
        *head = tmp;
    }
}

int main(void) 
{
    const int N = 10;
    
    list *head = NULL;
    
    display( head );
    
    reverse( &head );
    
    display( head );
    
    putchar( '\n' );
    
    for ( int i = 0; i < N; i++ )
    {
        push_front( &head, i );
    }
    
    display( head );
    
    reverse( &head );
    
    display( head );
    
    return 0;
}

The program output is

null
null

9 -> 8 -> 7 -> 6 -> 5 -> 4 -> 3 -> 2 -> 1 -> 0 -> null
0 -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9 -> null

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM