leetcode148_排序链表的3种解法
- 1. 题目
- 2. 解答
- 2.1. 解法1
- 2.2. 解法2
- 2.3. 解法3
1. 题目
给你链表的头结点head
,请将其按升序排列并返回排序后的链表。
/*** Definition for singly-linked list.* struct ListNode {* int val;* ListNode *next;* ListNode() : val(0), next(nullptr) {}* ListNode(int x) : val(x), next(nullptr) {}* ListNode(int x, ListNode *next) : val(x), next(next) {}* };*/
class Solution {
public:ListNode* sortList(ListNode* head) {}
};
样例:
输入:head = [4,2,1,3]
输出:[1,2,3,4]
输入:head = [-1,5,3,4,0]
输出:[-1,0,3,4,5]
输入:head = []
输出:[]
提示:
链表中节点的数目在范围 [0, 5 * 104] 内
-105 <= Node.val <= 105
进阶:
你可以在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序吗?
2. 解答
2.1. 解法1
把链表中每个节点的val
保存到一个额外的数组里,对数组进行排序,最后把数组里的值依次复制到链表的各个节点。
class Solution {
public:ListNode* sortList(ListNode* head) {vector<int> vi;ListNode *curNode = head;while (curNode) {vi.push_back(curNode->val);curNode = curNode->next;}sort(vi.begin(), vi.end());auto it = vi.begin();curNode = head;while (curNode) {curNode->val = *it;curNode = curNode->next;++it;}return head;}
};
时间复杂度:O(nlogn)
。性能瓶颈是sort
函数的时间复杂度。
空间复杂度:O(n)
,额外分配的数组跟链表是一个数量级的。
2.2. 解法2
自顶向下归并排序。
- 通过快慢指针把链表分割成两部分。
- 递归分别排序两个子链表。
- 使用归并排序的思路合并两个子链表。
class Solution {
public:ListNode* sortList(ListNode* head) {if (!head || !head->next) {return head;}ListNode *firstBegin = head;ListNode *firstEnd = head;ListNode *secondEnd = head->next;while (firstEnd && secondEnd) {if (secondEnd->next) {secondEnd = secondEnd->next->next;} else {break;}firstEnd = firstEnd->next;}ListNode *secondBegin = firstEnd->next;firstEnd->next = nullptr;ListNode *firstList = sortList(firstBegin);ListNode *secondList = sortList(secondBegin);return MergeList(firstList, secondList);}private:ListNode *MergeList(ListNode *firstList, ListNode *secondList){ListNode *fList = firstList;ListNode *sList = secondList;ListNode *list = new ListNode();ListNode *cur = list;while (fList && sList) {if (fList->val <= sList->val) {cur->next = fList;fList = fList->next;} else {cur->next = sList;sList = sList->next;}cur = cur->next;}cur->next = fList ? fList : sList;cur = list->next;delete list;return cur;}
};
时间复杂度:O(nlogn)
。
空间复杂度:O(logn)
,虽然是对链表做原地改动,但递归调用的栈空间达到了O(logn)
的空间复杂度。
虽然递归函数是自顶向下调用的,但它是自底向上返回的,因此需要栈空间保存一部分数据。
2.3. 解法3
自底向上归并排序。使用迭代代替递归,自底向上逐级排序。
- 将整个链表分成数个长度为
1
的子链表,每个子链表都是有序的。 - 将链表中相邻的每两个长度为
1
的子链表合并成一个长度为2
的有序的子链表。 - 将链表中相邻的每两个长度为
2
的子链表合并成一个长度为4
的有序的子链表。 - 以此类推,直到子链表的长度等于初始链表的长度。
class Solution {
public:ListNode* sortList(ListNode* head) {ListNode *curNode = head;int length = 0;while (curNode) {curNode = curNode->next;++length;}ListNode *mergedList = new ListNode(0, head);for (int subLength = 1; subLength <= length; subLength <<= 1) {curNode = mergedList->next;ListNode *mergedCur = mergedList;while (curNode) {ListNode *head1 = curNode;ListNode *tail1 = GetList(head1, subLength);if (!tail1) {// head1一定是有序的mergedCur->next = head1;break;}ListNode *head2 = tail1->next;tail1->next = nullptr;ListNode *tail2 = GetList(head2, subLength);if (tail2) {curNode = tail2->next;tail2->next = nullptr;}mergedCur->next = MergeList(head1, head2);while (mergedCur->next) {mergedCur = mergedCur->next;}if (!tail2) {break;}}}curNode = mergedList->next;delete mergedList;return curNode;}private:// 以head为第一个元素,获取长度为length的链表,返回最后一个元素的地址。// 如果返回nullptr说明获取的链表长度不足lengthListNode *GetList(ListNode *head, int length){ListNode *cur = head;for (int i = 1; i < length && cur; ++i) {cur = cur->next;}return cur;}ListNode *MergeList(ListNode *firstList, ListNode *secondList){ListNode *fList = firstList;ListNode *sList = secondList;ListNode *list = new ListNode();ListNode *cur = list;while (fList && sList) {if (fList->val <= sList->val) {cur->next = fList;fList = fList->next;} else {cur->next = sList;sList = sList->next;}cur = cur->next;}cur->next = fList ? fList : sList;cur = list->next;delete list;return cur;}
};
时间复杂度:O(nlogn)
,sortList
里for
循环的时间复杂度是O(logn)
,它内嵌的while
循环复杂度是O(n)
,相乘为O(nlogn)
。
空间复杂度:O(1)
。只额外分配了常数级的变量,无递归开销。
相关文章:
leetcode148_排序链表的3种解法
1. 题目2. 解答 2.1. 解法12.2. 解法22.3. 解法3 1. 题目 给你链表的头结点head,请将其按升序排列并返回排序后的链表。 /*** Definition for singly-linked list.* struct ListNode {* int val;* ListNode *next;* ListNode() : val(0), next(nullp…...

使用stm32实现电机的PID控制
使用stm32实现电机的PID控制 PID控制应该算是非常古老而且应用非常广泛的控制算法了,小到热水壶温度控制,大到控制无人机的飞行姿态和飞行速度等等。在电机控制中,PID算法用的尤为常见。 文章目录使用stm32实现电机的PID控制一、位置式PID1.计…...

数学原理—嵌入矩阵
目录 1.嵌入矩阵的基本作用 2.嵌入矩阵的数学解释 3.嵌入矩阵在联合分布适应中的数学推导主要包括以下几个步骤 4.在JDA中,怎么得到嵌入矩阵 5.联合分布自适应中如何得到嵌入矩阵 (另一种解释) 1.嵌入矩阵的基本作用 在机器学习中&a…...
English Learning - L2 语音作业打卡 辅音翘舌音 [ʃ] [ʒ] 空气摩擦音 [h] Day31 2023.3.23 周四
English Learning - L2 语音作业打卡 辅音翘舌音 [ʃ] [ʒ] 空气摩擦音 [h] Day31 2023.3.23 周四💌发音小贴士:💌当日目标音发音规则/技巧:翘舌音 [ʃ] [ʒ]空气摩擦音 [h]🍭 Part 1【热身练习】🍭 Part2【练习内容】…...

记录springboot+vue+fastdfs实现简易的文件(上传、下载、删除、预览)操作
前言说明:springboot vue FastDFS实现文件上传(支持预览)升级版 FASTDFS部分 FASTDFS安装过程:基于centos 7安装FastDFS文件服务器 SpringBoot部分 springboot源码实现 package com.core.doc.controller;import com.baomid…...

Java中循环使用Stream应用场景
在JAVA中,涉及到对数组、Collection等集合类中的元素进行操作的时候,通常会通过循环的方式进行逐个处理,或者使用Stream的方式进行处理。例如,现在有这么一个需求:从给定句子中返回单词长度大于5的单词列表,…...

中国蚁剑AntSword实战
中国蚁剑AntSword实战1.基本使用方法2.绕过安全狗连接3.请求包修改UA特征伪造RSA流量加密4.插件使用1.基本使用方法 打开蚂蚁宝剑,右键添加数据: 输入已经上传马的路径和连接密码: 测试连接,连接成功! GetShell了&…...
C++ 直接初始化和拷贝初始化
首先我们介绍直接初始化:编译器使用普通的函数匹配来选择与我们提供的参数最匹配的构造函数。文字描述可能会让你们云里雾里,那我们直接看代码: //先设计这样的一个类 class A{ public:A(){ cout << "A()" << endl; }A…...

数据迁移工具
1.Kettle Kettle是一款国外开源的ETL工具,纯Java编写,绿色无需安装,数据抽取高效稳定 (数据迁移工具)。 Kettle 中有两种脚本文件,transformation 和 job,transformation 完成针对数据的基础转换,job 则完成整个工作流的控制。 Kettle 中文名称叫水壶,该项目的主程序…...

【C/C++】程序的内存开辟
在C/C语言中,不同的类型开辟的空间区域都是不一样的. 这节我们就简单了解下开辟不同的类型内存所存放的区域在哪里. 文章目录栈区(stack)堆区(heap)数据段(静态区)常量存储区内存开辟布局图栈区…...

全网最完整,接口测试总结彻底打通接口自动化大门,看这篇就够了......
目录:导读前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结(尾部小惊喜)前言 所谓接口࿰…...

28-flume和kafka为什么要结合使用
一:flume和kafka为什么要结合使用 首先:Flume 和 Kafka 都是用于处理大量数据的工具,但它们的设计目的不同。Flume 是一个可靠地收集、聚合和移动大量日志和事件数据的工具,而Kafka则是一个高吞吐量的分布式消息队列,…...

STM32外设-定时器详解
0. 概述 本文针对STM32F1系列,主要讲解了其中的8个定时器的原理和功能 1. 定时器分类 STM32F1 系列中,除了互联型的产品,共有 8 个定时器,分为基本定时器,通用定时器和高级定时器基本定时器 TIM6 和 TIM7 是一个 16 位…...

史上最详细的改良顺序表讲解,看完不会你打我
目录 0.什么是顺序表 1.顺序表里结构体的定义 2.顺序表的初始化 3.顺序表的输入 4.增加顺序表的长度 5.1顺序表的元素查找(按位查找) 5.2顺序表的元素查找(按值查找)在顺序表进行按值查找,大概只能通过遍历的方…...

【Unity入门】资源包导入和导出
【Unity入门】资源包导入和导出 大家好,我是Lampard~~ 欢迎来到Unity入门系列博客,所学知识来自B站阿发老师~感谢 (1)资源目录 Unity的资源(模型,场景,脚本)等都保存在Assert目录下&…...

python条件语句与循环语句
目录 一、条件语句 1.1if 二、循环语句 2.1while 2.2for循环 2.3break和continue 三、test和总结 一、条件语句 1.1if Python条件语句是通过一条或多条语句的执行结果(True或者False)来决定执行的代码块。 Python程序语言指定: 任…...

【leetcode】链表(2)
目录 1. 环形链表 解题思路 2. 环形链表 II 解题思路 3. 删除排序链表中的重复元素 解题思路 4. 删除排序链表中的重复元素 II 解题思路 5. 移除链表元素 解题思路 6. 链表的中间结点 解题思路 1. 环形链表 OJ:环形链表 给你一个链表的头节点 head &am…...

使用Vue+vue-router+路由守卫实现路由鉴权功能实战
目录 一、本节介绍和上节回顾 1. 上节介绍 2. Vue SpringBoot前后端分离项目实战的目录 3. 本小节介绍 二、Vue-router改造以及路由鉴权 1. 路由数据的拆分 2. 路由守卫 三、404错误页的实现 1. 创建全局css样式 2. 全局样式引入 3. 404页面的开发 4. el-button的…...

多线程(三):Thread 类的基本属性
上一个篇章浅浅了解了一下 线程的概念,进程与线程的区别,如何实现多线程编程。 而且上一章提到一个重要的面试点: start 方法和 run 方法的区别。 start 方法是从系统那里创建一个新的线程,这个线程会自动调用内部的run 方法&…...

蓝桥杯嵌入式第六课--串口收发
前言串口作为一个考试中考察频率较高的考点,其套路比较固定,因此值得我们仔细把握。本节课主要着眼于快速配置实现 串口收发与串口的中断。CubeMX配置选择串口2配置异步收发模式基本参数设置(波特率、校验位等等)开启串口收发中断…...

如何将联系人从 iPhone 转移到 Android
从 iPhone 换到 Android 手机时,你可能需要保留重要的数据,例如通讯录。好在,将通讯录从 iPhone 转移到 Android 手机非常简单,你可以从本文中学习 6 种可靠的方法,确保随时保持连接,不错过任何信息。 第 1…...
css的定位(position)详解:相对定位 绝对定位 固定定位
在 CSS 中,元素的定位通过 position 属性控制,共有 5 种定位模式:static(静态定位)、relative(相对定位)、absolute(绝对定位)、fixed(固定定位)和…...
Device Mapper 机制
Device Mapper 机制详解 Device Mapper(简称 DM)是 Linux 内核中的一套通用块设备映射框架,为 LVM、加密磁盘、RAID 等提供底层支持。本文将详细介绍 Device Mapper 的原理、实现、内核配置、常用工具、操作测试流程,并配以详细的…...
大语言模型(LLM)中的KV缓存压缩与动态稀疏注意力机制设计
随着大语言模型(LLM)参数规模的增长,推理阶段的内存占用和计算复杂度成为核心挑战。传统注意力机制的计算复杂度随序列长度呈二次方增长,而KV缓存的内存消耗可能高达数十GB(例如Llama2-7B处理100K token时需50GB内存&a…...
JAVA后端开发——多租户
数据隔离是多租户系统中的核心概念,确保一个租户(在这个系统中可能是一个公司或一个独立的客户)的数据对其他租户是不可见的。在 RuoYi 框架(您当前项目所使用的基础框架)中,这通常是通过在数据表中增加一个…...

基于Springboot+Vue的办公管理系统
角色: 管理员、员工 技术: 后端: SpringBoot, Vue2, MySQL, Mybatis-Plus 前端: Vue2, Element-UI, Axios, Echarts, Vue-Router 核心功能: 该办公管理系统是一个综合性的企业内部管理平台,旨在提升企业运营效率和员工管理水…...
LRU 缓存机制详解与实现(Java版) + 力扣解决
📌 LRU 缓存机制详解与实现(Java版) 一、📖 问题背景 在日常开发中,我们经常会使用 缓存(Cache) 来提升性能。但由于内存有限,缓存不可能无限增长,于是需要策略决定&am…...

R 语言科研绘图第 55 期 --- 网络图-聚类
在发表科研论文的过程中,科研绘图是必不可少的,一张好看的图形会是文章很大的加分项。 为了便于使用,本系列文章介绍的所有绘图都已收录到了 sciRplot 项目中,获取方式: R 语言科研绘图模板 --- sciRplothttps://mp.…...
LOOI机器人的技术实现解析:从手势识别到边缘检测
LOOI机器人作为一款创新的AI硬件产品,通过将智能手机转变为具有情感交互能力的桌面机器人,展示了前沿AI技术与传统硬件设计的完美结合。作为AI与玩具领域的专家,我将全面解析LOOI的技术实现架构,特别是其手势识别、物体识别和环境…...

快速排序算法改进:随机快排-荷兰国旗划分详解
随机快速排序-荷兰国旗划分算法详解 一、基础知识回顾1.1 快速排序简介1.2 荷兰国旗问题 二、随机快排 - 荷兰国旗划分原理2.1 随机化枢轴选择2.2 荷兰国旗划分过程2.3 结合随机快排与荷兰国旗划分 三、代码实现3.1 Python实现3.2 Java实现3.3 C实现 四、性能分析4.1 时间复杂度…...