【JS每日一算法】🟨143.排序链表(插入法、归并)

123 阅读4分钟

给你链表的头结点 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 = []
输出: []

  提示:

  • 链表中节点的数目在范围 [0, 5 * 104] 内
  • -105 <= Node.val <= 105  

进阶: 你可以在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序吗?

题解:

个人博客

更多JS版本题解点击链接关注该仓库👀

/**
 * @description: 插入排序  TC:O(n^2)  SC:O(1)
 * @author: JunLiangWang
 * @param {*} head 给定链表头节点
 * @return {*}
 */
function insertSort(head) {

    /**
     * 本方案采用插入排序的方式,与上一题一致,较为简单,但是在
     * 具体写得时候有很多需要注意得地方,比对交换两节点指针不能
     * 指向当前需要交换的两节点,而是指向其上一个节点通过next来
     * 访问,因为移动节点需要改变其上一个节点的next值
     */
    if (!head) return head
    const HEAD = new ListNode(0, head)
    let selectNode = HEAD;

    while (selectNode.next) {
        let compareNode = HEAD, isChange = false;
        // 遍历比对节点
        while (compareNode.next && compareNode.next != selectNode.next) {
            // 如果大于则需要移动节点
            if (compareNode.next.val > selectNode.next.val) {
                let nextNode = selectNode.next.next
                selectNode.next.next = compareNode.next
                compareNode.next = selectNode.next
                selectNode.next = nextNode
                isChange = true;
                break;
            }
            compareNode = compareNode.next
        }
        if (!isChange) selectNode = selectNode.next
    }
    return HEAD.next
}


/**
 * @description: 归并排序  TC:O(nlogn)  SC:O(1)
 * @author: JunLiangWang
 * @param {*} head 给定链表头节点
 * @return {*}
 */
function mergeSort(head) {
    /* 对链表自顶向下归并排序的过程如下:
         1.找到链表的中点,以中点为分界,将链表拆分成两个子链表。
           寻找链表的中点可以使用快慢指针的做法,快指针每次移动 
           2 步,慢指针每次移动1步,当快指针到达链表末尾时,慢指
           针指向的链表节点即为链表的中点。
         2.对两个子链表分别排序。
         3.将两个排序后的子链表合并,得到完整的排序后的链表。
     */

    /**
     * @description: 两链表排序
     * @author: JunLiangWang
     * @param {*} list1 链表1
     * @param {*} list2 链表2
     * @return {*}
     */         
    function sort(list1, list2) {
        const newList = new ListNode(0);
        let newHead = newList, head1 = list1, head2 = list2;
        while (head1 !== null && head2 !== null) {
            if (head1.val < head2.val) {
                newHead.next = head1
                head1 = head1.next
            }
            else {
                newHead.next = head2
                head2 = head2.next
            }
            newHead = newHead.next
        }
        if (head1) newHead.next = head1
        else newHead.next = head2
        return newList.next;
    }
    /**
     * @description: 归并排序
     * @author: JunLiangWang
     * @param {*} head 开始节点
     * @param {*} tail 结束节点
     * @return {*}
     */    
    function mergeSort(head, tail) {
        if (head == null) return head
        if (head.next == tail) {
            head.next = null
            return head
        }
        let slowPoint = head, fastPoint = head
        while (fastPoint !== tail) {
            slowPoint = slowPoint.next;
            fastPoint = fastPoint.next;
            if (fastPoint !== tail) fastPoint = fastPoint.next;
        }
        let list1 = mergeSort(head, slowPoint),
            list2 = mergeSort(slowPoint, tail)
        return sort(list1, list2)
    }
    // 执行排序
    return mergeSort(head, null)
}

/**
 * @description: 插入排序  TC:O(n^2)  SC:O(1)
 * @author: JunLiangWang
 * @param {*} head 给定链表头节点
 * @return {*}
 */
function insertSort(head) {

    /**
     * 本方案采用插入排序的方式,与上一题一致,较为简单,但是在
     * 具体写得时候有很多需要注意得地方,比对交换两节点指针不能
     * 指向当前需要交换的两节点,而是指向其上一个节点通过next来
     * 访问,因为移动节点需要改变其上一个节点的next值
     */
    if (!head) return head
    const HEAD = new ListNode(0, head)
    let selectNode = HEAD;

    while (selectNode.next) {
        let compareNode = HEAD, isChange = false;
        // 遍历比对节点
        while (compareNode.next && compareNode.next != selectNode.next) {
            // 如果大于则需要移动节点
            if (compareNode.next.val > selectNode.next.val) {
                let nextNode = selectNode.next.next
                selectNode.next.next = compareNode.next
                compareNode.next = selectNode.next
                selectNode.next = nextNode
                isChange = true;
                break;
            }
            compareNode = compareNode.next
        }
        if (!isChange) selectNode = selectNode.next
    }
    return HEAD.next
}


/**
 * @description: 归并排序  TC:O(nlogn)  SC:O(1)
 * @author: JunLiangWang
 * @param {*} head 给定链表头节点
 * @return {*}
 */
function mergeSort(head) {
    /* 对链表自顶向下归并排序的过程如下:
         1.找到链表的中点,以中点为分界,将链表拆分成两个子链表。
           寻找链表的中点可以使用快慢指针的做法,快指针每次移动 
           2 步,慢指针每次移动1步,当快指针到达链表末尾时,慢指
           针指向的链表节点即为链表的中点。
         2.对两个子链表分别排序。
         3.将两个排序后的子链表合并,得到完整的排序后的链表。
     */

    /**
     * @description: 两链表排序
     * @author: JunLiangWang
     * @param {*} list1 链表1
     * @param {*} list2 链表2
     * @return {*}
     */         
    function sort(list1, list2) {
        const newList = new ListNode(0);
        let newHead = newList, head1 = list1, head2 = list2;
        while (head1 !== null && head2 !== null) {
            if (head1.val < head2.val) {
                newHead.next = head1
                head1 = head1.next
            }
            else {
                newHead.next = head2
                head2 = head2.next
            }
            newHead = newHead.next
        }
        if (head1) newHead.next = head1
        else newHead.next = head2
        return newList.next;
    }
    /**
     * @description: 归并排序
     * @author: JunLiangWang
     * @param {*} head 开始节点
     * @param {*} tail 结束节点
     * @return {*}
     */    
    function mergeSort(head, tail) {
        if (head == null) return head
        if (head.next == tail) {
            head.next = null
            return head
        }
        let slowPoint = head, fastPoint = head
        while (fastPoint !== tail) {
            slowPoint = slowPoint.next;
            fastPoint = fastPoint.next;
            if (fastPoint !== tail) fastPoint = fastPoint.next;
        }
        let list1 = mergeSort(head, slowPoint),
            list2 = mergeSort(slowPoint, tail)
        return sort(list1, list2)
    }
    // 执行排序
    return mergeSort(head, null)
}