力扣刷题TOP101:10.BM12 单链表的排序

目录:

目的

思路

复杂度

记忆秘诀

python代码

目的

{1,3,2,4,5} 排序变成 {1,2,3,4,5}


思路

这个任务是将无序单链表变成有序表。推荐使用归并算法。可以理解为汉武帝的推恩令政策(分治思想)。将大块封地分成小块封地(分割链表),对小封地进行整顿,确保符合中央标准(分到最小),将整治好的封地“统一”到中央管理中(合并链表)。


分:递归拆分封地

  • 检查诸侯的封地:当一个封地已经小到只有一块地时(链表只有一个节点或为空),就无需再分封了。

  • 长子拿一半封地:为了分封,需要找到封地的中间点,然后将封地一分为二。

    • 左半部分是给长子继承的封地;
    • 右半部分是分给其他子孙的封地;right = mid.next(右侧封地的起点)
    • 使左侧和右侧完全独立。mid.next = None

治:治理每块小封地

要确保每块封地都封到无可再封的地步,长子和其他子孙的封地还要再封。

left_sorted = self.sortInList(head)

right_sorted = self.sortInList(right)


合:整合治理好的子封地:

  • 返回合并后的完整有序链表,这一步相当于将治理成果上报给中央,最终完成帝国范围内的整体治理return self.merge(left_sorted, right_sorted)。

  • 递归的本质是将链表逐步缩小,直到每段链表只有一个节点为止(递归终止条件),然后从底向上逐步排序合并。

中央辅助:两位中央监督员

这两位监督员巡视分封的封地,通过协调配合准确找到分封的中点。

  • 慢速监督员 slow从head开始 (封地的开端),一次检查一个,确保不遗漏任何细节。
  • 快速监督员 fast从head.next开始(封地稍远的位置),一次检查两个,快速推进巡视进度。
  • 当快速监督员走到封地末尾时(fastNonefast.nextNone),慢速监督员正好站在封地的中点。
    • 为什么是中点:

      • 快速监督员到链表的末尾时,快速监督员移动的总路径就是链表的长度 n
      • 慢速监督员每次移动一步,与快速监督员相比步数正好是其一半。当快速监督员到达末尾时,慢速监督员移动的总路径为:n/2。

*fast=head.next 是更安全的初始化方式,它避免了短链表中快指针越界的问题,并确保对奇偶长度链表都能正确找到中点。这种处理方式是归并排序和链表问题中最常用的技巧。


复杂度

  • 时间复杂度:O(nlogn)

    • 每次切分链表需要 O(logn),合并过程需要 O(n),所以总体为 O(nlogn)。
  • 空间复杂度:O(logn)

    • 递归调用栈的深度与链表长度的对数成正比。

记忆秘诀

  • 分阶段:通过中央指令,逐步分割封地。
  • 治阶段:对子封地分别进行整顿。
  • 合阶段:最终统一整合封地,确保整个国家井然有序。

python代码

# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None
#
# 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
#
# 
# @param head ListNode类 the head node
# @return ListNode类
#
class Solution:
    # 工具函数:合并两个有序链表
    def merge(self, l1: ListNode, l2: ListNode) -> ListNode:
        dummy = ListNode(0)  # 虚拟头节点
        tail = dummy
        
        # 合并两个有序链表
        while l1 and l2:
            if l1.val < l2.val:
                tail.next = l1
                l1 = l1.next
            else:
                tail.next = l2
                l2 = l2.next
            tail = tail.next

        # 如果l1还有剩余节点
        if l1:
            tail.next = l1
        # 如果l2还有剩余节点
        if l2:
            tail.next = l2

        return dummy.next
    
    # 工具函数:使用快慢指针法找到链表的中点
    def getMiddle(self, head: ListNode) -> ListNode:
        slow, fast = head, head.next
        while fast and fast.next:
            slow = slow.next
            fast = fast.next.next
        return slow
    
    # 归并排序主函数
    def sortInList(self, head: ListNode) -> ListNode:
        # 递归终止条件
        if not head or not head.next:
            return head
        
        # 找到链表的中点并将链表断成两部分
        mid = self.getMiddle(head)
        right = mid.next
        mid.next = None  # 将链表断开
        
        # 递归对左右两部分排序
        left_sorted = self.sortInList(head)
        right_sorted = self.sortInList(right)
        
        # 合并已排序的左右链表
        return self.merge(left_sorted, right_sorted)
        

还有一种更简单的方法,适合投机一点的,思路类似这道题:
 力扣刷题TOP101:4.BM5 合并k个已排序的链表-优快云博客

class Solution:
    def sortInList(self , head ):
        p = head
        nums = []
        #遍历链表,将节点值加入数组
        while p: 
            nums.append(p.val)
            p = p.next
        p = head
        #对数组元素排序
        nums.sort() 
        #遍历数组
        for i in range(len(nums)): 
            #将数组元素依次加入链表
            p.val = nums[i] 
            p = p.next
        return head

* 欢迎大家探讨新思路,能够更好的理解和记忆

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

乱七八糟2333

随缘打赏,一分也是爱!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值