LeetCode148. 排序链表

 

链表的归并操作不需要额外空间,因而使用归并排序符合本题常数级空间复杂度的要求。最适合单链表的排序算法是归并排序。

☆☆☆思路1:自顶向下的归并排序(递归)     时间复杂度O(nlogn),  空间复杂度O(logn),其中空间复杂度主要取决于递归调用的栈空间。

    主要考察:1)归并排序思想;2)寻找链表中间节点;3)合并两个有序链表

☆☆☆☆思路2:自底向上的归并排序(迭代)   时间复杂度O(nlogn),  空间复杂度O(1)

☆☆☆☆思路3:单链表的快排实现(面试常考)

代码1:(归并 递归)

class Solution {
    public ListNode sortList(ListNode head) {
        if (head == null || head.next == null)
            return head;
        ListNode fast = head, slow = head;
        while (fast.next != null && fast.next.next != null) {
            fast = fast.next.next;
            slow = slow.next;
        }
        ListNode right = slow.next;
        slow.next = null;
        ListNode l1 = sortList(head);
        ListNode l2 = sortList(right);
        return merge(l1, l2);
    }
    //  合并两个有序链表
    public ListNode merge(ListNode l1, ListNode l2) {
        ListNode dummyHead = new ListNode(-1);
        ListNode cur = dummyHead;
        while (l1 != null && l2 != null) {
            if (l1.val < l2.val) {
                cur.next = l1;
                l1 = l1.next;
            }else {
                cur.next = l2;
                l2 = l2.next;
            }
            cur = cur.next;
        }
        if (l1 != null) cur.next = l1;
        if (l2 != null) cur.next = l2;
        return dummyHead.next;
    }
}

 

代码2:(归并 非递归)

class Solution {
    public ListNode sortList(ListNode head) {
        if (head == null || head.next == null) return head;
        int len = getLength(head);
        ListNode dummyHead = new ListNode(-1);
        dummyHead.next = head;
        for (int step = 1; step < len; step *= 2) { //依次将链表分成1块,2块,4块...
            //每次变换步长,pre指针和cur指针都初始化在链表头
            ListNode pre = dummyHead, cur = dummyHead.next;
            while (cur != null) {
                ListNode head1 = cur; //第一部分头(第二次循环之后,cur为剩余部分头,
                ListNode head2 = split(head1, step); //第二部分头
                cur = split(head2, step); //剩余部分的头
                ListNode temp = merge(head1, head2);
                pre.next = temp;
                while (pre.next != null) {
                    pre = pre.next;  //把pre指针移动到排序好的部分的末尾
                }
            }
        }
        return dummyHead.next;
    }
    // 断链操作 返回第二部分链表头
    public ListNode split(ListNode head,int step){
        if (head == null) return head;
        ListNode cur = head;
        for (int i = 1; i < step && cur.next != null; i++) { // 注意 i 从1开始
            cur = cur.next;
        }
        ListNode right = cur.next;
        cur.next = null;
        return right;
    }
    // 获取链表长度
    public int getLength(ListNode head) {
        int len = 1;
        while (head.next != null) {
            head = head.next;
            len ++;
        }
        return len;
    }
    //  合并两个有序链表
    public ListNode merge(ListNode l1, ListNode l2) {
        ListNode dummyHead = new ListNode(-1);
        ListNode cur = dummyHead;
        while (l1 != null && l2 != null) {
            if (l1.val <= l2.val) {
                cur.next = l1;
                l1 = l1.next;
            }else {
                cur.next = l2;
                l2 = l2.next;
            }
            cur = cur.next;
        }
        if (l1 != null) cur.next = l1;
        if (l2 != null) cur.next = l2;
        return dummyHead.next;
    }
}

 

代码3:(快排)

具体过程如下:

  1. 将原链表中所有节点依次划分成三个链表,分别为small代表左部分,equal代表中间部分,big代表有部分。

  2. 将small、equal和big三个链表重新串起来即可。

  3. 整个过程需要特别注意对null节点的判断和处理。

class Solution {
    public ListNode sortList(ListNode head) {
        return quickSort(head)[0];
    }
    private ListNode[] quickSort(ListNode head) {
        if (head == null || head.next == null)
            return new ListNode[]{head, head};
        ListNode[][] parAns = listPartition(head);
        ListNode[] left = quickSort(parAns[0][0]);
        ListNode[] right = quickSort(parAns[2][0]);
        // 小的和相等的重新连接
        if (left[1] != null) {
            left[1].next = parAns[1][0];
            parAns[1][1] = parAns[1][1] != null ? parAns[1][1] : left[1];
        }
        // 所有的重新连接
        if (parAns[1][1] != null) {
            parAns[1][1].next = right[0];
        }
        // 返回的数组中第一个是头节点,第二个是尾节点
        return new ListNode[]{left[0] != null ? left[0] : (parAns[1][0] != null ? parAns[1][0] : right[0]),
                              right[1] != null ? right[1] : (parAns[1][1] != null ? parAns[1][1] : left[1])};
    }
    private ListNode[][] listPartition(ListNode head) {
        ListNode sH = null; // 小的头
        ListNode sT = null; // 小的尾
        ListNode eH = null; // 相等的头
        ListNode eT = null; // 相等的尾
        ListNode bH = null; // 大的头
        ListNode bT = null; // 大的尾
        ListNode next = null; // 保存下一个节点
        int pivot = head.val;
        while (head != null) {
            next = head.next;
            head.next = null; // 删除可以AC,否则会超时
            if (head.val < pivot) {
                if (sH == null) {
                    sH = head;
                    sT = head;
                }else {
                    sT.next = head;
                    sT = head;
                }
            }else if (head.val == pivot) {
                if (eH == null) {
                    eH = head;
                    eT = head;
                }else{
                    eT.next = head;
                    eT = head;
                }
            }else { // head.val > pivot
                if (bH == null) {
                    bH = head;
                    bT = head;
                }else {
                    bT.next = head;
                    bT = head;
                }
            }
            head = next;
        }
        if (sT != null) sT.next = null;
        if (eT != null) eT.next = null;
        if (bT != null) bT.next = null;
        return new ListNode[][]{{sH,sT},{eH,eT},{bH,bT}};
    }
}

 

单链表快排参考:

    左神书P55

    单链表快排,字节面试原题

 

posted @ 2020-12-18 11:03  不学无墅_NKer  阅读(126)  评论(0编辑  收藏  举报