LeetCode(15): 每k个一组翻转链表
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 };