Leetcode: 排序链表

26 阅读3分钟

原题:leetcode.cn/problems/so…

题目描述

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

解题思路

  1. 归并排序框架
    • 使用快慢指针找到链表中点
    • 递归排序左半部分链表
    • 递归排序右半部分链表
    • 合并两个有序链表
  2. 迭代优化
    • 自底向上归并,避免递归栈空间
    • 按子链表长度倍增合并
  3. 关键操作
    • 链表分割时需断开中点连接
    • 合并过程使用哑节点简化操作
  4. 空间控制
    • 迭代法实现常数空间复杂度

关键洞察

  1. 链表特性利用
    • 归并排序天然适合链表结构
    • 合并操作无需额外空间
  2. 中点定位技巧
    • 快指针走2步,慢指针走1步
    • 慢指针停在中点前驱位置
  3. 递归终止条件
    • 空链表或单节点链表已有序
  4. 迭代步进控制
    • 子链表长度从1开始倍增
    • 每次遍历完成整轮合并

复杂度分析

方法时间复杂度空间复杂度
递归法O(n log n)O(log n) (递归栈)
迭代法O(n log n)O(1)

代码实现

  • JavaScript(递归)
/**
 * Definition for singly-linked list.
 * function ListNode(val, next) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.next = (next===undefined ? null : next)
 * }
 */
/**
 * @param {ListNode} head
 * @return {ListNode}
 */
var sortList = function (head) {

    const toSortList = (head, tail) => {

        if (!head) return head;
        if (head.next === tail) {
            head.next = null;
            return head;
        }

        let fast = slow = head;


        while (fast !== tail) {
            slow = slow.next;
            fast = fast.next;
            if (fast !== tail) {
                fast = fast.next;
            }
        }

        let mid = slow;
        return merge(toSortList(head, mid), toSortList(mid, tail))
    }

    const merge = (head1, head2) => {
        const dummyHead = new ListNode(0);
        let temp = dummyHead, temp1 = head1, temp2 = head2;

        while (temp1 !== null && temp2 !== null) {
            if (temp1.val <= temp2.val) {
                temp.next = temp1;
                temp1 = temp1.next
            } else {
                temp.next = temp2;
                temp2 = temp2.next
            }

            temp = temp.next;
        }

        if (temp1 !== null) {
            temp.next = temp1
        } else if (temp2 !== null) {
            temp.next = temp2;
        }

        return dummyHead.next;
    }


    return toSortList(head, null);
};
  • JavaScript(迭代)
/**
 * Definition for singly-linked list.
 * function ListNode(val, next) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.next = (next===undefined ? null : next)
 * }
 */
/**
 * @param {ListNode} head
 * @return {ListNode}
 */
const sortList = (head) => {
    // 边界处理
    if (!head || !head.next) return head;

    // 获取链表长度
    let n = 0, cur = head;
    while (cur) {
        n++;
        cur = cur.next;
    }

    const dummy = new ListNode(-1, head);

    // 子链表长度从1开始倍增
    for (let size = 1; size < n; size *= 2) {
        let prev = dummy;
        let cur = dummy.next;

        while (cur) {
            // 获取左子链表
            const left = cur;
            const right = split(left, size);
            cur = split(right, size); // 剩余部分

            // 合并左右子链表
            prev.next = merge(left, right);

            // 移动prev到合并后链表的末尾
            while (prev.next) {
                prev = prev.next;
            }
        }
    }

    return dummy.next;
};

// 合并两个有序链表
const merge = (l1, l2) => {
    const dummy = new ListNode(-1);
    let cur = dummy;

    while (l1 && l2) {
        if (l1.val <= l2.val) {
            cur.next = l1;
            l1 = l1.next;
        } else {
            cur.next = l2;
            l2 = l2.next;
        }
        cur = cur.next;
    }

    cur.next = l1 || l2;
    return dummy.next;
};

// 分割链表:返回后半部分头节点
const split = (head, size) => {
    if (!head) return null;

    // 移动size-1步找到分割点
    for (let i = 1; i < size && head.next; i++) {
        head = head.next;
    }

    const nextHead = head.next;
    head.next = null; // 断开连接
    return nextHead;
};

相似题目