Update Leetcode 题解 - 链表.md

补充 K 个一组翻转链表、重排链表、合并 K 个有序链表、链表排序,并配图说明 K 个一组翻转时的指针变化。
This commit is contained in:
CyC2018
2026-10-25 21:34:15 +08:00
parent 66e712517e
commit 709067f4f2
3 changed files with 266 additions and 0 deletions
+171
View File
@@ -11,6 +11,10 @@
* [8. 回文链表](#8-回文链表)
* [9. 分隔链表](#9-分隔链表)
* [10. 链表元素按奇偶聚集](#10-链表元素按奇偶聚集)
* [11. K 个一组翻转链表](#11-k-个一组翻转链表)
* [12. 重排链表](#12-重排链表)
* [13. 合并 K 个有序链表](#13-合并-k-个有序链表)
* [14. 链表排序](#14-链表排序)
<!-- GFM-TOC -->
@@ -363,3 +367,170 @@ public ListNode oddEvenList(ListNode head) {
return head;
}
```
## 11. K 个一组翻转链表
25\. Reverse Nodes in k-Group (Hard)
[Leetcode](https://leetcode.com/problems/reverse-nodes-in-k-group/) / [力扣](https://leetcode.cn/problems/reverse-nodes-in-k-group/)
```text
Input: 1->2->3->4->5, k = 2
Output: 2->1->4->3->5
```
题目描述:每 k 个节点一组进行翻转,最后不足 k 个的节点保持原有顺序。
使用一个虚拟头节点 dummy,pre 指向下一组的前一个节点。先从 pre 向后走 k 步找到这一组的尾节点 tail,走不到说明剩余节点不足 k 个。然后把 pre.next 到 tail 这一段原地翻转,再把它接回原链表,最后 pre 移到这一组翻转后的末尾。
<div align="center"> <img src="pics/reverse-k-group.png" width="560px"/> </div><br>
```java
public ListNode reverseKGroup(ListNode head, int k) {
ListNode dummy = new ListNode(-1);
dummy.next = head;
ListNode pre = dummy;
while (true) {
ListNode tail = pre;
for (int i = 0; i < k && tail != null; i++) {
tail = tail.next;
}
if (tail == null) {
break;
}
ListNode first = pre.next, next = tail.next;
ListNode prev = next, cur = first;
while (cur != next) {
ListNode tmp = cur.next;
cur.next = prev;
prev = cur;
cur = tmp;
}
pre.next = tail;
pre = first;
}
return dummy.next;
}
```
## 12. 重排链表
143\. Reorder List (Medium)
[Leetcode](https://leetcode.com/problems/reorder-list/) / [力扣](https://leetcode.cn/problems/reorder-list/)
```text
Input: 1->2->3->4->5
Output: 1->5->2->4->3
```
题目描述:把 L0→L1→…→Ln 重排为 L0→Ln→L1→Ln-1→…,只能修改节点的指针。
可以拆成三个已经解决过的子问题:用快慢指针找到中点并断开;把后半段反转;再把两段交替连接起来。
```java
public void reorderList(ListNode head) {
if (head == null || head.next == null) {
return;
}
ListNode slow = head, fast = head;
while (fast.next != null && fast.next.next != null) {
slow = slow.next;
fast = fast.next.next;
}
ListNode l2 = reverse(slow.next);
slow.next = null;
ListNode l1 = head;
while (l2 != null) {
ListNode n1 = l1.next, n2 = l2.next;
l1.next = l2;
l2.next = n1;
l1 = n1;
l2 = n2;
}
}
private ListNode reverse(ListNode head) {
ListNode pre = null;
while (head != null) {
ListNode next = head.next;
head.next = pre;
pre = head;
head = next;
}
return pre;
}
```
## 13. 合并 K 个有序链表
23\. Merge k Sorted Lists (Hard)
[Leetcode](https://leetcode.com/problems/merge-k-sorted-lists/) / [力扣](https://leetcode.cn/problems/merge-k-sorted-lists/)
用小顶堆保存每个链表当前的头节点,每次取出值最小的节点接到结果链表后面,再把它的下一个节点放入堆中。设一共有 N 个节点,每个节点进出堆一次,时间复杂度为 O(NlogK)。
也可以像归并排序那样两两合并,时间复杂度同样是 O(NlogK)。
```java
public ListNode mergeKLists(ListNode[] lists) {
PriorityQueue<ListNode> pq = new PriorityQueue<>((a, b) -> Integer.compare(a.val, b.val));
for (ListNode node : lists) {
if (node != null) {
pq.add(node);
}
}
ListNode dummy = new ListNode(-1), tail = dummy;
while (!pq.isEmpty()) {
ListNode node = pq.poll();
tail.next = node;
tail = node;
if (node.next != null) {
pq.add(node.next);
}
}
return dummy.next;
}
```
## 14. 链表排序
148\. Sort List (Medium)
[Leetcode](https://leetcode.com/problems/sort-list/) / [力扣](https://leetcode.cn/problems/sort-list/)
题目描述:在 O(NlogN) 时间复杂度内对链表进行排序。
链表不能随机访问,适合用归并排序:用快慢指针找到中点断开,两半分别排序后,用第 3 题的方法归并。递归需要 O(logN) 的栈空间。
```java
public ListNode sortList(ListNode head) {
if (head == null || head.next == null) {
return head;
}
ListNode slow = head, fast = head.next;
while (fast != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
}
ListNode right = slow.next;
slow.next = null;
return merge(sortList(head), sortList(right));
}
private ListNode merge(ListNode l1, ListNode l2) {
ListNode dummy = new ListNode(-1), tail = dummy;
while (l1 != null && l2 != null) {
if (l1.val <= l2.val) {
tail.next = l1;
l1 = l1.next;
} else {
tail.next = l2;
l2 = l2.next;
}
tail = tail.next;
}
tail.next = l1 != null ? l1 : l2;
return dummy.next;
}
```
Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

+95
View File
@@ -0,0 +1,95 @@
<svg xmlns="http://www.w3.org/2000/svg" width="600" height="300" viewBox="0 0 600 300">
<style>
text { font-family: "PingFang SC", "Microsoft YaHei", Helvetica, Arial, sans-serif; fill: #666; font-size: 12px; }
.title { font-weight: bold; font-size: 15px; fill: #777; text-anchor: middle; }
.b { font-weight: bold; }
.c { text-anchor: middle; }
.e { text-anchor: end; }
.note { fill: #C0605A; }
.ok { fill: #6A9F68; }
.sub { font-size: 11px; fill: #888; }
.mono { font-family: Menlo, Consolas, monospace; }
.arrow { stroke: #666; stroke-width: 1.3; fill: none; marker-end: url(#a); }
.red { stroke: #C0605A; marker-end: url(#r); }
.green { stroke: #6A9F68; marker-end: url(#g); }
.dash { stroke-dasharray: 5 4; }
.line { stroke: #666; stroke-width: 1.3; fill: none; }
.frame { fill: none; stroke: #999; stroke-width: 1; stroke-dasharray: 4 3; }
.shadow { fill: #C0C0C0; }
</style>
<defs>
<marker id="a" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
<path d="M1,1 L9,5 L1,9" fill="none" stroke="#666" stroke-width="1.6"/>
</marker>
<marker id="r" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
<path d="M1,1 L9,5 L1,9" fill="none" stroke="#C0605A" stroke-width="1.6"/>
</marker>
<marker id="g" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
<path d="M1,1 L9,5 L1,9" fill="none" stroke="#6A9F68" stroke-width="1.6"/>
</marker>
</defs>
<rect width="100%" height="100%" fill="#FFFFFF"/>
<text x="300" y="28" class="title">K 个一组翻转链表(k = 2)</text>
<text x="20" y="90" class="b">翻转前</text>
<rect x="88" y="72" width="50" height="32" rx="6" class="shadow"/>
<rect x="85" y="69" width="50" height="32" rx="6" fill="#E6E6E6"/>
<text x="110" y="89.5" class="c b">dummy</text>
<line x1="137" y1="85" x2="163" y2="85" class="arrow "/>
<rect x="168" y="72" width="50" height="32" rx="6" class="shadow"/>
<rect x="165" y="69" width="50" height="32" rx="6" fill="#B1DDF0"/>
<text x="190" y="89.5" class="c b">1</text>
<line x1="217" y1="85" x2="243" y2="85" class="arrow "/>
<rect x="248" y="72" width="50" height="32" rx="6" class="shadow"/>
<rect x="245" y="69" width="50" height="32" rx="6" fill="#B1DDF0"/>
<text x="270" y="89.5" class="c b">2</text>
<line x1="297" y1="85" x2="323" y2="85" class="arrow "/>
<rect x="328" y="72" width="50" height="32" rx="6" class="shadow"/>
<rect x="325" y="69" width="50" height="32" rx="6" fill="#B1DDF0"/>
<text x="350" y="89.5" class="c b">3</text>
<line x1="377" y1="85" x2="403" y2="85" class="arrow "/>
<rect x="408" y="72" width="50" height="32" rx="6" class="shadow"/>
<rect x="405" y="69" width="50" height="32" rx="6" fill="#B1DDF0"/>
<text x="430" y="89.5" class="c b">4</text>
<line x1="457" y1="85" x2="483" y2="85" class="arrow "/>
<rect x="488" y="72" width="50" height="32" rx="6" class="shadow"/>
<rect x="485" y="69" width="50" height="32" rx="6" fill="#B1DDF0"/>
<text x="510" y="89.5" class="c b">5</text>
<line x1="110" y1="131" x2="110" y2="105" class="arrow "/>
<text x="110" y="147" class="c ">pre</text>
<line x1="190" y1="131" x2="190" y2="105" class="arrow "/>
<text x="190" y="147" class="c ">first</text>
<line x1="270" y1="131" x2="270" y2="105" class="arrow "/>
<text x="270" y="147" class="c ">tail</text>
<line x1="350" y1="131" x2="350" y2="105" class="arrow "/>
<text x="350" y="147" class="c ">next</text>
<text x="20" y="205" class="b">翻转后</text>
<rect x="88" y="187" width="50" height="32" rx="6" class="shadow"/>
<rect x="85" y="184" width="50" height="32" rx="6" fill="#E6E6E6"/>
<text x="110" y="204.5" class="c b">dummy</text>
<line x1="137" y1="200" x2="163" y2="200" class="arrow "/>
<rect x="168" y="187" width="50" height="32" rx="6" class="shadow"/>
<rect x="165" y="184" width="50" height="32" rx="6" fill="#B1DDF0"/>
<text x="190" y="204.5" class="c b">2</text>
<line x1="217" y1="200" x2="243" y2="200" class="arrow "/>
<rect x="248" y="187" width="50" height="32" rx="6" class="shadow"/>
<rect x="245" y="184" width="50" height="32" rx="6" fill="#B1DDF0"/>
<text x="270" y="204.5" class="c b">1</text>
<line x1="297" y1="200" x2="323" y2="200" class="arrow "/>
<rect x="328" y="187" width="50" height="32" rx="6" class="shadow"/>
<rect x="325" y="184" width="50" height="32" rx="6" fill="#B1DDF0"/>
<text x="350" y="204.5" class="c b">3</text>
<line x1="377" y1="200" x2="403" y2="200" class="arrow "/>
<rect x="408" y="187" width="50" height="32" rx="6" class="shadow"/>
<rect x="405" y="184" width="50" height="32" rx="6" fill="#B1DDF0"/>
<text x="430" y="204.5" class="c b">4</text>
<line x1="457" y1="200" x2="483" y2="200" class="arrow "/>
<rect x="488" y="187" width="50" height="32" rx="6" class="shadow"/>
<rect x="485" y="184" width="50" height="32" rx="6" fill="#B1DDF0"/>
<text x="510" y="204.5" class="c b">5</text>
<line x1="110" y1="246" x2="110" y2="220" class="arrow "/>
<text x="110" y="262" class="c ">pre.next = tail</text>
<line x1="270" y1="246" x2="270" y2="220" class="arrow green"/>
<text x="270" y="262" class="c ok">pre = first</text>
<text x="300" y="292" class="c sub">first 成为这一组的末尾,也是下一组的 pre</text>
</svg>

After

Width:  |  Height:  |  Size: 5.3 KiB