hard!
題目描述:
給出一個鏈表,每 k 個節點為一組進行翻轉,並返回翻轉后的鏈表。
k 是一個正整數,它的值小於或等於鏈表的長度。如果節點總數不是 k 的整數倍,那么將最后剩余節點保持原有順序。
示例 :
給定這個鏈表:1->2->3->4->5
當 k = 2 時,應當返回: 2->1->4->3->5
當 k = 3 時,應當返回: 3->2->1->4->5
說明 :
- 你的算法只能使用常數的額外空間。
- 你不能只是單純的改變節點內部的值,而是需要實際的進行節點交換。
解題思路:
這道題讓我們以每k個為一組來翻轉鏈表,實際上是把原鏈表分成若干小段,然后分別對其進行翻轉,那么肯定總共需要兩個函數,一個是用來分段的,一個是用來翻轉的,我們就以題目中給的例子來看,對於給定鏈表1->2->3->4->5,一般在處理鏈表問題時,我們大多時候都會在開頭再加一個dummy node,因為翻轉鏈表時頭結點可能會變化,為了記錄當前最新的頭結點的位置而引入dummy node,那么我們加入dummy node后的鏈表變為-1->1->2->3->4->5,如果k為3的話,我們的目標是將1,2,3翻轉一下,那么我們需要一些指針,pre和next分別指向要翻轉的鏈表的前后的位置,然后翻轉后pre的位置更新到如下新的位置:
-1->1->2->3->4->5 | | pre next -1->3->2->1->4->5 | | pre next
以此類推,只要next走過k個節點,就可以調用翻轉函數來進行局部翻轉了。
C++解法一:
1 /** 2 * Definition for singly-linked list. 3 * struct ListNode { 4 * int val; 5 * ListNode *next; 6 * ListNode(int x) : val(x), next(NULL) {} 7 * }; 8 */ 9 class Solution { 10 public: 11 ListNode *reverseKGroup(ListNode *head, int k) { 12 if (!head || k == 1) return head; 13 ListNode *dummy = new ListNode(-1); 14 ListNode *pre = dummy, *cur = head; 15 dummy->next = head; 16 int i = 0; 17 while (cur) { 18 ++i; 19 if (i % k == 0) { 20 pre = reverseOneGroup(pre, cur->next); 21 cur = pre->next; 22 } else { 23 cur = cur->next; 24 } 25 } 26 return dummy->next; 27 } 28 ListNode *reverseOneGroup(ListNode *pre, ListNode *next) { 29 ListNode *last = pre->next; 30 ListNode *cur = last->next; 31 while(cur != next) { 32 last->next = cur->next; 33 cur->next = pre->next; 34 pre->next = cur; 35 cur = last->next; 36 } 37 return last; 38 } 39 };
也可以在一個函數中完成,我們首先遍歷整個鏈表,統計出鏈表的長度,然后如果長度大於等於k,我們開始交換節點,當k=2時,每段我們只需要交換一次,當k=3時,每段需要交換兩次,所以i從1開始循環,注意交換一段后更新pre指針,然后num自減k,直到num<k時循環結束。
C++解法二:
1 class Solution { 2 public: 3 ListNode* reverseKGroup(ListNode* head, int k) { 4 ListNode *dummy = new ListNode(-1), *pre = dummy, *cur = pre; 5 dummy->next = head; 6 int num = 0; 7 while (cur = cur->next) ++num; 8 while (num >= k) { 9 cur = pre->next; 10 for (int i = 1; i < k; ++i) { 11 ListNode *t = cur->next; 12 cur->next = t->next; 13 t->next = pre->next; 14 pre->next = t; 15 } 16 pre = cur; 17 num -= k; 18 } 19 return dummy->next; 20 } 21 };
也可以使用遞歸來做,我們用head記錄每段的開始位置,cur記錄結束位置的下一個節點,然后我們調用reverse函數來將這段翻轉,然后得到一個new_head,原來的head就變成了末尾,這時候后面接上遞歸調用下一段得到的新節點,返回new_head即可。
C++解法三:
1 class Solution { 2 public: 3 ListNode* reverseKGroup(ListNode* head, int k) { 4 ListNode *cur = head; 5 for (int i = 0; i < k; ++i) { 6 if (!cur) return head; 7 cur = cur->next; 8 } 9 ListNode *new_head = reverse(head, cur); 10 head->next = reverseKGroup(cur, k); 11 return new_head; 12 } 13 ListNode* reverse(ListNode* head, ListNode* tail) { 14 ListNode *pre = tail; 15 while (head != tail) { 16 ListNode *t = head->next; 17 head->next = pre; 18 pre = head; 19 head = t; 20 } 21 return pre; 22 } 23 };