本文描述了LeetCode 148题 sort-list 的解法。html
题目描述以下:
Sort a linked list in O(n log n) time using constant space complexity.java
题目要求咱们在O(n log n)时间复杂度下完成对单链表的排序,咱们知道平均时间复杂度为O(n log n)的排序方法有快速排序、归并排序和堆排序。而通常是用数组来实现二叉堆,固然能够用二叉树来实现,可是这么作太麻烦,还得花费额外的空间构建二叉树,因而不采用堆排序。
故本文采用快速排序和归并排序来对单链表进行排序。node
在通常实现的快速排序中,咱们经过首尾指针来对元素进行切分,下面采用快排的另外一种方法来对元素进行切分。git
咱们只须要两个指针p1和p2,这两个指针均往next方向移动,移动的过程当中保持p1以前的key都小于选定的key,p1和p2之间的key都大于选定的key,那么当p2走到末尾时交换p1与key值便完成了一次切分。github
图示以下:
数组
代码以下:ui
public ListNode sortList(ListNode head) { //采用快速排序 quickSort(head, null); return head; } public static void quickSort(ListNode head, ListNode end) { if (head != end) { ListNode node = partion(head, end); quickSort(head, node); quickSort(node.next, end); } } public static ListNode partion(ListNode head, ListNode end) { ListNode p1 = head, p2 = head.next; //走到末尾才停 while (p2 != end) { //大于key值时,p1向前走一步,交换p1与p2的值 if (p2.val < head.val) { p1 = p1.next; int temp = p1.val; p1.val = p2.val; p2.val = temp; } p2 = p2.next; } //当有序时,不交换p1和key值 if (p1 != head) { int temp = p1.val; p1.val = head.val; head.val = temp; } return p1; }
归并排序应该算是链表排序最佳的选择了,保证了最好和最坏时间复杂度都是nlogn,并且它在数组排序中广受诟病的空间复杂度在链表排序中也从O(n)降到了O(1)。spa
归并排序的通常步骤为:指针
首先用快慢指针(快慢指针思路,快指针一次走两步,慢指针一次走一步,快指针在链表末尾时,慢指针刚好在链表中点)的方法找到链表中间节点,而后递归的对两个子链表排序,把两个排好序的子链表合并成一条有序的链表。code
代码以下:
public ListNode sortList(ListNode head) { //采用归并排序 if (head == null || head.next == null) { return head; } //获取中间结点 ListNode mid = getMid(head); ListNode right = mid.next; mid.next = null; //合并 return mergeSort(sortList(head), sortList(right)); } /** * 获取链表的中间结点,偶数时取中间第一个 * * @param head * @return */ private ListNode getMid(ListNode head) { if (head == null || head.next == null) { return head; } //快慢指针 ListNode slow = head, quick = head; //快2步,慢一步 while (quick.next != null && quick.next.next != null) { slow = slow.next; quick = quick.next.next; } return slow; } /** * * 归并两个有序的链表 * * @param head1 * @param head2 * @return */ private ListNode mergeSort(ListNode head1, ListNode head2) { ListNode p1 = head1, p2 = head2, head; //获得头节点的指向 if (head1.val < head2.val) { head = head1; p1 = p1.next; } else { head = head2; p2 = p2.next; } ListNode p = head; //比较链表中的值 while (p1 != null && p2 != null) { if (p1.val <= p2.val) { p.next = p1; p1 = p1.next; p = p.next; } else { p.next = p2; p2 = p2.next; p = p.next; } } //第二条链表空了 if (p1 != null) { p.next = p1; } //第一条链表空了 if (p2 != null) { p.next = p2; } return head; }
完整代码放在:
https://github.com/morethink/algorithm/blob/master/src/main/java/algorithm/leetcode/L_148_SortList.java
参考文档: