diff --git a/notes/Leetcode 题解 - 链表.md b/notes/Leetcode 题解 - 链表.md index 270ec9ad..188f91d6 100644 --- a/notes/Leetcode 题解 - 链表.md +++ b/notes/Leetcode 题解 - 链表.md @@ -11,6 +11,10 @@ * [8. 回文链表](#8-回文链表) * [9. 分隔链表](#9-分隔链表) * [10. 链表元素按奇偶聚集](#10-链表元素按奇偶聚集) + * [11. K 个一组翻转链表](#11-k-个一组翻转链表) + * [12. 重排链表](#12-重排链表) + * [13. 合并 K 个有序链表](#13-合并-k-个有序链表) + * [14. 链表排序](#14-链表排序) @@ -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 移到这一组翻转后的末尾。 + +

+ +```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 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; +} +``` diff --git a/notes/pics/reverse-k-group.png b/notes/pics/reverse-k-group.png new file mode 100644 index 00000000..61aabccb Binary files /dev/null and b/notes/pics/reverse-k-group.png differ diff --git a/tools/diagram/src/reverse-k-group.svg b/tools/diagram/src/reverse-k-group.svg new file mode 100644 index 00000000..04a412c7 --- /dev/null +++ b/tools/diagram/src/reverse-k-group.svg @@ -0,0 +1,95 @@ + + + + + + + + + + + + + + + + K 个一组翻转链表(k = 2) + 翻转前 + + + dummy + + + + 1 + + + + 2 + + + + 3 + + + + 4 + + + + 5 + + pre + + first + + tail + + next + 翻转后 + + + dummy + + + + 2 + + + + 1 + + + + 3 + + + + 4 + + + + 5 + + pre.next = tail + + pre = first + first 成为这一组的末尾,也是下一组的 pre +