原题链接: http://oj.leetcode.com/problems/sort-list/
对链表进行排序,要求的时间复杂度为O(n log n)。nlogn的排序有快速排序、归并排序、堆排序。双向链表用快排比较适合,堆排序也可以用于链表,单向链表适合用归并排序。题目要求的是常数的空间复杂度,因为这里用了递归,如果算上栈空间的话,也要 o(logn)的复杂度。
关于链表的划分,这里使用了快慢指针,从中间节点进行切开成单独的链表。在merge之后又会合并成一个链表。
以下分别用归并排序和快速排序实现(Java),遗憾的是快速排序的实现超时了,毕竟交换数据的次数过多,没有归并排序之家更换指针要快。快速排序的实现大家可以忽略,代码写的不太好。
关于链表的归并排序可参考之前的一篇文章归并排序对链表进行排序
,只是实现比下面这个有些复杂。
1. 归并排序实现
01 | public class MergetSortList { |
02 |
03 | public static ListNode sortList(ListNode head) { |
04 | if (head == null || head.next == null ) return head; |
05 | ListNode slow = head; |
06 | ListNode fast = head; |
07 | //用快慢指针找到中间节点 |
08 | while (fast.next != null && fast.next.next != null ){ |
09 | slow = slow.next; |
10 | fast = fast.next.next; |
11 | } |
12 | ListNode list2 = slow.next; |
13 | slow.next = null ; |
14 | head = sortList(head); |
15 | list2 = sortList(list2); |
16 | return merge(head, list2); |
17 | } |
18 |
19 | private static ListNode merge(ListNode list1, ListNode list2) { |
20 | if (list1 == null ) return list2; |
21 | if (list2 == null ) return list1; |
22 | ListNode newHead = new ListNode( 0 ); //链表头不存储实际数据 |
23 | ListNode last = newHead; |
24 | last = newHead; |
25 | //连接每个节点,只更换指针,因此空间复杂度为O(1) |
26 | while (list1 != null && list2 != null ){ |
27 | if (list1.val < list2.val){ |
28 | last.next = list1; |
29 | list1 = list1.next; |
30 | } else { |
31 | last.next = list2; |
32 | list2 = list2.next; |
33 | } |
34 | last = last.next; |
35 | } |
36 | //最后剩余的部分,直接连接起来即可 |
37 | if (list1 != null ) last.next = list1; |
38 | else if (list2 != null ) last.next = list2; |
39 | return newHead.next; |
40 | } |
41 |
42 | public static void main(String[] args) { |
43 | ListNode l1 = new ListNode( 8 ); |
44 | ListNode l2 = new ListNode( 7 ); |
45 | ListNode l3 = new ListNode( 6 ); |
46 | ListNode l4 = new ListNode( 5 ); |
47 | ListNode l5 = new ListNode( 4 ); |
48 | ListNode l6 = new ListNode( 3 ); |
49 |
50 | l1.next = l2; |
51 | l2.next = l3; |
52 | l3.next = l4; |
53 | l4.next = l5; |
54 | l5.next = l6; |
55 |
56 | l1 = sortList(l1); |
57 |
58 | while (l1 != null ){ |
59 | System.out.print(l1.val + " " ); |
60 | l1 = l1.next; |
61 | } |
62 | } |
63 | } |
2.快速排序实现
01 | //以end节点作为pivot进行划分,返回划分的节点的前一个节点 |
02 | public static ListNode partition(ListNode head, ListNode end){ |
03 | int pivot = end.val; |
04 | ListNode lastSmallNode = null ; |
05 | ListNode tmpHead = head; |
06 | while (tmpHead != end){ |
07 | if (tmpHead.val < pivot){ |
08 | if (lastSmallNode == null ) lastSmallNode = head; |
09 | else lastSmallNode = lastSmallNode.next; |
10 | int tmp = lastSmallNode.val; |
11 | lastSmallNode.val = tmpHead.val; |
12 | tmpHead.val = tmp; |
13 | } |
14 | tmpHead = tmpHead.next; |
15 | } |
16 | //有可能是划分的节点就是第一个,此时返回null |
17 | if (lastSmallNode == null ){ |
18 | end.val = head.val; |
19 | head.val = pivot; |
20 | return null ; |
21 | } else { |
22 | end.val = lastSmallNode.next.val; |
23 | lastSmallNode.next.val = pivot; |
24 | return lastSmallNode; |
25 | } |
26 | } |
27 |
28 | public static void quickSort(ListNode head, ListNode end){ |
29 | if ( head == null || head == end || end == null || head.next == null ) return ; |
30 | ListNode mid = partition(head, end); |
31 | quickSort(head, mid); |
32 | //空表示,划分点是第一个位置 |
33 | if (mid == null ) |
34 | quickSort(head.next, end); |
35 | //如果划分点是最后一个位置,就无需再排序 |
36 | else if (mid != end && mid.next != null && mid.next != end) |
37 | quickSort(mid.next.next, end); |
38 | } |
39 |
40 | public static ListNode sortList(ListNode head) { |
41 | if (head == null || head.next == null ) return head; |
42 | ListNode tail = head; |
43 | while (tail.next != null ) |
44 | tail = tail.next; |
45 | quickSort(head,tail); |
46 | return head; |
47 | } |