1.问题描述
Sort a linked list in O(n log n) time using constant space complexity.
2.翻译
在固定的空间复杂度中使用O(nlog n)的时间复杂度进行链表的排序。
3.思路分析
提起排序,我们脑海中会迅速出现各种排序算法:冒泡排序、快速排序、简单排序、堆排序、直接插入排序、希尔排序(递减增量排序)、直接选择排序、堆排序、归并排序,其中能够有O(n lgn)时间复杂度的算法为,快速排序,堆排序,归并排序,而三者的空间复杂度分别为O(1), O(N),O(N),理论上讲为了满足题目的要求我们需要在这是哪个排序算法里面找出需要的最合适的排序算法。在这里我们采用归并排序算法来实现,归并排序的空间复杂度为O(N), 也就是说需要开出O(N)的额外空间来保存数组信息。因为是链表,我们只需要修改指针即可,我们只需要O(1)的空间复杂度即可。为实现归并排序我们需要首先了解归并排序的思想,以及找到中间节点还有就是合并有序序列,
归并排序的原理将原序列划分为有序的两个序列,然后利用归并算法进行合并,合并之后即为有序序列。既然明确了算法,那么我们就可以开始实现了。
找到中间的节点:因为排序的对象是个链表我们没办法一步到位的获取到链表的长度换句话说没办法拿到中间节点,我们只能通过遍历链表来实现了。为了获取中间节点我们需要设置两个指针,一个是指针移动慢些一个移动快些,然后确定当快指针移动链表末尾的时候慢指针能够拿到中间节点,这样就获取到了中间节点。
合并:就是需要比较链表的值然后遍历循环了。
4.实现代码
链表节点(ListNode),ListNode.java
package sortList; public class ListNode { int val; ListNode next; ListNode(int x) { val = x; next = null; } }
排序加合并实现Solution.java
package sortList; public class Solution { public ListNode sortList(ListNode head) { ListNode slow_p = head; ListNode fast_p = head; if (head == null) { return null; } ListNode pre = head; while(slow_p.next != null && fast_p != null && fast_p.next != null ){ pre = slow_p; slow_p = slow_p.next; fast_p = fast_p.next.next; } pre.next = null; ListNode res = mergeSort(head, slow_p); return res; } private ListNode mergeSort(ListNode l1, ListNode l2){ if (l2 == null || l1 == l2) { return l1; } if(l1 == null){ return l2; } ListNode slow_p = l1; ListNode fast_p = l1; ListNode pre = l1; while(slow_p.next != null && fast_p != null && fast_p.next != null ){ pre = slow_p; slow_p = slow_p.next; fast_p = fast_p.next.next; } pre.next = null; ListNode head1 = mergeSort(l1, slow_p); slow_p = l2; fast_p = l2; pre = l2; while(slow_p.next != null && fast_p != null && fast_p.next != null ){ pre = slow_p; slow_p = slow_p.next; fast_p = fast_p.next.next; } pre.next = null; ListNode head2 = mergeSort(l2, slow_p); ListNode res = new ListNode(0); ListNode cur = res; while (head1 != null && head2 != null) { if (head1.val < head2.val) { cur.next = head1; head1 = head1.next; } else{ cur.next = head2; head2 = head2.next; } cur = cur.next; } while (head1 != null) { cur.next = head1; head1 = head1.next; cur = cur.next; } while (head2 != null) { cur.next = head2; head2 = head2.next; cur = cur.next; } return res.next; } }
TestSolution.java
package sortList; public class TestSolution { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub ListNode node = new ListNode(0); ListNode head = node; ListNode tempHead = head; for(int index = 0;index < 10;index++){ int temp = (int) (Math.random()*100); ListNode tempNode = new ListNode(temp); node.next = tempNode; node = tempNode; } System.out.println("排序前的链表的值"); while(head.next!=null){ head = head.next; System.out.println(head.val); } ListNode result = new Solution().sortList(tempHead); System.out.println("排序后的链表的值"); while(result.next!=null){ result = result.next; System.out.println(result.val); } } }
执行结果如下:
从测试的结果可以看出的确实现了题目的要求。
5.找到链表的中间节点不难,比较难的的子链表的合并上面,需要考虑的逻辑比较复杂。