Given the head
of a linked list, return the list after sorting it in ascending order.
Example 1:
Input: head = [4,2,1,3] Output: [1,2,3,4]
Example 2:
Input: head = [-1,5,3,4,0] Output: [-1,0,3,4,5]
Example 3:
Input: head = [] Output: []
Constraints:
- The number of nodes in the list is in the range
[0, 5 * 104]
. -105 <= Node.val <= 105
Follow up: Can you sort the linked list in O(n logn)
time and O(1)
memory (i.e. constant space)?
在刷这题时除了用归并排序148. Sort List ,还想过用快速排序(Quick Sort)法,其核心思想是选择一个顶点然后进行分区,把小于顶点值的所有节点以及顶点归到前半部分,把大于等于顶点值的所有节点归到后半部分,如果两个部分是有序的,那只要把前后两部分尾首相连整个链表就是有序的了。通过递归调用不停的分区直到最后只剩0或1个节点时就可以停止返回,再依次合并返回,最后实现整个链表排序。对于链表最简单的就是选择头节点为节点进行分区,首次实现的代码如下。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def sortList(self, head: Optional[ListNode]) -> Optional[ListNode]:
if not head or not head.next:
return head
h1, h2 = self.partition(head)
h1 = self.sortList(h1)
head.next = self.sortList(h2)
return h1
def partition(self, head):
h1, h2 = ListNode(-1), ListNode(-1)
p, p1, p2 = head, h1, h2
head = head.next
while head:
if head.val < p.val:
p1.next = head
p1 = p1.next
else:
p2.next = head
p2 = p2.next
head = head.next
p.next = None
p1.next, p2.next = p, None
return h1.next, h2.next
一跑代码超时不通过,从不通过的test case里会发现两种情况。
第一种情况是链表很长里面有很多值相等的节点,这就会造成有的分区里所有节点值都相等,对于这类分区其实已经是有序的了,如果再继续递归调用分成更小的分区就会浪费很多时间,因此可以优化分区算法,取一个顶点把这个链表分成3个区间,前面部分小于顶点值,中间部分等于顶点值,后面部分大于顶点值,只要继续递归处理前面和后面部分就行,最后把3个部分串在一起。
第二种情况是链表很长但已经是有序的了,这就会造成分区很不均衡,前面部分只有一个节点,剩下节点都被分到后面部分,使得时间复杂度变成O(n^2)。可以通过不选头节点做为顶点来分区进行优化,可以选中间节点或随机节点作为顶点,对于链表不容易获取随机节点,因此只能通过选中间节点,用我们非常熟悉的快慢指针会比较高效地获取中到间节点。
优化后代码就可以通过测试了。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def sortList(self, head: Optional[ListNode]) -> Optional[ListNode]:
if not head or not head.next:
return head
def partition(h):
def getMid(h):
if not h or not h.next:
return h
slow, fast = h, h.next
while fast and fast.next:
slow = slow.next
fast = fast.next.next
return slow
h1, h2, h3 = ListNode(-1), ListNode(-1), ListNode(-1)
p, p1, p2, p3 = getMid(h), h1, h2, h3
while h:
if h == p:
h = h.next
continue
if h.val < p.val:
p1.next = h
p1 = p1.next
elif h.val == p.val:
p2.next = h
p2 = p2.next
else:
p3.next = h
p3 = p3.next
h = h.next
p1.next = p
p.next, p2.next, p3.next = None, None, None
return h1.next, p, h2.next, p2, h3.next
h1, t1, h2, t2, h3 = partition(head)
h1 = self.sortList(h1)
if h2:
t1.next = h2
t1 = t2
t1.next = self.sortList(h3)
return h1