Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 1882475

Browse files
Merge pull request MisterBooo#87 from anomot/master
0025 Solved
2 parents 76b7c5f + 938f18f commit 1882475

File tree

3 files changed

+169
-0
lines changed

3 files changed

+169
-0
lines changed
2.26 MB
Binary file not shown.
16.7 MB
Loading[フレーム]
Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
# LeetCode 第 25 号问题:K 个一组翻转链表
2+
3+
> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ](<https://github.com/MisterBooo/LeetCodeAnimation>) 系列文章之一。
4+
>
5+
> 同步博客:https://www.algomooc.com
6+
7+
题目来源于 LeetCode 上第 25 号问题:K 个一组翻转链表。题目难度为 Hard
8+
9+
### 题目描述
10+
11+
给你一个链表,每 *k* 个节点一组进行翻转,请你返回翻转后的链表。
12+
13+
*k* 是一个正整数,它的值小于或等于链表的长度。
14+
15+
如果节点总数不是 *k* 的整数倍,那么请将最后剩余的节点保持原有顺序。
16+
17+
**示例:**
18+
19+
给你这个链表:`1->2->3->4->5`
20+
21+
*k* = 2 时,应当返回: `2->1->4->3->5`
22+
23+
*k* = 3 时,应当返回: `3->2->1->4->5`
24+
25+
**说明:**
26+
27+
- 你的算法只能使用常数的额外空间。
28+
- **你不能只是单纯的改变节点内部的值**,而是需要实际进行节点交换。
29+
30+
### 题目解析
31+
32+
这道算法题可以说是 [两两交换链表中的节点](https://github.com/MisterBooo/LeetCodeAnimation/blob/master/0024-Swap-Nodes-in-Pairs/Article/0024-Swap-Nodes-in-Pairs2.md) 的升级版, 区别就是反转的子链表节点个数变成了自定义.
33+
34+
总体思路还是一样的, 具体可以分为两个处理模块:
35+
36+
1. 根据 *k* 划分若干个需要反转的子链表, 连接反转后的子链表, 最后不足 *k* 的子链表保持不变
37+
38+
- 设置哨兵 `dummy` 指向 `head` , 为了能找到反转后的链表头结点;
39+
40+
- 循环 *k* 确定需要 反转子链表 的范围:
41+
42+
- 循环完成, 确定子链表可以反转
43+
44+
假设 *A* , *B* 子链表邻接且都可以反转
45+
46+
- 指针 `start` 指向 *A* 的头结点, `end` 指向 *A* 的尾结点, `nxt` 指向 *B* 的头结点
47+
- `start -> end` 反转后, `start` 变成了 A 的尾结点, `start -> next = nxt` , 反转后的 *A* 链表指向了 *B*
48+
- 重置 `start`*B* 的头节点, `end`*B* 的尾结点, `nxt` 为下一个子链表头节点, 反转 *B* 链表
49+
- 重复上面动作, 知道 循环终止
50+
51+
- 循环终止, 剩余节点不足 *k* , 终止反转, 返回链表
52+
53+
2. 反转子链表
54+
55+
假设子链表前三个节点为 *a*, *b*, *c* ,设置指针 `pre`, `cur`, `nxt` , 初始化 `pre` 值为 `null`, `cur` 值为 *a* , `nxt` 值为 *a* , 这三个指针位置不变且相邻
56+
57+
终止条件: `cur` 不为空
58+
59+
将当前节点的指针指向上一个节点
60+
61+
1. `cur` 指向 `nxt` ( `nxt` 值为 *b* )
62+
2. `cur` 指向 `pre` ( `cur` 指向 `null` )
63+
3. `cur` 赋值给 `pre` ( `pre` 值为 *a* ) , `nxt` 赋值给 `cur` ( `cur` 值为 *b* )
64+
4. 在执行 步骤 `1` ( `nxt` 值为 *c* , 到此相当于 `pre`, `cur` , `nxt` 指向依次向后移动 `1` 位 )
65+
5. 重复上面动作
66+
67+
### 动画描述
68+
69+
<img src="../Animation/Animation.gif" alt="Animation" style="zoom:150%;" />
70+
71+
### 参考代码
72+
73+
#### 反转链表
74+
75+
```javascript
76+
/**
77+
* JavaScript 描述
78+
* 反转区间 [start, end) 的元素, 注意不包含 end
79+
*/
80+
function reverse(start, end) {
81+
let pre = null,
82+
cur = start,
83+
nxt = start;
84+
while (cur != end) {
85+
nxt = cur.next;
86+
// 逐个节点反转
87+
cur.next = pre;
88+
// 更新指针位置
89+
pre = cur;
90+
cur = nxt;
91+
}
92+
// 反转后的头结点, start 移到了最后, end 没有发生改变
93+
return pre;
94+
};
95+
```
96+
97+
#### 递归解法
98+
99+
```javascript
100+
/**
101+
* JavaScript 描述
102+
* 递归
103+
*/
104+
var reverseKGroup = function(head, k) {
105+
if (head == null) {
106+
return null;
107+
}
108+
let start, end;
109+
start = end = head;
110+
for (let i = 0; i < k; i++) {
111+
// 不足 k 个,不需要反转
112+
if (end == null) {
113+
return head;
114+
}
115+
end = end.next;
116+
}
117+
// 反转前 k 个元素, 不包含 end
118+
let reverseHead = reverse(start, end);
119+
// 递归反转后面k个元素 , 并前后连接起来
120+
start.next = reverseKGroup(end, k);
121+
return reverseHead;
122+
};
123+
```
124+
125+
#### 迭代解法
126+
127+
```javascript
128+
/**
129+
* JavaScript 描述
130+
* 迭代
131+
*/
132+
var reverseKGroup = function(head, k) {
133+
let dummy = new ListNode(0);
134+
dummy.next = head;
135+
let pre, start ,end, nxt;
136+
pre = start = end = nxt = dummy;
137+
while (end.next != null) {
138+
for (let i = 0; i < k && end != null; i++) {
139+
end = end.next;
140+
}
141+
if (end == null) {
142+
// 不足 k 个, 跳出循环
143+
break;
144+
}
145+
start = pre.next;
146+
nxt = end.next;
147+
// 反转前 k 个元素, 不包含 nxt
148+
pre.next = reverse(start, nxt);
149+
// 链接后面的链表
150+
start.next = nxt;
151+
// pre , end 重置到 下一个 k 子链表
152+
pre = start;
153+
end = pre;
154+
}
155+
return dummy.next;
156+
};
157+
```
158+
159+
### 复杂度分析
160+
161+
- 时间复杂度: **O( nk )** , 最好情况 O( n ), 最坏情况 O( n^2 )
162+
163+
- 空间复杂度: **O( 1 )**
164+
165+
166+
167+
168+
169+
![](../../Pictures/qrcode.jpg)

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /