LeetCode 148. Sort List (快速排序法) - 链表(Linked List)系列题20

这篇博客探讨了如何对链表进行排序,重点介绍了使用快速排序算法进行优化。首先,原始实现中由于选择头节点进行分区导致在处理大量相等节点或已排序链表时效率降低。为解决这些问题,提出了两种优化策略:一是将链表分为三个区,分别处理小于、等于和大于顶点值的节点;二是使用快慢指针找到中间节点作为顶点进行分区,以避免不均衡分区。优化后的代码能够有效地处理各种情况,实现链表的升序排序。
摘要由CSDN通过智能技术生成

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值