K 个一组翻转链表(链表反转,固定长度反转)(困难)
优质博文:IT-BLOG-CN
一、题目
给你链表的头节点head,每k个节点一组进行翻转,请你返回修改后的链表。
k是一个正整数,它的值小于或等于链表的长度。如果节点总数不是k的整数倍,那么请将最后剩余的节点保持原有顺序。
你不能只是单纯的改变节点内部的值,而是需要实际进行节点交换。
示例1:

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

输入:head = [1,2,3,4,5], k = 3
 输出:[3,2,1,4,5]
二、代码
【1】先实现链表的反转功能
/*** Definition for singly-linked list.* public class ListNode {*     int val;*     ListNode next;*     ListNode() {}*     ListNode(int val) { this.val = val; }*     ListNode(int val, ListNode next) { this.val = val; this.next = next; }* }*/
class Solution {public ListNode reverseKGroup(ListNode head) {// 1、第一个考查点:反转链表ListNode pre = null;ListNode cur = head;// 用户暂时保存next的值;ListNode nxt = null;// 遍历链表进行翻转while(cur != null) {nxt = cur.next;cur.next = pre;pre = cur;cur = nxt;}// 在原链表上看,pre指向tail节点,cur指向pre下一个节点return pre;}
}
 
【2】实现指定长度数据的反转
/*** Definition for singly-linked list.* public class ListNode {*     int val;*     ListNode next;*     ListNode() {}*     ListNode(int val) { this.val = val; }*     ListNode(int val, ListNode next) { this.val = val; this.next = next; }* }*/
class Solution {public ListNode reverseKGroup(ListNode head, int left, int right) {// 主要作用:保留开始反转节点的上一个节点ListNode headPre = new ListNode(0, head);// 后面会不断更新,直至需要反转ListNode p0 = headPre;// 先遍历不反转的部分for (int i = 1; i < left; i++) {p0 = p0.next;}// 1、第一个考查点:反转链表ListNode pre = null;// 这里不再指向头节点,指向开始反转的节点ListNode cur = p0.next;// 用户暂时保存next的值;ListNode nxt = null;// 遍历链表进行翻转for (int i = 0; i < right - left + 1; i++) {if ( cur != null ) {nxt = cur.next;cur.next = pre;pre = cur;cur = nxt;}}// 在原链表上看,pre指向tail节点,cur指向pre下一个节点// 将 pre节点放入 p0的next节点p0.next.next = cur;p0.next = pre;return headPre;}
}
 
【3】实现k位反转,不足k位不反转
/*** Definition for singly-linked list.* public class ListNode {*     int val;*     ListNode next;*     ListNode() {}*     ListNode(int val) { this.val = val; }*     ListNode(int val, ListNode next) { this.val = val; this.next = next; }* }*/
class Solution {public ListNode reverseKGroup(ListNode head, int k) {// 1、计算中记录数ListNode countList = head;int count = 0;while(countList != null) {count++;countList = countList.next;}// 主要作用:保留开始反转节点的上一个节点ListNode dummp = new ListNode(0, head);ListNode p0 = dummp;// 2、第一个考查点:反转链表while (k <= count) {// 循环推出条件count -= k;ListNode pre = null;ListNode cur = p0.next;// 遍历链表进行翻转for(int i = 0; i<k; i++) {// 用户暂时保存next的值;ListNode  nxt = cur.next;cur.next = pre;pre = cur;cur = nxt;}// 3、倒序后重新串联ListNode p0Next = p0.next;p0.next.next = cur;p0.next = pre;p0 = p0Next;}// 在原链表上看,pre指向tail节点,cur指向pre下一个节点return dummp.next;}
}
 
说明:自己尝试画图理解,否则不容易理解,附视频讲解
【3】模拟: 本题的目标非常清晰易懂,不涉及复杂的算法,但是实现过程中需要考虑的细节比较多,容易写出冗长的代码。主要考查面试者设计的能力。
我们需要把链表节点按照k个一组分组,所以可以使用一个指针head依次指向每组的头节点。这个指针每次向前移动k步,直至链表结尾。对于每个分组,我们先判断它的长度是否大于等于k。若是,我们就翻转这部分链表,否则不需要翻转。
接下来的问题就是如何翻转一个分组内的子链表但是对于一个子链表,除了翻转其本身之外,还需要将子链表的头部与上一个子链表连接,以及子链表的尾部与下一个子链表连接
因此,在翻转子链表的时候,我们不仅需要子链表头节点head,还需要有head的上一个节点pre,以便翻转完后把子链表再接回pre。
但是对于第一个子链表,它的头节点head前面是没有节点pre的。太麻烦了!难道只能特判了吗?答案是否定的。没有条件,我们就创造条件;没有节点,我们就创建一个节点。我们新建一个节点,把它接到链表的头部,让它作为pre的初始值,这样head前面就有了一个节点,我们就可以避开链表头部的边界条件。这么做还有一个好处,下面我们会看到。
反复移动指针head与pre,对head所指向的子链表进行翻转,直到结尾,我们就得到了答案。下面我们该返回函数值了。
有的同学可能发现这又是一件麻烦事:链表翻转之后,链表的头节点发生了变化,那么应该返回哪个节点呢?照理来说,前k个节点翻转之后,链表的头节点应该是第k个节点。那么要在遍历过程中记录第k个节点吗?但是如果链表里面没有k个节点,答案又还是原来的头节点。我们又多了一大堆循环和判断要写,太崩溃了!
等等!还记得我们创建了节点pre吗?这个节点一开始被连接到了头节点的前面,而无论之后链表有没有翻转,它的next指针都会指向正确的头节点。那么我们只要返回它的下一个节点就好了。至此,问题解决。
class Solution {public ListNode reverseKGroup(ListNode head, int k) {ListNode hair = new ListNode(0);hair.next = head;ListNode pre = hair;while (head != null) {ListNode tail = pre;// 查看剩余部分长度是否大于等于 kfor (int i = 0; i < k; ++i) {tail = tail.next;if (tail == null) {return hair.next;}}ListNode nex = tail.next;ListNode[] reverse = myReverse(head, tail);head = reverse[0];tail = reverse[1];// 把子链表重新接回原链表pre.next = head;tail.next = nex;pre = tail;head = tail.next;}return hair.next;}public ListNode[] myReverse(ListNode head, ListNode tail) {ListNode prev = tail.next;ListNode p = head;while (prev != tail) {ListNode nex = p.next;p.next = prev;prev = p;p = nex;}return new ListNode[]{tail, head};}
}
 
时间复杂度: O(n),其中n为链表的长度。head指针会在O(⌊nk⌋)个节点上停留,每次停留需要进行一次O(k)的翻转操作。
 空间复杂度: O(1),我们只需要建立常数个变量。
**【4】栈:**我们把k个数压入栈中,然后弹出来的顺序就是翻转的!剩下的链表个数够不够k个(因为不够k个不用翻转);已经翻转的部分要与剩下链表连接起来。
/*** Definition for singly-linked list.* public class ListNode {*     int val;*     ListNode next;*     ListNode(int x) { val = x; }* }*/
class Solution {public ListNode reverseKGroup(ListNode head, int k) {Deque<ListNode> stack = new ArrayDeque<ListNode>();ListNode dummy = new ListNode(0);ListNode p = dummy;while (true) {int count = 0;ListNode tmp = head;while (tmp != null && count < k) {stack.add(tmp);tmp = tmp.next;count++;}if (count != k) {p.next = head;break;}while (!stack.isEmpty()){p.next = stack.pollLast();p = p.next;}p.next = tmp;head = tmp;}return dummy.next;}
}
 
尾插法
/*** Definition for singly-linked list.* public class ListNode {*     int val;*     ListNode next;*     ListNode(int x) { val = x; }* }*/
class Solution {public ListNode reverseKGroup(ListNode head, int k) {ListNode dummy = new ListNode(0);dummy.next = head;ListNode pre = dummy;ListNode tail = dummy;while (true) {int count = 0;while (tail != null && count != k) {count++;tail = tail.next;}if (tail == null) break;ListNode head1 = pre.next;while (pre.next != tail) {ListNode cur = pre.next;pre.next = cur.next;cur.next = tail.next;tail.next = cur;}pre = head1;tail = head1;}return dummy.next;}
}
 
递归
/*** Definition for singly-linked list.* public class ListNode {*     int val;*     ListNode next;*     ListNode(int x) { val = x; }* }*/
class Solution {public ListNode reverseKGroup(ListNode head, int k) {ListNode cur = head;int count = 0;while (cur != null && count != k) {cur = cur.next;count++;}if (count == k) {cur = reverseKGroup(cur, k);while (count != 0) {count--;ListNode tmp = head.next;head.next = cur;cur = head;head = tmp;}head = cur;}return head;}
相关文章:
K 个一组翻转链表(链表反转,固定长度反转)(困难)
优质博文:IT-BLOG-CN 一、题目 给你链表的头节点head,每k个节点一组进行翻转,请你返回修改后的链表。 k是一个正整数,它的值小于或等于链表的长度。如果节点总数不是k的整数倍,那么请将最后剩余的节点保持原有顺序。…...
Spring Boot - 利用Resilience4j-RateLimiter进行流量控制和服务降级
文章目录 Resilience4j概述Resilience4j官方地址Resilience4j-RateLimiter微服务演示Payment processorPOM配置文件ServiceController Payment servicePOMModelServiceRestConfigController配置验证 探究 Rate Limiting请求三次 ,观察等待15秒连续访问6次 Resilienc…...
概率论与数理统计————1.随机事件与概率
一、随机事件 随机试验:满足三个特点 (1)可重复性:可在相同的条件下重复进行 (2)可预知性:每次试验的可能不止一个,事先知道试验的所有可能结果 (3)不确定…...
【生存技能】git操作
先下载git https://git-scm.com/downloads 我这里是win64,下载了相应的直接安装版本 64-bit Git for Windows Setup 打开git bash 设置用户名和邮箱 查看设置的配置信息 获取本地仓库 在git bash或powershell执行git init,初始化当前目录成为git仓库…...
docker 将镜像打包为 tar 包
目录 1 实现 1 实现 要将镜像导出为.tar包,可以使用Docker命令行工具进行操作。下面是导出镜像的步骤: 首先,使用以下命令列出当前系统上的镜像,并找到要导出的镜像的ID或名称: docker images使用以下命令将镜像导出为…...
341. 最优贸易(dp思想运用,spfa,最短路)
341. 最优贸易 - AcWing题库 C 国有 n 个大城市和 m 条道路,每条道路连接这 n 个城市中的某两个城市。 任意两个城市之间最多只有一条道路直接相连。 这 m 条道路中有一部分为单向通行的道路,一部分为双向通行的道路,双向通行的道路在统计…...
FineBI实战项目一(19):每小时订单笔数分析开发
点击新建组件,创建下每小时订单笔数组件。 选择饼图,拖拽cnt(总数)到角度,拖拽hourstr到颜色,调节内径。 修改现在的文字 拖拽组件到仪表盘。 效果如下:...
What is `@RequestBody ` does?
RequestBody 是SpringMVC框架中的注解,通常与POST、PUT等方法配合使用。当客户端发送包含JSON或XML格式数据的请求时,可以通过该注解将请求体内容绑定到Controller方法参数上 作用 自动反序列化: SpringMVC会根据RequestBody注解的参数类型&…...
Windows安装Rust环境(详细教程)
一、 安装mingw64(C语言环境) Rust默认使用的C语言依赖Visual Studio,但该工具占用空间大安装也较为麻烦,可以选用轻便的mingw64包。 1.1 安装地址 (1) 下载地址1-GitHub:Releases niXman/mingw-builds-binaries GitHub (2) 下载地址2-W…...
Marin说PCB之传输线损耗---趋肤效应和导体损耗01
大家在做RF上的PCB走线或者是车载相机的上走线的时候经常会听那些硬件工程师们说你这个走线一定要保证50欧姆的阻抗匹配啊,还有就是记得加粗走做隔层参考。 有的公司的EE硬件同事会很贴心的把RF走线的注意事项给你备注在原理图上或者是layoutguide上,遇到…...
八:分布式锁
1、为什么要使用分布式锁 锁是多线程代码中的概念,只有多任务访问同一个互斥的共享资源时才需要锁。单机应用开发时一般使用synchronized或lock。多线程的运行都是在同一个JVM之下。应用是分布式集群,属于多JVM的工作环境,JVM之间已经无法通过…...
示例:php将文本内容写入一个文件(面向过程写法)
一、封装2个函数,读写文件 /*** desc 读取文件内容* param string $filename* return array*/ private function readContent(string $filename): array {$text file_get_contents($filename);if (!$text) {return [];}$result json_decode($text,true);return…...
Flutter开发进阶之并发操作数据库
Flutter开发进阶之并发操作数据库 尽管 Flutter 本身不包含任何数据库功能,但可以使用各种第三方库和插件来在 Flutter 应用程序中实现数据库功能; 以下将使用sqflite作为例子,sqflite允许在 Flutter 应用程序中执行 SQL 查询,创…...
docker应用:搭建uptime-kuma监控站点
简介:Uptime Kuma是一个易于使用的自托管监控工具,它的界面干净简洁,部署和使用都非常方便。 历史攻略: docker:可视化工具portainer docker-compose:搭建自动化运维平台Spug 开源地址: ht…...
在illustrator中按大小尺寸选择物体 <脚本 018>
在Illustrator中我们可以依据对象的属性 如:填充颜色、描边颜色或描边宽度来选择相同属性的对象,但是Illustrator中没有根据不同大小尺寸来选择对象的功能,下面介绍的就是根据大小尺寸选择对象的脚本。 1、下面是当前画板中的所有对象&#…...
leetcode - 934. Shortest Bridge
Description You are given an n x n binary matrix grid where 1 represents land and 0 represents water. An island is a 4-directionally connected group of 1’s not connected to any other 1’s. There are exactly two islands in grid. You may change 0’s to 1…...
k8s的存储卷、数据卷
容器内的目录和宿主机目录进行挂载。 容器在系统上的生命周期是短暂的。 k8s用控制器创建的pod。delete相当于重启。容器的状态也会恢复到初始状态。一旦恢复到初始状态,所有的后天编辑的文件都会消失 容器和节点之间创建一个可以持久化保存容器内文件的存储卷。…...
流星全自动网页生成系统重构版源码
流星全自动网页生成系统重构版源码分享,所有模板经过精心审核与修改,完美兼容小屏手机大屏手机,以及各种平板端、电脑端和360浏览器、谷歌浏览器、火狐浏览器等等各大浏览器显示。 为用户使用方便考虑,全自动网页制作系统无需繁琐…...
vscode打开c_cpp_properties.json文件的一种方式
步骤一 点击win32 步骤二 点击json 自动生成了...
发起人自选-钉钉审批
场景描述 配置一个审批流程,在某些审批节点,不能确定谁具体来审批,所以需要手工选择一个人或者多个人保证流程能得以顺利通过。有些审批流程的做法是,上一个节点来选择指定的人,而钉钉的做法是发起人来指定。 钉钉设…...
Chapter03-Authentication vulnerabilities
文章目录 1. 身份验证简介1.1 What is authentication1.2 difference between authentication and authorization1.3 身份验证机制失效的原因1.4 身份验证机制失效的影响 2. 基于登录功能的漏洞2.1 密码爆破2.2 用户名枚举2.3 有缺陷的暴力破解防护2.3.1 如果用户登录尝试失败次…...
设计模式和设计原则回顾
设计模式和设计原则回顾 23种设计模式是设计原则的完美体现,设计原则设计原则是设计模式的理论基石, 设计模式 在经典的设计模式分类中(如《设计模式:可复用面向对象软件的基础》一书中),总共有23种设计模式,分为三大类: 一、创建型模式(5种) 1. 单例模式(Sing…...
【ROS】Nav2源码之nav2_behavior_tree-行为树节点列表
1、行为树节点分类 在 Nav2(Navigation2)的行为树框架中,行为树节点插件按照功能分为 Action(动作节点)、Condition(条件节点)、Control(控制节点) 和 Decorator(装饰节点) 四类。 1.1 动作节点 Action 执行具体的机器人操作或任务,直接与硬件、传感器或外部系统…...
Cinnamon修改面板小工具图标
Cinnamon开始菜单-CSDN博客 设置模块都是做好的,比GNOME简单得多! 在 applet.js 里增加 const Settings imports.ui.settings;this.settings new Settings.AppletSettings(this, HTYMenusonichy, instance_id); this.settings.bind(menu-icon, menu…...
k8s业务程序联调工具-KtConnect
概述 原理 工具作用是建立了一个从本地到集群的单向VPN,根据VPN原理,打通两个内网必然需要借助一个公共中继节点,ktconnect工具巧妙的利用k8s原生的portforward能力,简化了建立连接的过程,apiserver间接起到了中继节…...
JavaScript基础-API 和 Web API
在学习JavaScript的过程中,理解API(应用程序接口)和Web API的概念及其应用是非常重要的。这些工具极大地扩展了JavaScript的功能,使得开发者能够创建出功能丰富、交互性强的Web应用程序。本文将深入探讨JavaScript中的API与Web AP…...
云原生安全实战:API网关Kong的鉴权与限流详解
🔥「炎码工坊」技术弹药已装填! 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 一、基础概念 1. API网关(API Gateway) API网关是微服务架构中的核心组件,负责统一管理所有API的流量入口。它像一座…...
群晖NAS如何在虚拟机创建飞牛NAS
套件中心下载安装Virtual Machine Manager 创建虚拟机 配置虚拟机 飞牛官网下载 https://iso.liveupdate.fnnas.com/x86_64/trim/fnos-0.9.2-863.iso 群晖NAS如何在虚拟机创建飞牛NAS - 个人信息分享...
五子棋测试用例
一.项目背景 1.1 项目简介 传统棋类文化的推广 五子棋是一种古老的棋类游戏,有着深厚的文化底蕴。通过将五子棋制作成网页游戏,可以让更多的人了解和接触到这一传统棋类文化。无论是国内还是国外的玩家,都可以通过网页五子棋感受到东方棋类…...
如何做好一份技术文档?从规划到实践的完整指南
如何做好一份技术文档?从规划到实践的完整指南 🌟 嗨,我是IRpickstars! 🌌 总有一行代码,能点亮万千星辰。 🔍 在技术的宇宙中,我愿做永不停歇的探索者。 ✨ 用代码丈量世界&…...
