zl程序教程

您现在的位置是:首页 >  其他

当前栏目

代码随想录刷题-链表-反转链表

2023-04-18 14:25:53 时间

反转链表

本节对应代码随想录中:代码随想录,讲解视频:帮你拿下反转链表 | LeetCode:206.反转链表 | 双指针法 | 递归法_哔哩哔哩_bilibili

习题

题目链接:206. 反转链表 - 力扣(LeetCode)

给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

示例:
输入:head = [1,2,3,4,5]
输出:[5,4,3,2,1]

双指针

例如,上面的示例,我们想反转 1 2 3 4 5。用一个指针 cur 用来遍历单链表,再用另一个指针 pre 指向 cur 的前面一个节点。例如 cur=2时,原本是1->2,现在要变成2->1。直接让 cur->next=pre 就可以实现,但这样的话,后面的3就没法遍历到了。所以要用一个临时指针 temp 先指向 cur->next,再让 cur->next=pre。

到这里,就实现了1和2的反转,如果想继续遍历下去,还需让 pre 和 cur 都向后移一位。现在 pre、cur 和 temp 的位置为:pre cur temp,要让 pre 和 cur 向后移一位,只要让 pre=cur,cur=temp 即可。注意这两个顺序不能颠倒,否则 cur 先等于 temp,那就相当于丢失了原来的 cur。

class Solution {
   public:
    ListNode* reverseList(ListNode* head) {
        ListNode* cur = head;
        ListNode* pre = nullptr;
        ListNode* tmp;
        while (cur != nullptr) {
            tmp = cur->next;
            cur->next = pre;
            pre = cur;
            cur = tmp;
        }
        return pre;
    }
};

递归

首先给出代码随想录上类似双指针的递归。新定义了一个函数,将初始化的步骤更改为传参给新定义的函数来实现,而将两个指针后移的步骤更改为调用函数递归来实现。同时,和双指针的结束条件一样,cur == NULL 时递归函数结束。

class Solution {
public:
    ListNode* reverse(ListNode* pre,ListNode* cur){
        if(cur == NULL) return pre;
        ListNode* temp = cur->next;
        cur->next = pre;
        // 可以和双指针法的代码进行对比,如下递归的写法,其实就是做了这两步
        // pre = cur;
        // cur = temp;
        return reverse(cur,temp);
    }
    ListNode* reverseList(ListNode* head) {
        // 和双指针法初始化是一样的逻辑
        // ListNode* cur = head;
        // ListNode* pre = NULL;
        return reverse(NULL, head);
    }

};

上面的递归由于和双指针的写法类似,还较为容易理解,而LeetCode 官方给出了更简洁的递归。

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        if (!head || !head->next) {
            return head;
        }
        ListNode* newHead = reverseList(head->next);
        head->next->next = head;
        head->next = nullptr;
        return newHead;
    }
};

评论区有人给出了详细的注释(java 版本)。对于 head->next->next = head;,如 1 2 3,head=2,head->next=3,3->next=2,相当于2<->3。但是反转后2的 next 不应该再指向3了,因此 head->next = nullptr; 将2的 next 置为 nullptr。

public ListNode reverseList(ListNode head) {
        if (head == null || head.next == null) {
            /*
                直到当前节点的下一个节点为空时返回当前节点
                由于5没有下一个节点了,所以此处返回节点5
             */
            return head;
        }
        //递归传入下一个节点,目的是为了到达最后一个节点
        ListNode newHead = reverseList(head.next);
                /*
            第一轮出栈,head为5,head.next为空,返回5
            第二轮出栈,head为4,head.next为5,执行head.next.next=head也就是5.next=4,
                      把当前节点的子节点的子节点指向当前节点
                      此时链表为1->2->3->4<->5,由于4与5互相指向,所以此处要断开4.next=null
                      此时链表为1->2->3->4<-5
                      返回节点5
            第三轮出栈,head为3,head.next为4,执行head.next.next=head也就是4.next=3,
                      此时链表为1->2->3<->4<-5,由于3与4互相指向,所以此处要断开3.next=null
                      此时链表为1->2->3<-4<-5
                      返回节点5
            第四轮出栈,head为2,head.next为3,执行head.next.next=head也就是3.next=2,
                      此时链表为1->2<->3<-4<-5,由于2与3互相指向,所以此处要断开2.next=null
                      此时链表为1->2<-3<-4<-5
                      返回节点5
            第五轮出栈,head为1,head.next为2,执行head.next.next=head也就是2.next=1,
                      此时链表为1<->2<-3<-4<-5,由于1与2互相指向,所以此处要断开1.next=null
                      此时链表为1<-2<-3<-4<-5
                      返回节点5
            出栈完成,最终头节点5->4->3->2->1
         */
        head.next.next = head;
        head.next = null;
        return newHead;
    }

如果还是觉得有点抽象不太好理解,可以看下这个视频讲解:链表反转(递归法)_哔哩哔哩_bilibili