归并排序实现链表排序

175 阅读1分钟

归并排序实现链表排序

归并排序:

点击了解归并排序

递归版(自上而下)

 * 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 = (head) => {
    //如果链表为空或者链表只有一个结点,直接返回头结点
    if(head === null || head.next === null){
        return head
    }
    //对链表进行分组
    //为了方便分组,我们用快慢指针找到链表的中点,得到第二组的头结点
    let fast = head.next
    let slow = head
    while(fast !== null && fast.next !== null){
		fast = fast.next.next
        slow = slow.next
    }
    let head2 = slow.next
    slow.next = null
    
    //将链表分为两组
    let head1 = sortList(head)
    head2 = sortList(head2)
    
    //合并有序链表
    let cur = new ListNode()
    cur.next = head
    let res = cur
    
    while(head1 !== null && head2 !== null){
		if(head1.val < head2.val){
            cur.next = head1
            head1 = head1.next
        }else{
			cur.next = head2
            head2 = head2.next
        }
        cur = cur.next
    }
    cur.next = head1 !== null ? head1 : head2 
    return res.next
}

非递归版(自底向上)

所谓自底向上分组,即将一个链表划分为长度为一个结点的序列,这些序列两个一组比较结点值并按结点值大小排序。排序之后合并起来得到一个新的链表,然后我们再将新链表划分为长度为两个结点的序列,这些序列仍然按两个一组进行比较并排序,合并得到一个新的链表。以此类推,直到新链表无法继续划分,此时我们得到的新链表就是一个有序的链表。

    if(head === null || head.next === null)
        return head
    let node = new ListNode()
    node.next = head
    //获取链表长度
    let n = 0
    let cur = head
    while(cur){
        cur = cur.next
        n++
    }
    
    //自底向上分组
    let len = 1
    while(len < n){
        //设置指针,beforeHead指向两个序列中第一个序列的哨兵结点
        let beforeHead = node
        while(beforeHead !== null){
            let mid = beforeHead.next
            for(let i = 0 ; i < len ; i++){
                if(mid !== null)
                    mid = mid.next
                else break
            }
            //mid指向的是两序列中第二个序列的头结点
            let afterTail = mid
            for(let i = 0 ; i < len ; i++){
                if(afterTail !== null)
                    afterTail = afterTail.next
                else break
            }
            //afterTail指向的是下一组的第一个序列的头结点,或者为空
            merge(beforeHead,mid,afterTail)
            for(let i = 0 ; i < len*2 ; i++){
                if(beforeHead !== null)
                    beforeHead = beforeHead.next
                else break
            }
            //将beforeHead指向下一组的第一个序列的头结点,若beforeHead为null,本轮合并完成
        }
        //改变序列的长度大小,继续下一轮分组
        len*=2
    }
    return node.next
}

//合并排序链表
let merge = (beforeHead,mid,afterTail) => {
    let cur = beforeHead
    //设置第一个序列的头结点指针
    let head1 = beforeHead.next
    //设置第二个序列的头结点指针
    let head2 = mid
    while(head1 !== mid && head2 !== afterTail){
        if(head1.val < head2.val){
            cur.next = head1
            head1 = head1.next
        }else{
            cur.next = head2
            head2 = head2.next
        }
        cur = cur.next
    }
    while(head1 !== mid){
        cur.next = head1
        head1 = head1.next
        cur = cur.next
    }
    while(head2 !== afterTail){
		cur.next = head2
        head2 = head2.next
        cur = cur.next
    }
    cur.next = afterTail
}