python-leetcode 33.排序链表
题目:
给定链表的头结点head,请将其按升序排列,并返回排序后的链表

方法一:自顶向下归并排序
链表自顶向下归并排序的过程:
1.找到链表的中点,以中点为分界,将链表拆分成两个子链表。寻找链表的中点可以使用快慢指针的做法,快指针每次移动 2 步,慢指针每次移动 1 步,当快指针到达链表末尾时,慢指针指向的链表节点即为链表的中点
2.对两个链表进行排序
3.将两个排序后的子链表合并,得到完整的排序后的链表。可以使用[合并两个有序链表]的做法,将两个有序的子链表进行合并
可以通过递归实现。递归的终止条件是链表的节点个数小于或等于 1,即当链表为空或者链表只包含 1 个节点时,不需要对链表进行拆分和排序

# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution(object):def sortList(self, head):""":type head: Optional[ListNode]:rtype: Optional[ListNode]"""def sortFunc(head,tail):#个递归的排序函数,tail 是递归调用时用于控制链表的尾部的边界if not head: #链表已经空return headif head.next==tail: #当前链表只有一个元素,或者已经到达分割链表的边界head.next=Nonereturn headslow=fast=headwhile fast !=tail:slow=slow.nextfast=fast.nextif fast != tail: #fast 每次走两步,slow 每次走一步fast=fast.nextmid=slow #当 fast 到达链表尾部时,slow 就在链表的中间位置return merge(sortFunc(head,mid),sortFunc(mid,tail))
#递归调用sortFunc对head到mid的部分和mid到tail的部分分别进行排序,然后将两个排序好的部分合并。合并的过程交给 merge 函数def merge(head1,head2): #将两个已排序的链表合并成一个有序的链表dummyHead=ListNode(0) #虚拟的头节点temp,temp1,temp2=dummyHead,head1,head2
#临时指针,用来构建最终合并的链表,两个已排序链表的头节点while temp1 and temp2: #同时遍历temp1和temp2,比较它们的值,并将较小的节点连接到 temp 上,直到其中一个链表遍历完if temp1.val<temp2.val:temp.next=temp1 #将 temp1 加入合并后的链表temp1=temp1.next #将 temp1 移动到下一个节点else:temp.next=temp2temp2=temp2.nexttemp=temp.next #将已合并链表的指针后移if temp1: #如果 temp1 链表剩余节点未合并完,则直接将剩余的节点连接temp.next=temp1elif temp2: #如果 temp2 链表剩余节点未合并完,同样将剩余的节点连接到temp.next=temp2return dummyHead.next #去除虚拟结点,返回合并后的链表的头节点return sortFunc(head,None) #sortList 函数通过调用 sortFunc 来对整个链表进行排序,None 作为 tail 的参数表示没有上限
时间复杂度:O(nlogn)
空间复杂度:O(nlogn)
方法二:自底向上归并排序
首先求得链表的长度 length,然后将链表拆分成子链表进行合并。
具体做法:
-
用 subLength 表示每次需要排序的子链表的长度,初始时 subLength=1。
-
每次将链表拆分成若干个长度为 subLength 的子链表(最后一个子链表的长度可以小于 subLength)按照每两个子链表一组进行合并,合并后即可得到若干个长度为subLength×2 的有序子链表(最后一个子链表的长度可以小于 subLength×2)。合并两个子链表仍然使用[合并两个有序链表]的做法。
-
将 subLength 的值加倍,重复第 2 步,对更长的有序子链表进行合并操作,直到有序子链表的长度大于或等于 length,整个链表排序完毕。
举例:
首先,归并长度为 1 的子链表。例如 [4,2,1,3],把第一个节点和第二个节点归并,第三个节点和第四个节点归并,得到 [2,4,1,3]。
然后,归并长度为 2 的子链表。例如 [2,4,1,3],把前两个节点和后两个节点归并,得到 [1,2,3,4]。
然后,归并长度为 4 的子链表。
依此类推,直到归并的长度大于等于链表长度为止,此时链表已经是有序的了。
初始链表: 4 -> 2 -> 1 -> 3
step = 1:
- 分割: [4], [2], [1], [3]
- 合并: [2 -> 4], [1 -> 3]
- 结果: 2 -> 4 -> 1 -> 3
step = 2:
- 分割: [2 -> 4], [1 -> 3]
- 合并: [1 -> 2 -> 3 -> 4]
- 结果: 1 -> 2 -> 3 -> 4
step = 4:
- 分割: [1 -> 2 -> 3 -> 4]
- 无需合并
- 结果: 1 -> 2 -> 3 -> 4
最终结果: 1 -> 2 -> 3 -> 4
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = nextclass Solution(object):# 获取链表长度def getListLength(self, head):length = 0while head:length += 1head = head.nextreturn length# 分割链表# 如果链表长度 <= size,不做任何操作,返回空节点# 如果链表长度 > size,把链表的前 size 个节点分割出来(断开连接),并返回剩余链表的头节点def splitList(self, head, size):# 先找到 next_head 的前一个节点cur = headfor _ in range(size - 1):if cur is None:breakcur = cur.next# 如果链表长度 <= sizeif cur is None or cur.next is None:return None # 不做任何操作,返回空节点next_head = cur.nextcur.next = None # 断开 next_head 的前一个节点和 next_head 的连接return next_head# 合并两个有序链表(双指针)# 返回合并后的链表的头节点和尾节点def mergeTwoLists(self, list1, list2):cur = dummy = ListNode() # 用哨兵节点简化代码逻辑while list1 and list2:if list1.val < list2.val:cur.next = list1 # 把 list1 加到新链表中list1 = list1.nextelse: # 注:相等的情况加哪个节点都是可以的cur.next = list2 # 把 list2 加到新链表中list2 = list2.nextcur = cur.nextif list1:cur.next=list1elif list2:cur.next=list2while cur.next:cur=cur.nextreturn dummy.next, curdef sortList(self, head):length = self.getListLength(head) # 获取链表长度dummy = ListNode(next=head) # 用哨兵节点简化代码逻辑step = 1 # 步长(参与合并的链表长度)while step < length:new_list_tail = dummy # 新链表的末尾cur = dummy.next # 每轮循环的起始节点while cur:# 从 cur 开始,分割出两段长为 step 的链表,头节点分别为 head1 和 head2head1 = curhead2 = self.splitList(head1, step)cur = self.splitList(head2, step) # 下一轮循环的起始节点# 合并两段长为 step 的链表head, tail = self.mergeTwoLists(head1, head2)# 合并后的头节点 head,插到 new_list_tail 的后面new_list_tail.next = headnew_list_tail = tail # tail 现在是新链表的末尾step *= 2return dummy.next
时间复杂度:O(logn)
空间复杂度:O(1)
方法一的归并是自顶向下计算,需要 O(logn) 的递归栈开销。
方法二将其改成自底向上计算,空间复杂度优化成 O(1)
源自力扣官方题解,灵茶山艾府
相关文章:
python-leetcode 33.排序链表
题目: 给定链表的头结点head,请将其按升序排列,并返回排序后的链表 方法一:自顶向下归并排序 链表自顶向下归并排序的过程: 1.找到链表的中点,以中点为分界,将链表拆分成两个子链表。寻找链表的中点可以…...
【数据结构初阶第十二节】设计循环队列
云边有个稻草人-CSDN博客 必须有为成功付出代价的决心,然后想办法付出这个代价。 还有最后一道关于队列的习题,这题有点难,准备好迎接挑战吧! 目录 1.【题目】 2.实现循环队列推荐用数组,Why? 3.Q1:如…...
基于微信小程序的民宿短租系统设计与实现(ssm论文源码调试讲解)
第4章 系统设计 4.1系统设计的目标 系统设计的目标是满足用户的需求和满足系统实现所需要的所有要求。本系统收集了信息浏览、信息删除、信息添加、信息修改、信息查询为一体[17]。改变了用户民宿短租的方式,提高管理员管理效率以及用户预订的效率。为用户、房主提…...
使用 Jetty 构建 HTTPS 服务入门指南
在互联网安全越来越重要的今天,使用 HTTPS 为 Web 服务提供安全传输成为标准配置。Jetty 是一个高性能、易用且功能丰富的开源 Java HTTP 服务器和 Servlet 容器,能够轻松实现 HTTPS 支持。本文将结合代码实例,引导您快速搭建一个基于 Jetty 的 HTTPS 服务。 一、Jetty 简介…...
数据结构《图》
数据结构《图论》 图的性质 一、无向图(Undirected Graph) 定义 由一组顶点(Vertex)和一组无向边(Edge)构成。 每条无向边用一条无方向的线段连接两个顶点,记为 ( (u, v) ),其中…...
用Chrome Recorder轻松完成自动化测试脚本录制
前言 入门自动化测试,录制回放通常是小白测试首先用到的功能。而录制回放工具也一直是各大Web自动化测试必然会着重提供的一块功能。 早期WinRunner、QTP这样的工具,自动化测试可以说是围绕录制回放开展的。近年像Selenium也提供有录制工具 Selenium IDE,Playwright也包含…...
⭐️苹果电脑安装windows10双系统【详细图文步骤保姆级教程】【本教材适用于MAC台式机、笔记本MacBook air和pro】
苹果电脑安装windows10双系统【详细图文步骤保姆级教程】【本教材适用于MAC台式机、笔记本MacBook air和pro】 苹果电脑安装windows10双系统一、准备工作准备项1:U盘作为系统安装盘准备项2:您需要安装的系统镜像 二、启动转换助理步骤1:找到启…...
win10系统上的虚拟机安装麒麟V10系统提示找不到操作系统
目录预览 一、问题描述二、原因分析三、解决方案四、参考链接 一、问题描述 win10系统上的虚拟机安装麒麟V10系统提示找不到操作系统,报错:Operating System not found 二、原因分析 国产系统,需要注意的点: 需要看你的系统类…...
Java 大视界 -- 开源社区对 Java 大数据发展的推动与贡献(91)
💖亲爱的朋友们,热烈欢迎来到 青云交的博客!能与诸位在此相逢,我倍感荣幸。在这飞速更迭的时代,我们都渴望一方心灵净土,而 我的博客 正是这样温暖的所在。这里为你呈上趣味与实用兼具的知识,也…...
深入浅出C语言内存模型——高阶篇
在C语言编程的征途上,内存管理无疑是最具挑战性的部分之一。今天,我们将深入探讨C语言的内存模型,剖析其高级特性,并通过一系列案例,助你成为内存管理的佼佼者。本文为高阶篇,适合已经有一定C语言基础的读者…...
AI 百炼成神:逻辑回归, 垃圾邮件分类
第二个项目:逻辑回归垃圾邮件分类 项目代码下载地址:https://download.csdn.net/download/m0_56366541/90398247 项目目标 学习逻辑回归的基本概念。使用逻辑回归算法来实现垃圾邮件的分类。理解如何处理文本数据以及如何评估分类模型的性能。项目步骤 准备数据集 我们将使…...
MybatisPlus-扩展功能
逻辑删除乐观锁 MyBatisPlus从入门到精通-3(含mp代码生成器) Db静态工具类 Spring依赖循环问题 代码生成器 MybatisPlus代码生成器 枚举处理器 我们这里用int来存储状态 需要注解,很不灵活 希望用枚举类来代替这个Integer 这样的话我…...
《计算机视觉》——角点检测和特征提取sift
角点检测 角点的定义: 从直观上理解,角点是图像中两条或多条边缘的交点,在图像中表现为局部区域内的灰度变化较为剧烈的点。在数学和计算机视觉中,角点可以被定义为在两个或多个方向上具有显著变化的点。比如在一幅建筑物的图像…...
DeepSeek模型快速部署教程-搭建自己的DeepSeek
前言:在人工智能技术飞速发展的今天,深度学习模型已成为推动各行各业智能化转型的核心驱动力。DeepSeek 作为一款领先的 AI 模型,凭借其高效的性能和灵活的部署方式,受到了广泛关注。无论是自然语言处理、图像识别,还是…...
Swift CChar元祖转String
iOS有些API是调用C函数,Swift端获得的数据是CChar元祖,需要转成String方便使用,下面的代码以获取手机型号为例 方式一 var systemInfo utsname() uname(&systemInfo) let deviceModel withUnsafePointer(to: systemInfo.machine) { …...
【刷题】leetcode
题目 现有 s e r v e r N u m 台服务器,编号依次为 1 − s e r v e r...
WPF创建自定义类和控件及打包成dll引用
WPF创建自定义类和控件及打包成dll引用 一、前言二、创建自定义类和控件并生成dll文件2.1创建类库项目2.2创建自定义类和控件2.3生成dll文件 三、在其他项目中引用3.1添加dll文件引用3.2cs文件中引用命名空间3.3XAML文件中引用命名空间 一、前言 出于一些代码复用的需求&#…...
Zookeeper(54)如何使用Zookeeper的命令行工具?
使用 Zookeeper 的命令行工具可以方便地进行各种操作,如管理节点、查看状态、设置配置信息等。以下是详细的步骤和代码示例,涵盖如何使用 Zookeeper 的命令行工具。 1. 安装和配置 Zookeeper 首先确保已经安装并配置好 Zookeeper。可以在 Zookeeper 的…...
一周学会Flask3 Python Web开发-http响应状态码
锋哥原创的Flask3 Python Web开发 Flask3视频教程: 2025版 Flask3 Python web开发 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili 在Flask程序中,客户端发出的请求触发相应的视图函数,获取返回值会作为响应的主体,最后生成…...
【数据挖掘】
数据挖掘 目录:1. 数据转换2. 属性选择3. 独立于方案的选择4. 探索空间5. 具体方案的选择6. 离散化数值属性无监督离散化基于熵的离散化其他离散化方法 k-means算法原理算法步骤优缺点优点缺点 代码示例(使用Python和scikit-learn库)代码解释…...
uniapp 对接腾讯云IM群组成员管理(增删改查)
UniApp 实战:腾讯云IM群组成员管理(增删改查) 一、前言 在社交类App开发中,群组成员管理是核心功能之一。本文将基于UniApp框架,结合腾讯云IM SDK,详细讲解如何实现群组成员的增删改查全流程。 权限校验…...
web vue 项目 Docker化部署
Web 项目 Docker 化部署详细教程 目录 Web 项目 Docker 化部署概述Dockerfile 详解 构建阶段生产阶段 构建和运行 Docker 镜像 1. Web 项目 Docker 化部署概述 Docker 化部署的主要步骤分为以下几个阶段: 构建阶段(Build Stage):…...
【网络】每天掌握一个Linux命令 - iftop
在Linux系统中,iftop是网络管理的得力助手,能实时监控网络流量、连接情况等,帮助排查网络异常。接下来从多方面详细介绍它。 目录 【网络】每天掌握一个Linux命令 - iftop工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景…...
ESP32读取DHT11温湿度数据
芯片:ESP32 环境:Arduino 一、安装DHT11传感器库 红框的库,别安装错了 二、代码 注意,DATA口要连接在D15上 #include "DHT.h" // 包含DHT库#define DHTPIN 15 // 定义DHT11数据引脚连接到ESP32的GPIO15 #define D…...
在 Nginx Stream 层“改写”MQTT ngx_stream_mqtt_filter_module
1、为什么要修改 CONNECT 报文? 多租户隔离:自动为接入设备追加租户前缀,后端按 ClientID 拆分队列。零代码鉴权:将入站用户名替换为 OAuth Access-Token,后端 Broker 统一校验。灰度发布:根据 IP/地理位写…...
linux arm系统烧录
1、打开瑞芯微程序 2、按住linux arm 的 recover按键 插入电源 3、当瑞芯微检测到有设备 4、松开recover按键 5、选择升级固件 6、点击固件选择本地刷机的linux arm 镜像 7、点击升级 (忘了有没有这步了 估计有) 刷机程序 和 镜像 就不提供了。要刷的时…...
JDK 17 新特性
#JDK 17 新特性 /**************** 文本块 *****************/ python/scala中早就支持,不稀奇 String json “”" { “name”: “Java”, “version”: 17 } “”"; /**************** Switch 语句 -> 表达式 *****************/ 挺好的ÿ…...
实现弹窗随键盘上移居中
实现弹窗随键盘上移的核心思路 在Android中,可以通过监听键盘的显示和隐藏事件,动态调整弹窗的位置。关键点在于获取键盘高度,并计算剩余屏幕空间以重新定位弹窗。 // 在Activity或Fragment中设置键盘监听 val rootView findViewById<V…...
GruntJS-前端自动化任务运行器从入门到实战
Grunt 完全指南:从入门到实战 一、Grunt 是什么? Grunt是一个基于 Node.js 的前端自动化任务运行器,主要用于自动化执行项目开发中重复性高的任务,例如文件压缩、代码编译、语法检查、单元测试、文件合并等。通过配置简洁的任务…...
虚拟电厂发展三大趋势:市场化、技术主导、车网互联
市场化:从政策驱动到多元盈利 政策全面赋能 2025年4月,国家发改委、能源局发布《关于加快推进虚拟电厂发展的指导意见》,首次明确虚拟电厂为“独立市场主体”,提出硬性目标:2027年全国调节能力≥2000万千瓦࿰…...
