力扣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);并将已翻转的部分和待翻转的部分连接,尾结点指向它的下一个结点:

  start.next = next;并且将pre和end重置为下一次翻转链表头结点的前一个结点;

 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

 二、递归
1.设定一个end指针,初始指向head处

 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

posted on 2022-09-20 17:16  我不想一直当菜鸟  阅读(84)  评论(0编辑  收藏  举报