算法148. 排序链表

题目:

给你链表的头结点 head ,请将其按 升序 排列并返回 排序后的链表 。

示例 1:
在这里插入图片描述
输入:head = [4,2,1,3]
输出:[1,2,3,4]
示例 2:
在这里插入图片描述

输入:head = [-1,5,3,4,0]
输出:[-1,0,3,4,5]

示例 3:
输入:head = []
输出:[]

解法:

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:# 链表的中间节点(快慢指针法)def middleNode(self,head:Optional[ListNode])->Optional[ListNode]:if not head:return Noneslow=headfast=headwhile fast and fast.next:pre = slowslow=slow.nextfast=fast.next.nextpre.next = None # 断开slow的前一个节点和slow节点,拆分成两个链表return slow# 合并两个有序链表(双指针)def mergeTwoLists(self,list1:Optional[ListNode],list2:Optional[ListNode])->Optional[ListNode]:# 用哑节点标记链表头节点cur = dummy = ListNode()# 遍历两个有序链表,直到其中一个遍历完while list1 and list2:if list1.val >= list2.val:cur.next=list2list2=list2.nextelse:cur.next=list1list1=list1.nextcur=cur.next# 将没有遍历完的拼接到最后cur.next=list1 if list1 else list2return dummy.next# 排序链表def sortList(self,head:Optional[ListNode])->Optional[ListNode]:# 如果链表为空或者只有一个节点,无需排序if head is None or head.next is None:return head# 找到中间节点head2,并断开head2与其前一个节点的联接head2 = self.middleNode(head)# 分治,比如把[4,2,1,3]拆成[4,2]和[1,3],然后把[4,2]拆成[4][2],[1,3]拆成[1][3]head = self.sortList(head)head2 = self.sortList(head2)#合并return self.mergeTwoLists(head,head2)

解析:

这段代码是链表的归并排序(Merge Sort)实现,非常经典!我们来逐行讲解,重点剖析 sortList 中的递归逻辑,以及整个流程是如何运作的。


🌟 总体结构

这是一个典型的 分治算法(Divide and Conquer)

  1. :用快慢指针找到链表中点,把链表从中间断开成两部分。
  2. :对左右两部分分别递归排序。
  3. :将两个有序链表合并成一个有序链表(调用 mergeTwoLists)。

最终返回排好序的链表头。


✅ 三个函数的作用

1. middleNode(head):找链表中点并断开前连接

def middleNode(self, head: Optional[ListNode]) -> Optional[ListNode]:slow = fast = headwhile fast and fast.next:pre = slow      # 记录 slow 的前一个节点slow = slow.nextfast = fast.next.nextpre.next = None     # 断开连接return slow         # 返回中间节点作为右半部分的头
🔍 作用:
  • 找到链表的中间节点(偏右的那个,比如偶数长度时返回第 2 个)
  • 并且 断开左半部分和右半部分的连接
📌 举个例子:

输入:4 -> 2 -> 1 -> 3

执行过程:

slow 和 fast 从 head=4 开始第1步:slow=4, pre=Noneslow=2, fast=1第2步:pre=2, slow=1, fast=None → 结束pre.next = None → 2.next = None

结果:

  • 原链表被断成:
    • 左半:4 -> 2
    • 返回 slow=1,即右半:1 -> 3

✅ 所以这个函数不仅“找中点”,还完成了“分割”任务!

⚠️ 注意:pre 是在循环中动态记录 slow 的前驱,这样才能断开。


2. mergeTwoLists(list1, list2):合并两个有序链表

标准写法,使用哨兵节点:

def mergeTwoLists(self, list1, list2) -> ListNode:cur = dummy = ListNode()while list1 and list2:if list1.val < list2.val:cur.next = list1list1 = list1.nextelse:cur.next = list2list2 = list2.nextcur = cur.nextcur.next = list1 or list2  # 接上剩余部分return dummy.next

✅ 这个函数保证:给两个有序链表,返回一个合并后的有序链表。


3. sortList(head):主函数,递归实现归并排序

def sortList(self, head: Optional[ListNode]) -> Optional[ListNode]:# 终止条件:空或只有一个节点if head is None or head.next is None:return head# 分:找到中点,并断开head2 = self.middleNode(head)# 治:递归排序左右两部分head = self.sortList(head)      # 排序左半head2 = self.sortList(head2)    # 排序右半# 合:合并两个有序链表return self.mergeTwoLists(head, head2)

🧠 重点:sortList 的递归是怎么运行的?

我们用一个例子来 trace:4 -> 2 -> 1 -> 3

🌲 第一层递归(原始调用)

head = 4 -> 2 -> 1 -> 3
  1. 调用 middleNode(head)

    • 返回 head2 = 1 -> 3
    • 原链表变成:4 -> 2(因为 pre.next = None
  2. 递归调用:

    head = sortList(4 -> 2)
    head2 = sortList(1 -> 3)
    

🔁 第二层递归:sortList(4 -> 2)

head = 4 -> 2
  1. middleNode(4 -> 2)

    • slow 会走到 2pre = 4
    • pre.next = None4.next = None
    • 返回 head2 = 2
  2. 现在:

    • head = 4(单节点)
    • head2 = 2(单节点)
  3. 递归调用:

    sortList(4) → 返回 4
    sortList(2) → 返回 2
    
  4. 合并:mergeTwoLists(4, 2) → 返回 2 -> 4

✅ 所以 sortList(4 -> 2) 返回 2 -> 4


🔁 第二层递归:sortList(1 -> 3)

类似地:

  1. middleNode(1 -> 3)

    • 返回 head2 = 3,原链表断开为 13
  2. 递归:

    • sortList(1) → 返回 1
    • sortList(3) → 返回 3
  3. 合并:mergeTwoLists(1, 3) → 返回 1 -> 3

✅ 所以 sortList(1 -> 3) 返回 1 -> 3


🔚 第一层合并

现在回到第一层:

head = 2 -> 4
head2 = 1 -> 3

调用:

mergeTwoLists(2->4, 1->3)

合并过程:

  • 比较 2 和 1 → 取 1
  • 比较 2 和 3 → 取 2
  • 比较 4 和 3 → 取 3
  • 最后接上 4

结果:1 -> 2 -> 3 -> 4

✅ 完美排序!


📌 递归树可视化

                 [4,2,1,3]/         \[4,2]           [1,3]/    \           /    \[4]    [2]       [1]    [3]\     /          \     /merge(2,4)       merge(1,3)\             /merge(2->4, 1->3)|[1->2->3->4]

🧩 关键点解析

1. 为什么 middleNode 要返回 slow 并断开 pre.next

  • slow 是右半部分的头
  • pre.next = None 是为了断开左右两部分,避免递归时无限循环
  • 否则 sortList(head) 还是整个链表,无法真正“分”

2. 递归终止条件是什么?

if head is None or head.next is None:return head
  • 空链表或单节点 → 已经有序 → 直接返回

3. 为什么可以递归调用 sortList(head)

因为 middleNode 已经把 head 所在的链表从中间断开了,现在 head 指向的是左半部分,长度减半,递归下去一定会到达终止条件。

4. 时间复杂度?

  • 每层 O(n) 合并
  • 递归深度 O(log n)
  • 总体:O(n log n)

5. 空间复杂度?

  • 递归栈深度 O(log n)
  • 链表本身不额外分配节点
  • 所以是 O(log n)(递归栈空间)

✅ 总结

步骤函数作用
middleNode找中点,断开链表
sortList 递归对左右两部分分别排序
mergeTwoLists合并两个有序链表

💡 小技巧记忆

链表归并排序 = 快慢找中 + 断开 + 递归 + 合并

就像切面条:

  1. 一刀从中间切开(middleNode
  2. 左右分别再切,直到变成一根(递归到底)
  3. 然后从小到大一根根合并回去(merge

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/95552.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/95552.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

在腾讯云CodeBuddy上实现一个AI聊天助手

在腾讯云CodeBuddy上实现一个AI聊天助手项目 在当今数字化时代&#xff0c;AI聊天助手已经成为一种非常流行的应用&#xff0c;广泛应用于客户服务、智能助手等领域。今天&#xff0c;我们将通过腾讯云CodeBuddy平台&#xff0c;实现一个基于Spring Boot和OpenAI API的AI聊天助…

JavaScript Array.prototype.flatMap ():数组 “扁平化 + 映射” 的高效组合拳

在 JavaScript 数组处理中&#xff0c;我们经常需要先对每个元素进行转换&#xff08;映射&#xff09;&#xff0c;再将结果 “铺平”&#xff08;扁平化&#xff09;。比如将数组中的每个字符串按空格拆分&#xff0c;然后合并成一个新数组。传统做法是先用map()转换&#xf…

区块链与元宇宙:数字资产的守护者

1 区块链支撑元宇宙数字资产的底层逻辑1.1 不可篡改性构建信任基石区块链的不可篡改性为元宇宙数字资产提供了坚实的信任基础。其核心在于分布式账本技术&#xff0c;当一笔数字资产交易发生时&#xff0c;会被打包成区块并广播至网络中的所有节点。每个节点都会对这笔交易进行…

Linux软件编程:进程和线程(进程)

进程一、基本概念进程&#xff1a;是程序动态执行过程&#xff0c;包括创建、调度、消亡程序&#xff1a;存放在外存的一段数据的集合二、进程创建&#xff08;一&#xff09;进程空间分布每个进程运行起来后&#xff0c;操作系统开辟0-4G的虚拟空间进程空间&#xff1a;用户空…

Mybatis学习笔记(五)

分页插件与性能优化 分页插件配置 简要描述&#xff1a;MybatisPlus分页插件是基于物理分页实现的高性能分页解决方案&#xff0c;支持多种数据库的分页语法&#xff0c;能够自动识别数据库类型并生成对应的分页SQL。 核心概念&#xff1a; 物理分页&#xff1a;直接在SQL层面进…

企业可商用的conda:「Miniforge」+「conda-forge」

文章目录一、彻底卸载现有 Anaconda/Miniconda二、安装 Miniforge&#xff08;推荐&#xff09;macOS/Linux检查Windows检查三、将通道固定为 conda-forge&#xff08;严格优先&#xff09;四、验证是否仍引用 Anaconda 源五、常见问题&#xff08;FAQ&#xff09;六、参考命令…

Flutter ExpansionPanel组件(可收缩的列表)

可以展开或者收缩的面板组件&#xff0c;收缩面板组件效果由ExpansionPanelList组件和ExpansionPanel组件共同完成。 ExpansionPanelList属性说明属性说明children子元素expansionCallback设置回调事件ExpansionPanel属性说明headerBuilder收缩的标题body内容isExpanded设置内容…

C/C++ 进阶:深入解析 GCC:从源码到可执行程序的魔法四步曲

引言距离上一篇博客更新已经过去了大概一两周的时间&#xff0c;而对于 Linux 系统的基本指令以及 Shell 编程的学习其实基本讲解完毕&#xff0c;Linux基础一块的知识就将告一段落了&#xff0c;如果有细节性的知识&#xff0c;我也会及时分享给各位&#xff0c;作为一名正在攀…

云服务器运行持续强化学习COOM框架的问题

1 环境要求 下载地址&#xff1a;https://github.com/TTomilin/COOM tensorflow 2.11以上 python 3.9以上 tensorflow2.12.0&#xff0c;需要安装tensorflow-probability0.19 2 修改代码 COOM/wrappers/reward.py 将 from gym import RewardWrapper修改为 from gymnasium impor…

MyBatis Interceptor 深度解析与应用实践

MyBatis Interceptor 深度解析与应用实践 一、MyBatis Interceptor概述 1.1 什么是MyBatis Interceptor MyBatis Interceptor&#xff0c;也称为MyBatis 插件&#xff0c;是 MyBatis 提供的一种扩展机制&#xff0c;用于在 MyBatis 执行 SQL 的过程中插入自定义逻辑。它类似…

【自动化测试】Web自动化测试 Selenium

&#x1f525;个人主页&#xff1a; 中草药 &#x1f525;专栏&#xff1a;【Java】登神长阶 史诗般的Java成神之路 测试分类 了解各种各样的测试方法分类&#xff0c;不是为了墨守成规按照既定方法区测试&#xff0c;而是已了解思维为核心&#xff0c;并了解一些专业名词 根…

2025 电赛 C 题完整通关攻略:从单目标定到 2 cm 测距精度的全流程实战

摘要 2025 年全国大学生电子设计竞赛 C 题要求“仅用一颗固定摄像头”在 5 s 内完成 100 cm~200 cm 距离、误差 ≤2 cm 的单目测距&#xff0c;并实时显示功耗。本文整合国一选手方案、CSDN 高分博文、B 站实测视频及官方说明&#xff0c;给出从硬件选型→离线标定→在线算法→…

Day 10: Mini-GPT完整手写实战 - 从组件组装到文本生成的端到端实现

Day 10-2: Mini-GPT完整手写实战 - 从组件组装到文本生成的端到端实现 📚 今日学习目标 掌握GPT架构组装:将Transformer组件组装成完整的生成模型 理解生成式预训练:掌握自回归语言建模的核心机制 端到端代码实现:从数据预处理到模型训练的完整流程 文本生成实战:训练Mi…

深入解析Prompt缓存机制:原理、优化与实践经验

深入解析Prompt缓存机制&#xff1a;原理、优化与实践经验 概述 在大型语言模型应用中&#xff0c;API请求的延迟和成本始终是开发者关注的核心问题。Prompt缓存&#xff08;Prompt Caching&#xff09;技术通过智能地复用重复内容&#xff0c;有效减少了API响应时间和运行成本…

CV 医学影像分类、分割、目标检测,之【3D肝脏分割】项目拆解

CV 医学影像分类、分割、目标检测&#xff0c;之【3D肝脏分割】项目拆解第1行&#xff1a;from posixpath import join第2行&#xff1a;from torch.utils.data import DataLoader第3行&#xff1a;import os第4行&#xff1a;import sys第5行&#xff1a;import random第6行&a…

Mybatis学习笔记(七)

Spring Boot集成 简要描述&#xff1a;MyBatis-Plus与Spring Boot的深度集成&#xff0c;提供了自动配置、启动器等特性&#xff0c;大大简化了配置和使用。 核心概念&#xff1a; 自动配置&#xff1a;基于条件的自动配置机制启动器&#xff1a;简化依赖管理的starter配置属性…

机器人伴侣的智能升级:Deepoc具身智能模型如何重塑成人伴侣体验

引言&#xff1a;机器人伴侣市场的技术变革需求随着人工智能技术的飞速发展和人们情感需求的多元化&#xff0c;机器人成人伴侣市场正在经历前所未有的增长。传统机器人伴侣已经能够满足基础的交互需求&#xff0c;但在智能化、情感化和个性化方面仍存在明显不足。这正是深算纪…

metabase基础使用技巧 (dashboard, filter)

这是metabase系列分享文章的第2部分。本文将介绍metabase的基础概念和使用介绍 question question是metabase中提供的通过UI化操作就能实现简单的 快捷 直接的BI查询。 点击右侧的New -> Question即可创建Question&#xff0c;可以理解为一个格式化的查询&#xff1a; 这里…

机器人成人伴侣的智能化升级:Deepoc具身模型赋能沉浸式体验

引言&#xff1a;成人机器人市场的技术革新需求随着人工智能和机器人技术的快速发展&#xff0c;成人陪伴机器人行业正经历从简单机械运动到智能化交互的转型。据市场研究数据显示&#xff0c;全球成人机器人市场规模预计将在2026年突破100亿美元&#xff0c;年复合增长率保持在…

Go语言企业级权限管理系统设计与实现

最近跟着学长再写河南师范大学附属中学图书馆的项目&#xff0c;学长交给了我一个任务&#xff0c;把本项目的权限管理给吃透&#xff0c;然后应用到下一个项目上。 我当然是偷着乐呐&#xff0c;因为读代码的时候&#xff0c;总是莫名给我一种公费旅游的感觉。 本来就想去了解…