反转链表的三种方法

数据结构学习日记250902

迭代法反转链表

思路:逐一遍历,记录curr指针的前后位,将curr的next改为指向前节点,完成修改后更新curr为记录的后节点,实现curr的移动。
注意:curr在为空时停止遍历,此时的curr的前置节点才是新的头节点(因为链表已经反转)

/*
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* reverseList(ListNode* head) {
	//排除头节点为空
        if(head==nullptr){
            return nullptr;
        }
        ListNode*pre=nullptr;
        ListNode*curr=head;
        while(curr){
	//tmp为curr的后置节点
            ListNode*tmp=curr->next;
            curr->next=pre;
            pre=curr;
            curr=tmp;
        }
	//注意更新头节点
        head=pre;
        return head;
    }
};

递归法反转

为什么可以使用递归??

我们将函数的参数设置为head->next;每递归一层,相当于向下遍历一个节点,在此过程中,前面节点的信息被存到了堆栈中!!便于后续回溯中利用上。
那么递归出口显然:head走到链表的最后一个节点,即head->next==nullptr。考虑到空链表可能会导致这里内存错误,因为head为空。完整的递归条件应该为:head==nullptr||head->next==nullptr

两个注意点

1.完成链表反转后,怎么找到头节点?将原链表的尾节点一直作为返回值回传
2.首次触发return时,head是哪个节点?倒数第二个。此时已经回溯一层。

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
       if(head==nullptr||head->next==nullptr){
           //第一次触发回溯的位置,空链表退出,或者是走到链表尾部退出
           //这里返回的是链表的尾节点,尾节点需要一直保留,保证最后能正确给出反转后的链表头节点
           return head;
       }
        ListNode*newHead=reverseList(head->next);
        //head实际上是倒数第二个节点
        head->next->next=head;
        head->next=nullptr;
        return newHead;
    }
};

反转的核心逻辑

	head->next->next=head;
        head->next=nullptr;

这里以第一次回溯为例,将尾节点的next指向回溯得到的倒数第二个节点(head),将倒数第二个节点的后继置空!!没有这一步会导致原链表的头节点,也就是反转后链表的尾节点的next异常。

头插法反转链表

反转逻辑:将待反转区的前置节点prev保留,head记为待反转区的首个节点。
将head后的每个节点依次插入到prev的后方,即可实现链表的反转。
head.pic

例题:leetcode92
为了保证当需要反转头节点时能够正确的反转,使用虚拟头节点dummy。与此同时反转后的链表的头节点也就变成了dummy->next

class Solution {
public:
    ListNode* reverseBetween(ListNode* head, int left, int right) {
        // 采用头插法实现一次遍历
        ListNode* dummy = new ListNode(0);
        dummy->next = head;
        ListNode* prev = dummy;
        ListNode* curr = head;
        // 走到需要反转的前一个节点
        for (int i = 0; i < left - 1; ++i) {
            prev=prev->next;
        }
	//此时的curr为待反转区域的首个节点
        curr=prev->next;
	//循环的次数为需要反转的链表节点个数
	//每循环一次就可以将一个节点插入到prev的后方
        for(int i=0;i<right-left;++i){
            ListNode*nextNode=curr->next;
            curr->next=nextNode->next;
            nextNode->next=prev->next;
            prev->next=nextNode;
        }
        return dummy->next;
    }
};
posted @ 2025-09-03 08:12  JiaqiShan07  阅读(72)  评论(0)    收藏  举报