【问题标题】:Reversing a linked list list反转链表列表
【发布时间】:2021-05-21 03:16:29
【问题描述】:

我试图反转一个链接列表,但我一直遇到一个问题。我终于发现是错误的并修复了它,但是我不明白为什么我的最后一种方法不起作用。下面的代码是成功反转链表的代码。

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

}

但是,下面的代码不起作用。两者都使用双指针,但在这一次中,我取消了对 head 的两次引用以获取实际对象并用 *currNode 分配它。当我运行这段代码时,它最终进入一个无限循环并且它丢失了最后一项。例如,如果项目是 1,2,3,4,5,那么反过来就是 5,4,3,2,它会继续打印相同的列表。我不明白为什么这种方法不起作用,因为我正在访问实际对象(通过取消引用两次)并为其分配一个新对象(*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;
    }

}

下面的代码与上面的代码有相同的问题。我采用了与上面代码相​​同的方法,只是这个使用了一个指针。

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

}

任何帮助理解这一点将不胜感激。谢谢!

【问题讨论】:

  • 这是C代码还是C++代码? list 到底是什么?

标签: c linked-list reverse singly-linked-list function-definition


【解决方案1】:

不同级别的间接。

给定list** head*headhead指向的值,一个指向Node的指针,一个list *。我们再次取消引用,**head 取消引用 list *,然后取消引用 list。我们都没有指针,并且正在访问行尾的对象。所以

**head = *currNode;

将两个指针一直解引用回对象。这是为对象分配值,而不是为指向对象的指针分配地址。不是通过更新指针来改变列表的链接,而是将最终指向的list head 更改为匹配currNode,从而破坏了列表的完整性。

*head = currNode;

仅使头部离对象更近一层。这里我们对指针进行操作并改变链接。

在最后一个例子中

*head = *currNode;

这类似于第一次失败的尝试。它是赋值而不是更改指针。

【讨论】:

  • 非常感谢!你的解释很简短,信息量很大。这有助于我准确理解为什么我以前做的事情是错误的。
【解决方案2】:

对于初学者来说,由于这些行,当调用空列表时,所有三个函数通常都可以调用未定义的行为

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

因为这里使用了一个空指针来访问内存。

在这个 if 语句的第二个函数中

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

原来的第一个节点被最后一个节点覆盖

**head = *currNode;

所以在退出函数后,你会发现原来的指针头仍然指向最初的第一个节点,该节点被列表的最后一个节点覆盖。因此,一个实际的数据将会丢失,并且会出现内存泄漏。

至于第三个函数,只要指出该函数按值接受指向头节点的指针即可。

void reverse_list(list* head)

即该函数处理原始变量头的值的副本。更改副本不会影响原始指针。原来的指针仍会指向第一个节点。

函数可以通过以下方式看起来更简单

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

这是一个演示程序。

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

程序输出是

null
null

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

【讨论】:

  • 非常感谢!这确实帮助我理解了问题以及我成功解决方案中的潜在错误!
  • 也感谢您输入的代码。它比我原来的要简洁得多。
猜你喜欢
  • 1970-01-01
  • 2018-02-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-09-29
  • 1970-01-01
相关资源
最近更新 更多