[算法] 结点K个一组的链表反转(hard)
文章目录
- 1. 题目解析
- 2. 讲解原理
- 3. 编码实践
下面我们来分享一道简单的题目 -> 结点K个一组的链表反转(hard)
1. 题目解析
给你链表的头节点 head ,每 k 个节点一组进行翻转,请你返回修改后的链表。
k 是一个正整数,它的值小于或等于链表的长度。如果节点总数不是 k 的整数倍,么请将最后剩余的节点保持原有顺序。
你不能只是单纯的改变节点内部的值,而是需要实际进行节点交换。
下面是一个简单的举例:
示例 1:
输入:head = [1,2,3,4,5], k = 2
输出:[2,1,4,3,5]
示例 2:
输入:head = [1,2,3,4,5], k = 3
输出:[3,2,1,4,5]
因此, 说白了, 就是给你一串链表, 然后要求你k个节点一组然后将其反转即可.
2. 讲解原理
本质求解思路是模拟算法. 这里模拟的思路可能有所不同, 下面提供一种模拟思路.
模拟策略:
1. 先求需要逆序多少组.
2. 重复n组长度为k的链表逆序.
下面解释几个问题:
- 为什么要先求出需要逆序多少组?
因为我们在处理链表的时候是对该节点是否要头插是不确定的, 比如示例2当中, 如果cur = 2, 则需要头插, 如果cur = 4, 则不能头插. - 如何求需要逆置多少组?
很简单, 逆置组数 = 链表节点数量 / k. - 对于一组链表如何进行逆置?
很简单, 新建一个虚拟节点, 然后挨个头插即可.
3. 编码实践
编码的难点: 编码的唯一难点在于, 在这个过程中会出现多个指针来回使用的情况, 需要搞清楚各个指针的作用以及逻辑.
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* reverseKGroup(ListNode* head, int k) {
// 1. 先求出链表需要逆序多少组?
int size = 0;
ListNode* cur = head;
while(cur)
{
size += 1;
cur = cur->next;
}
int n = size / k;
// 2. 逆序链表
cur = head;
ListNode* newHead = new ListNode;
ListNode* prev = newHead;
for(int i = 0; i < n; i++)
{
ListNode* temp = cur;
for(int j = 0; j < k; j++)
{
ListNode* next = cur->next;
cur->next = prev->next;
prev->next = cur;
cur = next;
}
prev = temp;
}
// 3. 添加无需逆置的可能存在的剩余链表部分
if(cur) prev->next = cur;
// 4. 释放多余空间, 准备ret结果
prev = newHead->next;
delete newHead;
return prev;
}
};
代码中各链表的含义:
newHead: 始终指向虚拟节点
prev: 指向头插的前一个节点
temp: 标记下一组头插的前一个节点
cur: 指向原链表, 用来遍历
next: 用来记录原链表cur指向的下一个节点, 方便cur回归原链表.
时间复杂度是: O(N)
空间复杂度: O(1)