Leetcode: Reverse Nodes in k-Group

Given a linked list, reverse the nodes of a linked list k at a time and return its modified list.

If the number of nodes is not a multiple of k then left-out nodes in the end should remain as it is.

You may not alter the values in the nodes, only nodes itself may be changed.

Only constant memory is allowed.

For example,
Given this linked list: 1->2->3->4->5

For k = 2, you should return: 2->1->4->3->5

For k = 3, you should return: 3->2->1->4->5

Analysis: Linked List节点倒序,Naive的方法就是用Stack,这样的话只需要K层Stack,不满足constant memory的条件;

第二遍方法(采用解法)两个指针:walker指向需改序序列的上一个节点,runner指向需改序序列最后节点,每次把walker下一个节点移到runner下一点处,直到walker.next = runner

 1 /**
 2  * Definition for singly-linked list.
 3  * public class ListNode {
 4  *     int val;
 5  *     ListNode next;
 6  *     ListNode(int x) {
 7  *         val = x;
 8  *         next = null;
 9  *     }
10  * }
11  */
12 public class Solution {
13     public ListNode reverseKGroup(ListNode head, int k) {
14         ListNode dummy = new ListNode(-1);
15         dummy.next = head;
16         ListNode walker = dummy;
17         ListNode runner = dummy;
18         while (runner.next != null) {
19             int count = k;
20             while (count > 0 && runner.next != null) {
21                 runner = runner.next;
22                 count--;
23             }
24             if (count == 0) walker = reverse(walker, runner);
25             else break;
26             runner = walker;
27         }
28         return dummy.next;
29     }
30     
31     public ListNode reverse(ListNode walker, ListNode runner) {
32         ListNode store = walker.next;
33         while (walker.next != runner) {
34             ListNode cur = walker.next;
35             ListNode next = cur.next;
36             cur.next = runner.next;
37             runner.next = cur;
38             walker.next = next;
39         }
40         return store;
41     }
42 }

第二遍方法(另一种方法,可以但是不如第一种方法好写):精髓在于reverse子函数,输入的两个参数分别是prev: 需改续序列的上一个节点,end: 需改续序列的下一个节点

reverse函数中设计比较精巧的地方在于,需要一开始保存一个start.next节点,最后作为start的新位置返回去更新start,这就是新的需改续序列的上一个节点位置(我一开始也就是卡在这里,不知如何更新start)

 1 public class Solution {
 2     public ListNode reverseKGroup(ListNode head, int k) {
 3         ListNode dummy = new ListNode(0);
 4         dummy.next = head;
 5  
 6         ListNode start = dummy;
 7         ListNode end = head;
 8         while (end != null) {
 9             int count = k;
10             for (; count>0 && end!=null; count--) {
11                 end = end.next;
12             }
13             if (count == 0) {
14                 start = reverse(start, end);
15             }
16         }
17         return dummy.next;
18     }
19     
20     public ListNode reverse(ListNode start, ListNode end) {
21         ListNode pre = end;
22         ListNode store = start.next;
23         ListNode cur = store;
24         while (cur != end) {
25             ListNode next = cur.next;
26             cur.next = pre;
27             pre = cur;
28             cur = next;
29         }
30         start.next = pre;
31         return store;
32     }
33 }

Naive方法:使用Stack, 但不满足constant memeory的条件

 1 /**
 2  * Definition for singly-linked list.
 3  * public class ListNode {
 4  *     int val;
 5  *     ListNode next;
 6  *     ListNode(int x) {
 7  *         val = x;
 8  *         next = null;
 9  *     }
10  * }
11  */
12 public class Solution {
13     public ListNode reverseKGroup(ListNode head, int k) {
14         if (head == null) return null;
15         ListNode prev = new ListNode(-1);
16         prev.next = head;
17         ListNode current = prev;
18         while (head != null) {
19             Stack<Integer> store = new Stack<Integer>();
20             ListNode temp = head;
21             int i = 0;
22             for (; i < k; i++) {
23                 if (head == null) break;
24                 store.push(head.val);
25                 head = head.next;
26             }
27             if (i == k) {
28                 while (!store.empty()) {
29                     current.next = new ListNode(store.pop());
30                     current = current.next;
31                 }
32             }
33             else current.next = temp;
34         }
35         return prev.next;
36     }
37 }

Gode Ganker的改指针做法(与我的第二遍方法2相仿),输入给reverse函数的参数分别是:prev: 需改续序列的前一个节点,end: 需改续序列的后一个节点

基本思路是这样的,我们统计目前节点数量,如果到达k,就把当前k个结点reverse,这里需要reverse linked list的subroutine。这里我们需要先往前走,到达k的时候才做reverse,所以总体来说每个结点会被访问两次。总时间复杂度是O(2*n)=O(n),空间复杂度是O(1)。

 1 public ListNode reverseKGroup(ListNode head, int k) {
 2     if(head == null)
 3     {
 4         return null;
 5     }
 6     ListNode dummy = new ListNode(0);
 7     dummy.next = head;
 8     int count = 0;
 9     ListNode pre = dummy;
10     ListNode cur = head;
11     while(cur != null)
12     {
13         count ++;
14         ListNode next = cur.next;
15         if(count == k)
16         {
17             pre = reverse(pre, next);
18             count = 0;   
19         }
20         cur = next;
21     }
22     return dummy.next;
23 }
24 private ListNode reverse(ListNode pre, ListNode end)
25 {
26     if(pre==null || pre.next==null)
27         return pre;
28     ListNode head = pre.next;
29     ListNode cur = pre.next.next;
30     while(cur!=end)
31     {
32         ListNode next = cur.next;
33         cur.next = pre.next;
34         pre.next = cur;
35         cur = next;
36     }
37     head.next = end;
38     return head;
39 }
原文地址:https://www.cnblogs.com/EdwardLiu/p/3792086.html