148. 排序链表

181 阅读3分钟

方法一:归并排序递归实现,空间复杂度O(logN)

思路:

  • 自上而下从中间切分链表,当两个链表都为一个节点时,直接merge,然后上层调用。
  • 这里找中点用的是fast.next != null && fast.next.next != null,偶数链表slow会停在中轴前一个节点,方便findMid(head).next = null截取
//递归版,空间复杂度O(logN)
class Solution {
    public ListNode sortList(ListNode head) {
        //只剩一个node返回,head==null为特判
        if (head == null || head.next == null) {
            return head;
        }
        ListNode mid = findMid(head).next;//后半截
        findMid(head).next = null;//截取前半截

        ListNode left = sortList(head);//排好序的前半截
        ListNode right = sortList(mid);//排好序的后半截

        return merge(left, right);
    }
    //合并两个有序链表,返回新链表的头节点
    public ListNode merge(ListNode node1, ListNode node2) {
        ListNode dummy = new ListNode(0);
        ListNode cur = dummy;
        //挨个比较两根链表的节点,大的先接到后面
        while (node1 != null && node2 != null) {
            if (node1.val <= node2.val) {
                cur.next = node1;
                cur = cur.next;
                node1 = node1.next;
            } else {
                cur.next = node2;
                cur = cur.next;
                node2 = node2.next;
            }
        }
        //如果两根链表不一样长,直接把剩下的接上
        if (node1 != null) {
            cur.next = node1;
        }
        //如果两根链表不一样长,直接把剩下的接上
        if (node2 != null) {
            cur.next = node2;
        }
        return dummy.next;
    }
    //快慢指针找链表的中间节点,1->2->3->4返回2之后,1->2->3->4->5返回3之后
    public ListNode findMid(ListNode node) {
        ListNode fast = node;
        ListNode slow = node;
        while (fast.next != null && fast.next.next != null) {
            fast = fast.next.next;
            slow = slow.next;
        }
        return slow;
    }
}

方法二:归并排序迭代实现,空间复杂度O(1)

思路:

  • 用1,2,4,8,...为stride扫链表,直到stride超出链表长度。这样可以做到两两合并。
  • 在每个固定的stride下,用cur把链表扫一遍,把相邻stride的两个链表头merge。
  • 细节处理:当链表长度无法被strtide整除,那么最后一组的长度肯定小于stride,getStrideEnd()中注意实现。
//迭代版,空间复杂度O(1)
class Solution {
    public ListNode sortList(ListNode head) {
        ListNode dummy = new ListNode();
        dummy.next = head;
        int length = getLength(head);
        //以不同的stride扫链表。由于是两两归并,stride应以2的指数扩大
        for (int stride = 1; stride <= length; stride *= 2) {
            ListNode pre = dummy;
            ListNode cur = dummy.next;
            //以当前stride大小扫一遍链表
            while (cur != null) {
                ListNode end1 = getStrideEnd(cur, stride);
                ListNode end2 = getStrideEnd(end1.next, stride);//end1.next可能为null,导致end2为null
                ListNode head1 = cur;//第一段的起始节点
                ListNode head2 = end1.next;//第二段的起始节点
                end1.next = null;//截取第一段,长度为stride
                //防止空指针:end2可能为null,当stride=1时。
                if (end2 != null) {
                    cur = end2.next ;
                    end2.next = null;//截取第二段,长度为stride
                } else {
                    cur = null;
                }
                //把前面排好序的和刚刚merge的两段接起来,pre是前面排好序的最后一个节点
                pre.next = merge(head1, head2);
                //pre移到merge后的链表的最后一个节点
                while (pre.next != null) {
                    pre = pre.next;
                }
            }
        }
        return dummy.next;
    }
    
    //获取每个stride中的最后一个节点
    public ListNode getStrideEnd(ListNode node, int stride) {
        if (node == null) {
            return null;
        }
        int i = 1;//注意这里是1,当上面的循环中stride为1,表明是自己merge自己。
        ListNode cur = node;
        while (i < stride && cur.next != null) {
            cur = cur.next;
            i++;
        }
        return cur;
    }
    
    //求链表长度
    public int getLength(ListNode head) {
        int length = 0;
        ListNode cur = head;
        while (cur != null) {
            cur = cur.next;
            length++;
        }
        return length;
    }
    
    //合并两个有序链表,返回新链表的头节点
    public ListNode merge(ListNode node1, ListNode node2) {
        ListNode dummy = new ListNode(0);
        ListNode cur = dummy;
        //挨个比较两根链表的节点,大的先接到后面
        while (node1 != null && node2 != null) {
            if (node1.val <= node2.val) {
                cur.next = node1;
                cur = cur.next;
                node1 = node1.next;
            } else {
                cur.next = node2;
                cur = cur.next;
                node2 = node2.next;
            }
        }
        //如果两根链表不一样长,直接把剩下的接上
        if (node1 != null) {
            cur.next = node1;
        }
        //如果两根链表不一样长,直接把剩下的接上
        if (node2 != null) {
            cur.next = node2;
        }
        return dummy.next;
    }
}