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 };
相关文章
- Java实现 LeetCode 833 字符串中的查找与替换(暴力模拟)
- Java实现 LeetCode 817 链表组件(暴力)
- Java实现 LeetCode 725 分隔链表(暴力)
- Java实现 LeetCode 707 设计链表(环形链表)
- Java实现 LeetCode 307 区域和检索 - 数组可修改
- Java实现 LeetCode 279 完全平方数
- Java实现 LeetCode 109 有序链表转换二叉搜索树
- LeetCode(15): 每k个一组翻转链表
- 【LeetCode Python实现】6. Z 字形变换(中等)
- 【LeetCode Python实现】392. 判断子序列(简单)
- 【LeetCode Python实现】356. 直线镜像(中等)
- Leetcode 1356. 根据数字二进制下 1 的数目排序(有意思)
- Leetcode 594. 最长和谐子序列(已解决)
- [LeetCode] 24. Swap Nodes in Pairs ☆☆☆(链表,相邻两节点交换)
- [LeetCode] 032. Longest Valid Parentheses (Hard) (C++)
- 【Leetcode刷题Python】142.环形链表II
- LeetCode 61. 旋转链表
- LeetCode 138. 复制带随机指针的链表
- 【LeetCode】143. 重排链表