【LeetCode链表#8】翻转链表(双指针+递归)/K个一组翻转
翻转链表
题意:反转一个单链表。
示例: 输入: 1->2->3->4->5->NULL 输出: 5->4->3->2->1->NULL
思路分析
双指针法是本体的最基本的解法,由此还可以改写为递归解法
双指针法

我们需要定义两个指针,pre和cur
初始时,cur指向头节点,pre指向null(pre需要在cur之前,前面又没有节点,那只能是null咯。或者可以这么理解:翻转之后此时pre的位置会变成链表尾部,那链表尾部肯定指向null,所以需要将pre初始化为null)
让cur指向前一个节点
此时为了保存cur的下一个节点,好翻转之后让指针顺利移动到下一位置,我们需要先有一个临时节点temp

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

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

注意:这里先移动cur再移动pre的话会导致pre移动到的并不是之前cur的位置,因为cur的值已经先于pre改变(刻舟求剑懂不懂?)
之后就重复上述步骤即可
那么什么时候结束循环呢?当cur指到原链表的尾部(null)时便可结束,此时pre指向的节点为新的头节点

代码
明确步骤之后代码就很好写了
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;
}
【LeetCode链表#8】翻转链表(双指针+递归)/K个一组翻转的更多相关文章
- leetcode 24. 两两交换链表中的节点 及 25. K 个一组翻转链表
24. 两两交换链表中的节点 问题描述 给定一个链表,两两交换其中相邻的节点,并返回交换后的链表. 你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换. 示例: 给定 1->2-> ...
- [LeetCode] 25. Reverse Nodes in k-Group 每k个一组翻转链表
Given a linked list, reverse the nodes of a linked list k at a time and return its modified list. k ...
- [LeetCode]25. Reverse Nodes in k-Group k个一组翻转链表
Given a linked list, reverse the nodes of a linked list k at a time and return its modified list. k ...
- Java实现 LeetCode 25 K个一组翻转链表
25. K 个一组翻转链表 给你一个链表,每 k 个节点一组进行翻转,请你返回翻转后的链表. k 是一个正整数,它的值小于或等于链表的长度. 如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持 ...
- LeetCode 25 —— K 个一组翻转链表
1. 题目 2. 解答 首先,利用快慢指针确定链表的总结点数. 偶数个结点时,结点个数等于 i * 2. 奇数个结点时,结点个数等于 i * 2 + 1. 然后将链表的每 K 个结点划分为一组.循环对 ...
- leetcode 25. K 个一组翻转链表
# coding:utf-8 __author__ = "sn" """ 25. K 个一组翻转链表 给你一个链表,每 k 个节点一组进行翻转,请你返 ...
- LeetCode 25. K 个一组翻转链表 | Python
25. K 个一组翻转链表 题目来源:https://leetcode-cn.com/problems/reverse-nodes-in-k-group 题目 给你一个链表,每 k 个节点一组进行翻转 ...
- [LintCode] Reverse Nodes in k-Group 每k个一组翻转链表
Given a linked list, reverse the nodes of a linked list k at a time and return its modified list. If ...
- k个一组翻转链表(java实现)
题目: 给出一个链表,每 k 个节点一组进行翻转,并返回翻转后的链表. k 是一个正整数,它的值小于或等于链表的长度.如果节点总数不是 k 的整数倍,那么将最后剩余节点保持原有顺序. 示例 : 给定这 ...
- LeetCoded第25题题解--K个一组翻转链表--java--链表
链表 单链表:链表中的每个元素实际上是一个单独的对象,而所有对象都通过每个元素的引用字段链接在一起. 双链表:与单链表不同的是,双链表的每个节点都含有两个引用字段. 链表优点 灵活分配内存空间 能在O ...
随机推荐
- [转帖]公钥基础设施(PKI,Public Key Infrastructure)闲谈
https://zhuanlan.zhihu.com/p/384436119 背景 在现实空间中,人类的活动范围和接触人的范围有限,人和人最初的信任是建立在小团体或部落内部.随着全球化进展,人类的活动 ...
- Linux 一行命令 仅显示某一个网卡的ip地址
最简答的方法 1. 先使用 ifconfig 查看网卡的设备名 2. 然后输入命令 ifconfig ens192 |grep 'inet ' |cut -d " " -f 10命 ...
- clickhouse 优化实践,万级别QPS数据毫秒写入和亿级别数据秒级返回 | 京东云技术团队
1.背景 魔笛活动平台目前在采集每个活动的用户行为数据并进行查询,解决线上问题定位慢,响应不及时的问题,提升客诉的解决效率.目前每天采集的数据量5000万+,一个月的数据总量15亿+,总数据量40亿+ ...
- vue 路由守卫是否携带token
//整个实例出来 配置路由守卫 const router = new Router({ //这里面是路由配置哈 }) router.beforeEach((to, from, next) => ...
- 去除 i 标签的倾斜样式;如何引入本地的阿里字体图标
去除 i 标签的倾斜样式 i{ font-style:normal; } 如何引入本地的阿里字体图标 将代码下载下来 当然你将下载下载来的资源有用的放在静态资源中 然后在 main.js 引入: ma ...
- Linux命令行从x度网盘下载数据
技术背景 做开源项目的时候,尤其是现在的数据量越来越大,经常会面临到数据往哪里存放的问题.因为自己刚好有一个某度云的会员,看了一下还有几十个TB的空间还没用上.于是考虑把这个网盘变成一个定向共享数据的 ...
- 佳能F-789SGA升级与超频——互联网Tips存档
佳能F-789SGA简介 佳能F-789SGA是一款性价比极高的科学函数计算器,功能与卡西欧fx-991ES近似,稍强于991ES,弱于991CNX. 来自電卓院亜紀良的评价 来自杨高峰的对比总结 来 ...
- vim 从嫌弃到依赖(0)——概述
最近我想开一个新的系列,记录我使用vim的相关心得.初次接触vim是在大学操作系统实践课程中,跟着Linux一块进行学习的.当初我是百般嫌弃它的,想要进行编辑还要按下其他键,我想要移动光标居然还的切换 ...
- TienChin 渠道管理-更新渠道接口开发
ChannelController /** * 修改渠道 */ @PreAuthorize("hasPermission('tienchin:channel:edit')") @L ...
- 通过Demo学WPF—数据绑定(一)✨
前言 想学习WPF,但是看视频教程觉得太耗时间,直接看文档又觉得似懂非懂,因此想通过看Demo代码+文档的方式进行学习. 准备 微软官方其实提供了WPF的一些Demo,地址为:microsoft/WP ...