hot 100 第三十三 33.排序链表

给你链表的头结点 head ,请将其按 升序 排列并返回 排序后的链表

示例 1:

复制代码
输入:head = [4,2,1,3]
输出:[1,2,3,4]

示例 2:

复制代码
输入:head = [-1,5,3,4,0]
输出:[-1,0,3,4,5]

示例 3:

复制代码
输入:head = []
输出:[]

核心思路

归并排序(自底向上):链表的归并排序,自底向上避免递归栈开销。

复制代码
链表: 4 → 2 → 1 → 3

步骤1: 每1个节点一组,两两合并
[4] [2] [1] [3]
 ↓   ↓   ↓   ↓
[2,4] [1,3]

步骤2: 每2个节点一组,两两合并
[2,4] [1,3]
   ↓     ↓
  [1,2,3,4]

结果: 1 → 2 → 3 → 4

题解:

java 复制代码
class Solution {
    public ListNode sortList(ListNode head) {
        if (head == null || head.next == null) return head;
        
        // 1. 用快慢指针找到中点
        ListNode slow = head;
        ListNode fast = head.next;  // fast从head.next开始,确保slow在中点偏左
        
        while (fast != null && fast.next != null) {
            slow = slow.next;
            fast = fast.next.next;
        }
        
        // 2. 断开链表
        ListNode mid = slow.next;
        slow.next = null;
        
        // 3. 递归排序左右两部分
        ListNode left = sortList(head);
        ListNode right = sortList(mid);
        
        // 4. 合并两个有序链表
        return mergeTwoLists(left, right);
    }
    
    private ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        ListNode dummy = new ListNode(0);
        ListNode curr = dummy;
        
        while (l1 != null && l2 != null) {
            if (l1.val <= l2.val) {
                curr.next = l1;
                l1 = l1.next;
            } else {
                curr.next = l2;
                l2 = l2.next;
            }
            curr = curr.next;
        }
        
        curr.next = (l1 != null) ? l1 : l2;
        return dummy.next;
    }
}
```

### 递归演示
```
sortList(4→2→1→3)
  ├─ 找中点: slow=2
  ├─ 断开: [4→2] [1→3]
  ├─ sortList(4→2)
  │   ├─ 找中点: slow=4
  │   ├─ 断开: [4] [2]
  │   ├─ sortList(4) → 4
  │   ├─ sortList(2) → 2
  │   └─ merge(4, 2) → 2→4
  │
  ├─ sortList(1→3)
  │   ├─ 找中点: slow=1
  │   ├─ 断开: [1] [3]
  │   ├─ sortList(1) → 1
  │   ├─ sortList(3) → 3
  │   └─ merge(1, 3) → 1→3
  │
  └─ merge(2→4, 1→3) → 1→2→3→4
```

## 两种解法对比

| 特性 | 自底向上 | 自顶向下 |
|------|---------|---------|
| **时间复杂度** | O(n log n) | O(n log n) |
| **空间复杂度** | O(1) | O(log n) --- 递归栈 |
| **代码难度** | 较难 | 中等 |
| **符合题目要求** | ✓ 完全符合 | 空间不是O(1) |

## 快慢指针找中点详解
```
为什么fast从head.next开始?

链表: 1 → 2 → 3 → 4

如果fast从head开始:
slow = 1, fast = 1
slow = 2, fast = 3
slow = 3, fast = null
slow停在3(中点偏右)

断开: [1→2→3] [4]
左边比右边长,递归不平衡

如果fast从head.next开始:
slow = 1, fast = 2
slow = 2, fast = 4
slow = 2, fast = null
slow停在2(中点偏左)

断开: [1→2] [3→4]
左右平衡 ✓

本质

链表排序的关键:

  1. 选择合适算法 --- 归并排序最适合链表
  2. 自底向上实现 --- 避免递归栈,达到O(1)空间
  3. 链表分割 --- 用cut函数和快慢指针
  4. 合并有序链表 --- 核心作

这道题综合了:

  • 归并排序思想
  • 链表分割技巧
  • 合并有序链表
  • 空间优化方法
相关推荐
workflower2 小时前
用硬件换时间”与“用算法降成本”之间的博弈
人工智能·算法·安全·集成测试·无人机·ai编程
重生之我是Java开发战士4 小时前
【动态规划】简单多状态dp问题:按摩师,打家劫舍,删除并获得点数,粉刷房子,买卖股票的最佳时机
算法·动态规划·哈希算法
KAU的云实验台5 小时前
单/多UAV、静/动态路径规划,基于PlatEMO平台的带约束多目标优化 本文核心内容:
算法·matlab·无人机
Liangwei Lin5 小时前
洛谷 P1807 最长路
数据结构·算法
会编程的土豆5 小时前
【数据结构与算法】二叉树从建立开始
数据结构·c++·算法
_日拱一卒5 小时前
LeetCode:最大子数组和
数据结构·算法·leetcode
计算机安禾6 小时前
【数据结构与算法】第22篇:线索二叉树(Threaded Binary Tree)
c语言·开发语言·数据结构·学习·算法·链表·visual studio code
算法鑫探6 小时前
解密2025数字密码:数位统计之谜
c语言·数据结构·算法·新人首发
计算机安禾6 小时前
【数据结构与算法】第21篇:二叉树遍历的经典问题:由遍历序列重构二叉树
c语言·数据结构·学习·算法·重构·visual studio code·visual studio