📝 题目描述
题目链接:K 个一组翻转链表
给你链表的头节点 head ,每 k 个节点一组进行翻转,请你返回修改后的链表。
k 是一个正整数,它的值小于或等于链表的长度。如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。
你不能只是单纯的改变节点内部的值,而是需要实际进行节点交换。
示例:
示例 1:
1 2
| 输入:head = [1,2,3,4,5], k = 2 输出:[2,1,4,3,5]
|
示例 2:
1 2
| 输入:head = [1,2,3,4,5], k = 3 输出:[3,2,1,4,5]
|
提示:
链表中的节点数目为 n
1 <= k <= n <= 5000
0 <= Node.val <= 1000
💡 解题思路
方法一:模拟✅️
首先创建一个哨兵节点,使得第一个子链表的头节点 head 不再特殊化。
我们需要把链表节点按照 k 个一组分组,所以可以使用一个指针 head 依次指向每组的头节点。这个指针每次向前移动 k 步,直至链表结尾。对于每个分组,我们先判断它的长度是否大于等于 k。若是,我们就翻转这部分链表,否则不需要翻转。
接下来的问题就是如何翻转一个分组内的子链表。翻转一个链表并不难,过程可以参考“反转链表”。但是对于一个子链表,除了翻转其本身之外,还需要将子链表的头部与上一个子链表连接,以及子链表的尾部与下一个子链表连接。
因此,在翻转子链表的时候,我们不仅需要子链表头节点 head,还需要有 head 的上一个节点 prev,以便翻转完后把子链表再接回 prev。
反复移动指针 head 与 pre,对 head 所指向的子链表进行翻转,直到结尾,我们就得到了答案。最后返回哨兵节点的下一个节点作为答案即可。
方法二:数组模拟
构建一个数组,将所有链表的指针存储,然后利用 reverse 函数快速翻转,并然后逐个连起链表。
🔧 代码实现
1、模拟
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
|
class Solution { public: pair<ListNode*, ListNode*> myReverse(ListNode* head, ListNode* tail) { ListNode* prev = nullptr; ListNode* current = head; while (prev != tail) { ListNode* next = current->next; current->next = prev; prev = current; current = next; } return {tail, head}; }
ListNode* reverseKGroup(ListNode* head, int k) { ListNode* dummy = new ListNode(-1, head); ListNode* prev = dummy;
while (head) { ListNode* tail = prev; for (int i = 0; i < k; ++i) { tail = tail->next;
if (tail == nullptr) { return dummy->next; } }
ListNode* next = tail->next;
tie(head, tail) = myReverse(head, tail);
prev->next = head; tail->next = next;
prev = tail; head = next; }
ListNode* ans = dummy -> next; delete dummy; return ans; } };
|
2、数组模拟
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| class Solution { public: ListNode* reverseKGroup(ListNode* head, int k) { if (!head || k == 1) return head; vector<ListNode*> list; ListNode* ptr = head; while (ptr != nullptr) { list.push_back(ptr); ptr = ptr -> next; } int start = 0; while (start + k <= list.size()) { reverse(list.begin() + start, list.begin() + start + k); start += k; } for (int i = 0; i < list.size() - 1; i++) { list[i]->next = list[i + 1]; } list.back()->next = nullptr; return list[0]; } };
|
📊 复杂度分析
1、模拟
- 时间复杂度:O(n),其中 n 为链表的长度。head 指针会在 O(⌊kn⌋) 个节点上停留,每次停留需要进行一次 O(k) 的翻转操作。
- 空间复杂度:O(1),我们只需要建立常数个变量。
2、数组模拟
- 时间复杂度:O(n),遍历存储一遍节点,翻转访问一遍节点,重连访问一遍节点。
- 空间复杂度:O(n)。
🎯 总结