模拟
前置知识 : 反转链表、两两交换链表中的节点 。 LeetCode 有相应题目,可以先做。
设置哑结点 , 便于操作头结点。 翻转至少要 k k k 个结点 , 先检查剩余结点够不够 k k k 个。 不够 k k k 个就翻转完成了。
翻转分为组内翻转和首尾变向两步 。 如图所示 , 当
k
=
3
k =3
k=3 ,
1
1
1 ->
2
2
2 ->
3
3
3 变成
3
3
3 ->
2
2
2 ->
1
1
1 就是组内翻转 。
O
O
O ->
3
3
3 ,
1
1
1 ->
4
4
4 就是首尾变向 。
模拟组内翻转 。 对于第一组,组前结点
p
=
O
p=O
p=O 。 用
a
a
a 表示
p
p
p ->
n
e
x
t
next
next ,
b
b
b 表示
a
a
a ->
n
e
x
t
next
next ,
c
c
c 表示
b
b
b ->
n
e
x
t
next
next 。 让
b
b
b ->
n
e
x
t
=
a
next = a
next=a 完成反向 ,
a
=
b
,
b
=
c
a=b , b=c
a=b,b=c 让
a
、
b
a、b
a、b 后移。组内
k
k
k 个点之间的连接线一共
k
−
1
k-1
k−1 条,循环
k
−
1
k-1
k−1 次 , 完成组内翻转 。
组内翻转的最后 ,
a
a
a 为组内第一个结点 ,
b
b
b 为下一组的第一个结点 , 组前结点
p
p
p 的指向没有变过 ,
p
p
p ->
n
e
x
t
next
next 依然指向翻转前的第一个点 ,所以首尾变向很容易完成 , 见代码。
代码展示
C++
class Solution {
public:
ListNode* reverseKGroup(ListNode* head, int k) {
auto dummy = new ListNode(-1);
dummy->next = head;
auto p= dummy;
while(1){
auto t = p;
for(int i = 0;i<k&&t;i++) t = t->next;//检查k个结点够不够。
if(!t) break;//剩余结点不足k个
auto a = p->next , b = a->next;
for(int i = 0;i<k-1;i++){
auto c = b->next;
b->next = a;
a = b , b = c;
}
auto c = p->next;
p->next = a, c ->next = b;
p = c;
}
return dummy->next;
}
};
- 时间复杂度 : O ( n ) O(n) O(n) , n n n 是链表长度,翻转链表的时间复杂度 O ( n ) O(n) O(n) ,遍历链表的时间复杂度 O ( n ) O(n) O(n) , 二者是相加关系 , 每个结点最多被遍历两遍 , 总时间复杂度 O ( 2 n ) O(2n) O(2n) , 忽略常数时间复杂度 O ( n ) O(n) O(n) 。
- 空间复杂度: O ( 1 ) O(1) O(1) , 除若干变量占用的常量级空间,没有使用额外的线性空间