Sort a linked list in O(n log n)
time using constant space complexity.
这题思路比较直接。按照题目要求,需要在O(N*logN)时间内完成排序,所以可以考虑的排序方法只有三个:快速排序、归并排序和堆排序。然而,快速排序要求能在常数时间内置换两个指定元素,所以需要支持O(1)时间内的随机访问,这个无法在链表上实现,所以可以排除快速排序。此外,堆排序需要O(N)的额外空间进行堆的建立,而题目要求常数的空间复杂度,所以也可以排除。如此一来,只有归并排序可以考虑了。
按照分治法原理,其实也就只用实现两个步骤,即拆分链表和合并链表,都可以通过链表实现。把一个链表拆分成两半,简单做法就是先扫描一遍获得链表长度,然后再扫描一半长度进行拆分。这样需要两次扫描,虽然时间复杂度一样。更有效的方法是使用2个快慢指针,当快指针走到头的时候,慢指针会到达第一个的子链表的尾部。合并链表很直接,因为无论是用数组还是链表,都只用顺序扫描两个序列。
// merge the two sorted sublists private ListNode merge(ListNode head1, ListNode head2) { if (head1 == null) { return head2; } else if (head2 == null) { return head1; } ListNode head, prev; if (head1.val < head2.val) { head = head1; head1 = head1.next; } else { head = head2; head2 = head2.next; } prev = head; while (head1 != null && head2 != null) { if (head1.val < head2.val) { prev.next = head1; head1 = head1.next; } else { prev.next = head2; head2 = head2.next; } prev = prev.next; } if (head1 != null) { prev.next = head1; } else if (head2 != null) { prev.next = head2; } return head; } public ListNode sortList(ListNode head) { ListNode head1, head2; // sublist heads // split the list into two sublists if (head == null || head.next == null) { return head; } else { ListNode fastNode = head.next, slowNode = head; while (fastNode != null) { fastNode = fastNode.next; if (fastNode != null) { fastNode = fastNode.next; slowNode = slowNode.next; } } head1 = head; head2 = slowNode.next; slowNode.next = null; } // sort the two sublists recursively head1 = sortList(head1); head2 = sortList(head2); return merge(head1, head2); }
其实我本来想把拆分链表这一步也用一个函数封装的,不过这样的话要求对两个子链表的表头地址传引用,在Java里不好实现,不过可以在C++代码里引入二级指针或者对节点地址传引用实现。