33.排序链表

85 阅读1分钟

题目链接

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

解法1 暴力解法

思路

先不考虑空间的做法就是将所有节点都放在数组里,然后通过数组排序,最后修改链表指针。

代码

function sortList(head: ListNode | null): ListNode | null {
    if (!head || !head.next) {
        return head;
    }
    const nodeList = [];
    while (head) {
        nodeList.push(head);
        head = head.next;
    }

    nodeList.sort((a, b) => a.val - b.val);

    for (let i = 0; i < nodeList.length; i++) {
        nodeList[i].next = i === nodeList.length - 1 ? null : nodeList[i + 1];
    };

    return nodeList[0];
};

时空复杂度

时间复杂度:耗时最多是 sort 排序这里,渐进复杂度为 O(n logn)

空间复杂度:将所有链表都缓存了下来,所以是 O(n)

解法2 分治法

思路

我们可以先借鉴数组的排序,数组的分治就是先分层,然后对每层排序,最后将左右两边排序合并。

链表也可以这样,先通过快慢指针找到中点,然后递归调用找到左右节点,最后返回通过合并的节点。

代码

function sortList(head: ListNode | null): ListNode | null {
    if (!head || !head.next) {
        return head;
    }
    
    let prev = null;
    let slow = head;
    let fast = head;
    while (fast && fast.next) {
        prev = slow;
        slow = slow.next;
        fast = fast.next.next;
    }

    prev.next = null;
    const left = sortList(head);
    const right = sortList(slow);


    const merge = (left, right) => {
        const dummyHead = new ListNode(-1);
        let cur = dummyHead;

        while (left && right) {
            if (left.val < right.val) {
                cur.next = left;
                left = left.next;
            } else {
                cur.next = right;
                right = right.next;
            }
            cur = cur.next;
        }

        cur.next = left ?? right;
        return dummyHead.next;
    }

    return merge(left, right);
};

时空复杂度

时间复杂度:递归的时间复杂度是 O(logn),而每层递归渐进是 O(n),所以总的复杂度是 O(n logn)

空间复杂度:只有递归的栈空间 O(logn)