【面试经典 150 | 分治】排序链表

文章目录

  • 写在前面
  • Tag
  • 题目来源
  • 解题思路
    • 方法一:链表转数组
    • 方法二:自顶向下归并排序
    • 方法三:自底向上的归并排序
  • 写在最后

写在前面

本专栏专注于分析与讲解【面试经典150】算法,两到三天更新一篇文章,欢迎催更……

专栏内容以分析题目为主,并附带一些对于本题涉及到的数据结构等内容进行回顾与总结,文章结构大致如下,部分内容会有增删:

  • Tag:介绍本题牵涉到的知识点、数据结构;
  • 题目来源:贴上题目的链接,方便大家查找题目并完成练习;
  • 题目解读:复述题目(确保自己真的理解题目意思),并强调一些题目重点信息;
  • 解题思路:介绍一些解题思路,每种解题思路包括思路讲解、实现代码以及复杂度分析;
  • 知识回忆:针对今天介绍的题目中的重点内容、数据结构进行回顾总结。

Tag

【链表】【分治】【归并排序】


题目来源

148. 排序链表


解题思路

方法一:链表转数组

一种朴素的解法是将链表中的节点存储到数组中,然后对数组按节点值进行升序排序,排好序后,将节点数组再连接成一条链表。该方法比较简单,直接给出代码。

代码

/*** 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:// 自定义排序的仿函数struct myclass {bool operator() (ListNode *n1, ListNode *n2) {return n1->val < n2->val;}} myobject;ListNode* sortList(ListNode* head) {if (head == nullptr || head->next == nullptr) {return head;}vector<ListNode*> tmp;ListNode *curr = head;while (curr != nullptr) {tmp.push_back(curr);curr = curr->next;}sort(tmp.begin(), tmp.end(), myobject);int n = tmp.size();head = tmp[0];curr = head;for (int i = 1; i < tmp.size(); ++i) {curr->next = tmp[i];curr = curr->next;}// 最后一个结点的next要置空curr->next = nullptr;return head;}
};

复杂度分析

时间复杂度: O ( n l o g n ) O(nlogn) O(nlogn) n n n 是链表中节点个数。本题的时间瓶颈在于排序的时间复杂度。

空间复杂度: O ( n ) O(n) O(n),使用一个额外的数组记录链表中的节点的空间复杂度为 O ( n ) O(n) O(n)。最坏情况下,需要排序的序列是逆序的,需要 n 次递归调用。因此需要 O ( n ) O(n) O(n) 的栈空间

方法二:自顶向下归并排序

对链表进行自顶向下的归并排序步骤如下:

  • 找到链表的中点,以中点为分界,将链表拆分成两个子链表。寻找链表的中间节点可以使用快慢指针来实现,快指针每次移动 2 步,慢指针每次移动 1 步,当快指针到达链表末尾时,慢指针指向的链表节点即为链表的中点。
  • 递归对两个子链表进行排序。
  • 将两个升序的子链表进行合并。合并两个有序链表可以参考 【面试经典150 | 链表】合并两个有序链表。

代码

/*** 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 {
private:ListNode* sortList(ListNode* head, ListNode* tail) {if (head == nullptr) {      // 递归出口return head;}if (head->next == tail) {   // 递归出口head->next = nullptr;return head;}ListNode* slow = head, *fast = head;while (fast != tail) {slow = slow->next;fast = fast->next;if (fast != tail) {     // 无论链表长度是奇偶,都返回中间节点的左边那个(奇数则直接返回中间节点)fast = fast->next;}}ListNode* mid = slow;return merge(sortList(head, mid), sortList(mid, tail));}// 合并两个有序链表ListNode* merge(ListNode* head1, ListNode* head2) {ListNode* dummy = new ListNode(0);ListNode* prev = dummy;while (head1 && head2) {if (head1->val < head2->val) {prev->next = head1;head1 = head1->next;}else {prev->next = head2;head2 = head2->next;}prev = prev->next;}prev->next = head1 ? head1 : head2;return dummy->next;}
public:ListNode* sortList(ListNode* head) {return sortList(head, nullptr);}
};

复杂度分析

时间复杂度: O ( n l o g n ) O(nlogn) O(nlogn) n n n 是链表中节点个数。

空间复杂度: O ( l o g n ) O(logn) O(logn),空间复杂度取决于递归调用的占空间。

方法三:自底向上的归并排序

归并排序除了自顶向下实现,也可自底向上实现。自底向上的归并排序空间复杂度为 O ( 1 ) O(1) O(1)

首先要求出链表的长度 len。利用迭代可以轻松求出。

接着将链表拆分成子链表进行合并,具体步骤如下:

  • 枚举需要排序的子链表长度,自底向上的排序,初始化子链表长度 subLen = 1
  • 每次将链表拆分成若干个长度为 subLen 的子链表(最后一个链表的长度可以小于 subLen),按照每两个子链表一组进行合并,合并后即可得到若干个长度为 subLen × 2 的有序子链表(最后一个子链表的长度可以小于 subLength × 2。合并两个子链表仍然使用「21. 合并两个有序链表」的做法。
  • subLen 的值加倍,重复第 2 步,对更长的有序子链表进行合并操作,直到有序子链表的长度大于或等于 len,整个链表排序完毕。

代码

/*** 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 {
private:ListNode* merge(ListNode* head1, ListNode* head2) {ListNode* dummy = new ListNode(0);ListNode* prev = dummy;while (head1 && head2) {if (head1->val < head2->val) {prev->next = head1;head1 = head1->next;}else {prev->next = head2;head2 = head2->next;}prev = prev->next;}prev->next = head1 ? head1 : head2;return dummy->next;}public:ListNode* sortList(ListNode* head) {if (head == nullptr) {return head;}int len = 0;ListNode* node = head;while (node != nullptr) {++len;node = node->next;}ListNode* dummy = new ListNode(0, head);for (int subLen = 1; subLen < len; subLen <<= 1) {ListNode* prev = dummy, *cur = dummy->next;while (cur) {ListNode* head1 = cur;      // 第一个长度为 subLen 的子节点开头for (int i = 1; i < subLen && cur->next; ++i) {// 第一个 subLen 子节点末尾的节点cur = cur->next;}ListNode* head2 = cur->next;// 第二个长度为 subLen 的子节点开头cur->next = nullptr;cur = head2;for (int i = 1; i < subLen && cur && cur->next; ++i) {// 第二个 subLen 子节点末尾的节点cur = cur->next;}ListNode* next = nullptr;   //  维护下一个长度为 subLen 的子节点开头if (cur) {next = cur->next;cur->next = nullptr;}ListNode* merged = merge(head1, head2);prev->next = merged; while (prev->next) {prev = prev->next;}cur = next;}}return dummy->next;}
};

复杂度分析

时间复杂度: O ( n l o g n ) O(nlogn) O(nlogn) n n n 是链表中节点个数。

空间复杂度: O ( 1 ) O(1) O(1)


写在最后

如果您发现文章有任何错误或者对文章有任何疑问,欢迎私信博主或者在评论区指出 💬💬💬。

如果大家有更优的时间、空间复杂度的方法,欢迎评论区交流。

最后,感谢您的阅读,如果有所收获的话可以给我点一个 👍 哦。

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

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

相关文章

利用STM32实现语音识别功能

引言 随着物联网和智能设备的普及&#xff0c;语音识别技术正逐渐成为用户交互的主流方式之一。 STM32微控制器具备处理高效率语音识别算法的能力&#xff0c;使其成为实现低成本、低功耗语音交互系统的理想选择。 本教程将介绍如何在STM32平台上开发和部署一个基础的语音识…

Java进阶-Java Stream API详解与使用

本文全面介绍了 Java Stream API 的概念、功能以及如何在 Java 中有效地使用它进行集合和数据流的处理。通过详细解释和示例&#xff0c;文章展示了 Java Stream API 在简化代码、提高效率以及支持函数式编程方面的优势。文中还比较了 Java Stream API 与其他集合处理库的异同&…

【记录】Springboot项目集成docker实现一键部署

公司管理平台完成后&#xff0c;为了方便其他不懂开发的同事部署和测试&#xff0c;集成docker进行一键部署&#xff0c;也为后面自动化部署做准备。本文做个简单记录。 1、安装docker yum install https://download.docker.com/linux/fedora/30/x86_64/stable/Packages/cont…

Ubuntu GUI使用Root用户登录指南

Ubuntu GUI使用Root用户登录指南 一、前言 默认情况下&#xff0c;Ubuntu 禁用了 root 账户&#xff0c;我们必须使用 sudo 命令来执行任何需要 root 权限的任务&#xff0c;比如像这样删除一个系统配置文件&#xff08;操作危险&#xff0c;请勿尝试&#xff09;&#xff1a;…

是机遇?是未来?拥抱 AI Agent ,拥抱 AI 2.0时代~

✍️ 作者&#xff1a;哈哥撩编程&#xff08;视频号同名&#xff09; 博客专家全国博客之星第四名超级个体COC上海社区主理人特约讲师谷歌亚马逊演讲嘉宾科技博主极星会首批签约作者 &#x1f3c6; 推荐专栏&#xff1a; &#x1f3c5; 程序员&#xff1a;职场关键角色通识宝…

Devops部署maven项目

这里讲下应用k8s集群devops持续集成部署maven项目的流程。 failed to verify certificate: x509: certificate signed by unknown authority 今天在执行kubectl get nodes的时候报的证书验证问题&#xff0c;看了一圈首次搭建k8s的都是高频出现的问题。 couldn’t get curren…

代码随想录-二叉树(节点)

目录 104. 二叉树的最大深度 题目描述&#xff1a; 输入输出描述&#xff1a; 思路和想法&#xff1a; 111. 二叉树的最小深度 题目描述&#xff1a; 输入输出描述&#xff1a; 思路和想法&#xff1a; 222. 完全二叉树的节点个数 题目描述&#xff1a; ​输入输出描…

Flutter笔记:Widgets Easier组件库(3)使用按钮组件

Flutter笔记 Widgets Easier组件库&#xff08;3&#xff09;&#xff1a;使用按钮组件 - 文章信息 - Author: 李俊才 (jcLee95) Visit me at CSDN: https://jclee95.blog.csdn.netMy WebSite&#xff1a;http://thispage.tech/Email: 291148484163.com. Shenzhen ChinaAddre…

IDEA 开发找到 java-web 发布到 tomcat 的路径

使用 IDEA 开发 java web 应用&#xff0c;有没有遇到需要找到 tomcat 路径的问题 为什么要找 tomcat 路径呢&#xff1f; 拿我的项目来举例&#xff0c;有统一的线上线下 logback.xml 配置&#xff0c;配置时业务、框架日志输出到 file&#xff0c;少量的启动日志输出到 con…

mysql从入门到起飞+面试基础题

mysql基础 MySQL基础 企业面试题1 代码 select m.id,m.num from ( select t.id as id,count(1) num from ( select ra.requester_id as id from RequestAccepted raunion all select ra.accepter_id as id from RequestAccepted ra ) t group by t.id ) m group by id ord…

maven插件:dockerfile-maven-plugin和docker-maven-plugin

Maven插件dockerfile-maven-plugin和docker-maven-plugin都是为Java开发人员提供了一种便捷的方式&#xff0c;通过Maven构建流程来自动化创建、管理和推送Docker镜像。虽然它们有着相似的目标&#xff0c;即集成Docker与Maven项目&#xff0c;但这两个插件在实现细节、功能侧重…

ElasticSearch教程入门到精通——第四部分(基于ELK技术栈elasticsearch 7.x新特性)

ElasticSearch教程入门到精通——第四部分&#xff08;基于ELK技术栈elasticsearch 7.x新特性&#xff09; 1. Elasticsearch进阶1.1 核心概念1.1.1 索引Index1.1.1.1 索引创建原则1.1.1.2 Inverted Index 1.1.2 类型Type1.1.3 文档Document1.1.4 字段Field1.1.5 映射Mapping1.…

Agent AI智能体:机器学习与自我优化的奇妙之旅

文章目录 &#x1f4d1;前言一、Agent AI智能体的基本概念二、Agent AI智能体的技术进步2.1 机器学习技术2.2 自适应技术2.3 分布式计算与云计算 三、Agent AI智能体的知识积累3.1 知识图谱3.2 迁移学习 四、Agent AI智能体的挑战与机遇4.1 挑战4.2 机遇 小结 &#x1f4d1;前言…

LeetCode 11—— 盛最多水的容器

阅读目录 1. 题目2. 解题思路一3. 代码实现一4. 解题思路二5. 代码实现二 1. 题目 2. 解题思路一 暴力法&#xff0c;遍历所有可能的垂线对 ( i , j ) (i, j) (i,j)&#xff0c;求取最大面积&#xff1a; a r e a m i n ( h [ i ] , h [ j ] ) ∗ ( j − i ) area min(h[i]…

Python3.11修改并运行oneforall

遇到的问题 使用python3.11默认无法运行oneforall脚本&#xff0c;出现如下报错 # 解决方案 修改 /usr/local/lib/python3.11/dist-packages/exrex.py exrex.py具体文件路径报错中会显示 vim /usr/local/lib/python3.11/dist-packages/exrex.py# 修改前 from re import sre…

ctfshow——JWT

文章目录 web 345web 346——算法改为Noneweb 347-348——爆破密匙web 349——非对称加密算法RS256私钥泄漏web 350——泄漏公钥、非对称密码算法改为对称密码算法 web 345 抓个包&#xff0c;可以看到cookie部分使用JWT&#xff08;Json Web Token&#xff09;。 JWT实际上是…

自定义@Conditional注解案例,实现动态判断条件,创建Bean

案例2&#xff1a; 在Spring的IOC容器中有一个User的Bean&#xff0c;现要求&#xff1a;某个字节码存在就创建该Bean。不存在不创建。 1、引入依赖 <dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId> </depen…

此平台不支持虚拟化的 AMD-V/RVI。 不使用虚拟化的 AMD-V/RVI,是否继续?

此平台不支持虚拟化的 AMD-V/RVI。 不使用虚拟化的 AMD-V/RVI&#xff0c;是否继续? 关闭内核隔离&#xff0c;重启。

【二叉树——数据结构】

文章目录 1.二叉树1.基本概念.几种特殊的二叉树 2.考点3.二叉树的存储结构4.二叉树的遍历5.线索二叉树 1.二叉树 1.基本概念. 二叉树是n(n>0)个结点的有限集合 或者为空二叉树&#xff0c;即n0 或者由一个根结点和两个互不相交的被称作根的左子树和右子树组成。 每个结点至…

向gitee推送项目

步骤很详细&#xff0c;直接上教程 在gitee新建项目并复制链接 在当前项目目录打开git bash 输入以下指令进行初始化 git init配置个人信息 git config --global user.name 你的昵称 git config --global user.email 账号绑定的邮箱 5. 绑定远程仓库地址 git remote add ori…