Idiot-maker

  :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

https://leetcode.com/problems/sort-list/

Sort a linked list in O(n log n) time using constant space complexity.

解题思路:

常见的O(nlogn)算法,快速排序、归并排序,堆排序。大概讲讲优缺点,在数据量很大并且很乱的时候,快速排序有着最快的平均速度,比如Java和C++语言的库排序函数就主要是快排,但基本上是优化过的,因为快排有缺点。对于本来就已经排好序的数列,快排反而要花到O(n^2)的时间,因为如果总是选取第一个元素作为pivot,每次只能将n的数列化为1和n-1两部分,而不是相等的两部分。而且快速排序不是稳定的,因为pivot最后被交换到某一个位置,是不确定的。由于快速排序一般用递归,所以需要O(logn)的额外空间,(递归树的高度)。最坏情况需要O(n),也就是上面说的情况,递归树一边倒。

堆排序在最坏情况下,也能有O(nlogn)的时间,这点比快排好,而且不需要额外的存储空间。但是它也是不稳定的。

归并排序用在数组上的时候,好处就是稳定的,而且最坏情况下,也能有O(nlogn)的时间,这点比快排好。但是它需要花O(n)的额外空间,来存放归并的结果。但是用在链表上的时候,可以不需要这O(n)的空间,因为链表的排序完全可以做到随意移动-插入,类似in-place。归并排序还有一个重要的用途,用在外排序上,比如海量数据存在n个文件中。

好了,看题目,这道题是链表,用归并排序是最合适的。题目 Merge Two Sorted Lists 中我们已经解决了,两个已经排序的链表的归并,那么这道题就是类似数组归并排序的概念,用递归从顶之下。将原链表不断分成两个链表,再分为两个,直到只有一个元素。然后再从低至上,两两归并排序它们,直到合成一个整的。

这里要注意理解的就是,每个递归的返回值应该怎么用。

/**
 * Definition for singly-linked list.
 * class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode sortList(ListNode head) {
        return mergeSortList(head);
    }
    
    public ListNode mergeSortList(ListNode head){
        if(head == null || head.next == null) {
            return head;
        }
        ListNode fast = head;
        ListNode slow = head;
        //取终点的方法,是判断fast.next和fast.next.next,而不是fast和fast.next
        while(fast.next != null){
            fast = fast.next;
            if(fast.next != null){
                fast = fast.next;
                slow = slow.next;
            }
        }
        ListNode mid = slow.next;
        //重要,将原链表从中间断开,可以不要考虑前后两段的连接了,这个问题交给下面的mergeList方法
        slow.next = null;
        head = mergeSortList(head);
        mid = mergeSortList(mid);
        return mergeList(head, mid);
    }
    
    public ListNode mergeList(ListNode l1, ListNode l2) {
        ListNode dummy = new ListNode(0);
        ListNode current = dummy;
        while(l1 != null && l2 != null){
            if(l1.val < l2.val){
                current.next = l1;
                l1 = l1.next;
                current = current.next;
            }else{
                current.next = l2;
                l2 = l2.next;
                current = current.next;
            }
        }
        if(l1 != null){
            current.next = l1;
        }else{
            current.next = l2;
        }
        return dummy.next;
    }
}

update 2015/06/21:

三刷,把取中间节点的方法提取了出来。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
public class Solution {
    public ListNode sortList(ListNode head) {
        if(head == null || head.next == null) {
            return head;
        }
        ListNode mid = getMidNode(head);
        head = sortList(head);
        mid = sortList(mid);
        return mergeList(head, mid);
    }
    
    public ListNode getMidNode(ListNode head) {
        if(head == null || head.next == null) {
            return head;
        }
        ListNode slow = new ListNode(0);
        slow.next = head;
        while(head != null) {
            head = head.next;
            if(head != null) {
                head = head.next;
                slow = slow.next;
            }
        }
        ListNode res = slow.next;
        slow.next = null;//这一步很重要
        return res;
    }
    
    public ListNode mergeList(ListNode l1, ListNode l2) {
        ListNode dummy = new ListNode(0);
        ListNode current = dummy;
        while(l1 != null && l2 != null){
            if(l1.val < l2.val){
                current.next = l1;
                l1 = l1.next;
                current = current.next;
            }else{
                current.next = l2;
                l2 = l2.next;
                current = current.next;
            }
        }
        if(l1 != null){
            current.next = l1;
        }else{
            current.next = l2;
        }
        return dummy.next;
    }
}

 //20180920

https://www.geeksforgeeks.org/merge-sort/

注意的是针对链表的归并取的并不是mid node,而是mid的下一个node。

归并的一般步骤

MergeSort(arr[], l,  r)
If r > l
     1. Find the middle point to divide the array into two halves:  
             middle m = (l+r)/2
     2. Call mergeSort for first half:   
             Call mergeSort(arr, l, m)
     3. Call mergeSort for second half:
             Call mergeSort(arr, m+1, r)
     4. Merge the two halves sorted in step 2 and 3:
             Call merge(arr, l, m, r)
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode sortList(ListNode head) {
        if (head == null || head.next == null) {
            return head;
        }
        ListNode midNext = findMidNext(head);
        ListNode head1 = sortList(head);
        ListNode head2 = sortList(midNext);
        return merge(head1, head2);
    }
    
    public ListNode findMidNext(ListNode head) {
        if (head == null || head.next == null) {
            return head;
        }
        ListNode dummy = new ListNode(-1);
        dummy.next = head;
        ListNode fast = head;
        ListNode slow = head;
        while (fast.next != null && fast.next.next != null) {
            fast = fast.next.next;
            slow = slow.next;
        }
        ListNode midNext = slow.next;
        slow.next = null;
        return midNext;
    }
    
    public ListNode merge(ListNode head1, ListNode head2) {
        ListNode dummy = new ListNode(-1);
        ListNode iter = dummy;
        while (head1 != null && head2 != null) {
            if (head1.val <= head2.val) {
                iter.next = head1;
                head1 = head1.next;
            } else {
                iter.next = head2;
                head2 = head2.next;
            }
            iter = iter.next;
        }
        if (head1 == null) {
            iter.next = head2;
        } else {
            iter.next = head1;
        }
        return dummy.next;
    }
}

 

posted on 2015-03-28 20:39  NickyYe  阅读(257)  评论(0)    收藏  举报