【LeetCode链表#8】翻转链表(双指针+递归)/K个一组翻转

翻转链表

力扣题目链接(opens new window)

题意:反转一个单链表。

示例: 输入: 1->2->3->4->5->NULL 输出: 5->4->3->2->1->NULL

思路分析

双指针法是本体的最基本的解法,由此还可以改写为递归解法

双指针法
2382229-20230116211511103-1487059764.png

我们需要定义两个指针,pre和cur

初始时,cur指向头节点pre指向null(pre需要在cur之前,前面又没有节点,那只能是null咯。或者可以这么理解:翻转之后此时pre的位置会变成链表尾部,那链表尾部肯定指向null,所以需要将pre初始化为null)

让cur指向前一个节点

此时为了保存cur的下一个节点,好翻转之后让指针顺利移动到下一位置,我们需要先有一个临时节点temp

img

然后先把pre移动到当前cur的位置

img

再根据temp将cur移动到正确的下移位置

img

注意:这里先移动cur再移动pre的话会导致pre移动到的并不是之前cur的位置,因为cur的值已经先于pre改变(刻舟求剑懂不懂?)

之后就重复上述步骤即可

那么什么时候结束循环呢?当cur指到原链表的尾部(null)时便可结束,此时pre指向的节点为新的头节点

img
代码

明确步骤之后代码就很好写了

class Solution {
    public ListNode reverseList(ListNode head) {
        //双指针法
        //初始化两个指针
        ListNode cur = head;
        ListNode pre = null;
        //定义用于存放cur下一节点的临时节点temp
        ListNode temp;
        while(cur != null){//从旧的头节点开始遍历,到翻转前的链表尾部(也就是null)结束
            temp = cur.next;
            cur.next = pre;
            //注意,cur翻转完之后一定先让pre按照当前cur的值移动到指定位置,再让cur更新为temp
            //不然cur先动了pre就找不到之前cur的位置了
            pre = cur;
            cur = temp;
        }
        return pre;//翻转结束,此时pre指向的就是新的头节点
    }
}
c++版

思路一致,代码对应着写就可以

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        //双指针法
        ListNode* pre = nullptr;
        ListNode* cur = head;
        while(cur != nullptr){
            //保存cur的下一节点
            ListNode* temp = cur->next;
            //交换
            cur->next = pre;
            //一定要先移动pre
            pre = cur;
            cur = temp;
        }
        return pre;//此时的pre为新链表的head
        
    }
};
递归法

递归法主要是针对双指针法在代码层面上的一个优化,原理层面与双指针法一致

因此递归法的代码可以与双指针法的一一对应

代码

LeetCode上解题模板中,Solution类给了一个主方法reverseList

那么根据递归的写法,我们还要写一个reverse方法,让reverseList去调用它来翻转链表

class Solution {
    public ListNode reverse(ListNode pre, ListNode cur){
        
    }
    
    public ListNode reverseList(ListNode head) {
        reverse();
        
    }
}

reverseList传入的参数是head,那么reverse需要的两个参数怎么传?

参考双指针法的初始化部分,reverse中的两个参数也要对应进行初始化

即:

class Solution {
    public ListNode reverse(ListNode pre, ListNode cur){
        
    }
    
    public ListNode reverseList(ListNode head) {
        reverse(null, head);//与双指针法对应
        
    }
}

接下来按照双指针思路把reverse的功能完善即可,完整代码如下:

class Solution {
    public ListNode reverse(ListNode cur, ListNode pre){
        ListNode temp;
        if(cur == null){
            return pre;
        }
        temp = cur.next;//保存cur.next
        cur.next = pre;//翻转操作
        //接下来要让pre和cur整体向后移动一位,在递归里应该直接用return来实现,下面代码对应于
        //pre = cur;
        //cur = temp;
        return reverse(temp, cur);//进入下一层递归
        
    }
    
    public ListNode reverseList(ListNode head) {
        return reverse(head, null);//与双指针法对应 
    }
}
c++版

c++的解题模板如下:

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        
    }
};

要是用递归的话,要再写一个成员函数reverse,该函数的返回类型仍然是ListNode*

class Solution {
public:
    ListNode* reverse(ListNode* pre, ListNode* cur) {
        
    }
    
    
    ListNode* reverseList(ListNode* head) {
        
    }
};

代码如下

class Solution {
public:
    ListNode* reverse(ListNode* cur, ListNode* pre) {
        //这里就不好用while了因为要递归
        if(cur == nullptr){
            return pre;
        }
        ListNode* temp = cur->next;
        cur->next = pre;
        //接下来要用return实现以下代码
        // pre = cur;
        // cur = temp;
        //从而达到递归
        return reverse(temp, cur);//对着要实现的代码来填就行了
    }

    ListNode* reverseList(ListNode* head) {
        //这里要实现双指针法中的
        // ListNode* pre = nullptr;
        // ListNode* cur = head;
        //部分代码
        return reverse(head,nullptr);//对应这填即可
    }
};

二刷易错点

1、节点翻转环节的顺序

当使用临时节点保存cur的下一节点并将cur指向pre后,应该先将pre移动到cur处,再移动cur

要不然会乱

2、递归实现

递归版本完全可以根据双指针版本写出来,因此记住双指针版本的实现原理即可

需要注意,在递归实现中,不要使用while

K个一组翻转链表

https://leetcode.cn/problems/reverse-nodes-in-k-group/description/

给你链表的头节点 head ,每 k 个节点一组进行翻转,请你返回修改后的链表。

k 是一个正整数,它的值小于或等于链表的长度。如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。

你不能只是单纯的改变节点内部的值,而是需要实际进行节点交换。

思路

这里需要控制翻转区间,也就是每k个范围翻转一次

如果使用之前的迭代的方式,区间控制逻辑会变得很复杂,很容易出错(双层for加一堆变量),所以这里换用递归的方式来做

具体就是我们先定义一个指针post,指向head,然后遍历k步,此时就有了区间[head, post],该区间长度为k

然后我们还是定义pre和cur,将区间内的节点全部翻转

翻转结束后,cur、post以及翻转前的head节点会重叠到一起,此时我们需要继续翻转下一个k区间,也就是说我们要找head->next

因此,调用递归函数继续重复之前的操作:移动post确定区间,使用pre和cur翻转区间

最后,post为空,返回此时的pre即可。

代码

写法上遵守递归的三步走

class Solution {
public:
    ListNode* reverseKGroup(ListNode* head, int k) {
        ListNode* post = head; //用于标定翻转区间
        for(int i = 0; i < k; ++i){
            if(post == nullptr) return head;//终止条件
            post = post->next;
        }
        ListNode* pre = nullptr;
        ListNode* cur = head;
        while(cur != post){//翻转区间内的节点
            ListNode* tmp = cur->next;
            cur->next = pre;
            pre = cur;
            cur = tmp;
        }
        //区间翻转完成,此时的head又被移动(交换)到了post处
        //之后就调用递归翻转下一个k区间
        //head不能动,所以输入递归函数的应该是cur
        head->next = reverseKGroup(cur, k);
        return pre;//与常规翻转一样,最后pre会指向链表头部,cur会指向空
    }
};

ACM模式

#include <iostream>

using namespace std;

struct ListNode {
    int val;
    ListNode* next;
    ListNode(int x) : val(x), next(NULL) {}
};

class Solution {
public:
    ListNode* reverseKGroup(ListNode* head, int k) {
        ListNode* post = head; //用于标定翻转区间
        for (int i = 0; i < k; ++i) {
            if (post == nullptr) return head;//终止条件
            post = post->next;
        }
        ListNode* pre = nullptr;
        ListNode* cur = head;
        while (cur != post) {//翻转区间内的节点
            ListNode* tmp = cur->next;
            cur->next = pre;
            pre = cur;
            cur = tmp;
        }
        //区间翻转完成,此时的head又被移动(交换)到了post处
        //之后就调用递归翻转下一个k区间
        //head不能动,所以输入递归函数的应该是cur
        head->next = reverseKGroup(cur, k);
        return pre;//与常规翻转一样,最后pre会指向链表头部,cur会指向空
    }
};

int main() {
    ListNode* head = new ListNode(1);
    head->next = new ListNode(2);
    head->next->next = new ListNode(3);
    head->next->next->next = new ListNode(4);
    head->next->next->next->next = new ListNode(5);
    int k = 2;
    Solution solution;
    ListNode* res = solution.reverseKGroup(head, k);
    while (res) {
        cout << res->val << ' ';
        res = res->next;
    }
    return 0;
}
posted @ 2023-01-16 21:17  dayceng  阅读(34)  评论(0编辑  收藏  举报