力扣25(java&python)-K 个一组翻转链表(困难)
题目:
给你链表的头节点 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]
提示:
- 链表中的节点数目为
n
1 <= k <= n <= 5000
0 <= Node.val <= 1000
进阶:你可以设计一个只用 O(1)
额外内存空间的算法解决此问题吗?
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/reverse-nodes-in-k-group
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
解题思路:
一、模拟
参考 @康建斌学算法 大佬的解题思路,同时感谢评论区的各位提的疑问以及注解~
1.设置虚拟头结点,指向原始头结点将头结点当做普通结点来对待,减少特殊判断,将链表分区为已翻转部分+待翻转部分+未翻转部分;
2.定义两个指针,pre:每次要翻转链表头结点的前一个结点,end:待翻转链表的尾结点,让其初始都指向虚拟结点;
3.对链表进行遍历,首先通过给定的k来循环,找到第一次需要翻转的链表的范围;
4.经过k次循环后,end到达本次待翻转链表的尾结点,需要保存尾结点的下一个结点:ListNode next = end.next,找到待翻转链表的开始结点: ListNode start = pre.next,并断开end与next的连接:end.next = null;
5.对[start,end]区间的结点进行翻转,将这个待翻转的子链表进行操作:
- 定义三个指针,preNode:前一个结点指针,currNode:当前结点指针,nextNode:下一个结点指针;
- 然后对待翻转的结点进行循环,循环结束的条件是:当 当前结点为空:
- 先保存当前结点后面链表: nextNode = currNode.next;
-
- 将当前结点指向preNode: currNode.next = preNode;
-
- 更新preNoded位置,preNode向后移动指向当前结点:preNode = currNode;更新curNode位置,curNode向后移动,下一个结点变成当前结点:currNode = nextNode;
-
- 最后实现全部翻转,currNode为空,返回这时的preNode
5.将pre.next指向翻转后的链表:pre.next = reverse(start);并将已翻转的部分和待翻转的部分连接,尾结点指向它的下一个结点:

6.一直重复上述步骤,直到end为null时,结束遍历,最终返回 dummyHead.next。
java代码:
1 /** 2 * Definition for singly-linked list. 3 * public class ListNode { 4 * int val; 5 * ListNode next; 6 * ListNode() {} 7 * ListNode(int val) { this.val = val; } 8 * ListNode(int val, ListNode next) { this.val = val; this.next = next; } 9 * } 10 */ 11 class Solution { 12 public ListNode reverseKGroup(ListNode head, int k) { 13 //定义一个虚拟结点并指向头结点 14 ListNode dummyHead = new ListNode(0,head); 15 //定义两个指针初始都指向虚拟结点 16 //pre:每次要翻转链表头结点的前一个结点 17 //end:要翻转链表的尾结点 18 ListNode pre = dummyHead; 19 ListNode end = dummyHead; 20 //当到达尾结点的时候退出while循环 21 //找到end的位置 22 while(end.next != null){ 23 //end != null:防止end.next为空报错 24 for(int i = 0; i < k && end != null; i++){ 25 end = end.next; 26 } 27 //当不足k倍时,直接退出不进行翻转 28 if(end == null) break; 29 30 //翻转的开始结点 31 ListNode start = pre.next; 32 //保存end的下一个结点 33 ListNode next = end.next; 34 //断开end和下一个结点的连接 35 end.next = null; 36 //将pre.next指向翻转后的链表 37 pre.next = reverse(start); 38 //将尾结点指向end的下一个结点 39 start.next = next; 40 //将pre换成下一次翻转链表头结点的前一个结点 41 pre = start; 42 //将end也要换成下一次翻转链表头结点的前一个结点 43 end = start; 44 } 45 //最后返回虚拟头结点的下一个结点=真正的头结点 46 return dummyHead.next; 47 } 48 public ListNode reverse(ListNode head){ 49 //当子链表为空或者只有单结点时,直接返回原表 50 if(head == null || head.next == null) return head; 51 //定义三个指针 52 //preNode前一个结点指针 53 //currNode当前结点指针 54 //nextNode下一个结点指针 55 ListNode preNode = null; 56 ListNode currNode = head; 57 ListNode nextNode = null; 58 //循环结束条件是当当前结点为空 59 while(currNode != null){ 60 //先保存下一个结点 61 nextNode = currNode.next; 62 //当前结点指向preNode 63 currNode.next = preNode; 64 //更新preNoded位置 65 preNode = currNode; 66 //更新curNode位置 67 currNode = nextNode; 68 } 69 return preNode; 70 } 71 }
python3代码:
1 # Definition for singly-linked list. 2 # class ListNode: 3 # def __init__(self, val=0, next=None): 4 # self.val = val 5 # self.next = next 6 class Solution: 7 def reverseKGroup(self, head: Optional[ListNode], k: int) -> Optional[ListNode]: 8 dummyHead = ListNode(0,head) 9 pre, end = dummyHead, dummyHead 10 while end.next: 11 # 确定end的位置 12 i = 0 13 while i < k and end: 14 end = end.next 15 i += 1 16 # end为空 17 if not end: 18 break 19 # start的位置 20 start = pre.next 21 # 保存end的下一个结点并断开连接 22 next = end.next 23 end.next = None 24 25 # 将pre连接已翻转的链表 26 pre.next = self.reverse(start) 27 28 # 将start连接下一个待翻转的链表 29 start.next = next 30 # 更新pre和end的位置,在下一个翻转链表头结点的前一个结点 31 pre = start 32 end = start 33 return dummyHead.next 34 35 def reverse(self, head): 36 # 特殊判断 37 if not head or not head.next: 38 return head 39 cur = head 40 pre = None 41 next = None 42 while cur: 43 # 保存cur下一个结点 44 next = cur.next 45 # 将cur指向pre 46 cur.next = pre 47 # 将pre移动到cur,将cur移动到next 48 pre = cur 49 cur = next 50 return pre

2.找到待翻转的k个结点,如果剩余结点的数量小于k,则不需要翻转,直接返回剩下部分的头结点就行;
3.对[head,end)区间的结点进行翻转(翻转的结点不包括end),并返回翻转后的头结点newHead;
4.再以同样的方式,对剩下的结点进行翻转:head.next = reverseKGroup(end, k);
java代码:
1 /** 2 * Definition for singly-linked list. 3 * public class ListNode { 4 * int val; 5 * ListNode next; 6 * ListNode() {} 7 * ListNode(int val) { this.val = val; } 8 * ListNode(int val, ListNode next) { this.val = val; this.next = next; } 9 * } 10 */ 11 class Solution { 12 public ListNode reverseKGroup(ListNode head, int k) { 13 //特殊判断 14 if(head == null || head.next == null) return head; 15 //设定一个end指针 16 ListNode end = head; 17 for(int i = 0; i < k; i++){ 18 //如果剩下的结点不足k个 19 if(end == null) return head; 20 end = end.next; 21 } 22 //翻转前k个元素 23 ListNode newHead = reverse(head, end); 24 //下一轮翻转从end开始 25 head.next = reverseKGroup(end, k); 26 27 return newHead; 28 } 29 public ListNode reverse(ListNode head, ListNode end){ 30 //定义两个指针 31 //preNode前一个结点指针 32 //nextNode下一个结点指针 33 ListNode preNode = null; 34 ListNode nextNode = null; 35 //循环结束条件是当head没有到尾结点时 36 while(head != end){ 37 //先保存下一个结点 38 nextNode = head.next; 39 //当前结点指向preNode 40 head.next = preNode; 41 //更新preNoded位置 42 preNode = head; 43 //更新head位置 44 head = nextNode; 45 } 46 return preNode; 47 } 48 }
python3代码:
1 # Definition for singly-linked list. 2 # class ListNode: 3 # def __init__(self, val=0, next=None): 4 # self.val = val 5 # self.next = next 6 class Solution: 7 def reverseKGroup(self, head: Optional[ListNode], k: int) -> Optional[ListNode]: 8 # 特殊判断 9 if not head or not head.next: 10 return head 11 end = head 12 13 for i in range(k): 14 # not end:end为空,不足k个 15 if not end: 16 return head 17 end = end.next 18 19 # 翻转链表 20 newHead = self.reverse(head, end) 21 # 递归翻转剩下的 22 head.next = self.reverseKGroup(end, k) 23 24 return newHead 25 26 def reverse(self, head, end): 27 pre = None 28 next = None 29 while head != end: 30 # 保存head下一个结点 31 next = head.next 32 # 将head指向pre 33 head.next = pre 34 # 将pre移动到head,将head移动到next 35 pre = head 36 head = next 37 return pre
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)