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; } }
原文:http://www.cnblogs.com/NickyYe/p/4374758.html