当前位置: 首页 > news >正文

【力扣】hot100系列(三)链表(二)(多解法+时间复杂度分析)

本专栏文章持续更新,新增内容使用蓝色表示。

21. 合并两个有序链表 - 力扣(LeetCode)

将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 

示例 1:

输入:l1 = [1,2,4], l2 = [1,3,4]
输出:[1,1,2,3,4,4]
示例 2:

输入:l1 = [], l2 = []
输出:[]

数据结构课程必学内容。

思路是同时遍历两个链表,每次比较当前节点的值,将较小的节点接入结果链表,并移动相应指针,当某个链表遍历完成后,直接将另一个链表的剩余部分接上即可。

解法一

/*** 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* mergeTwoLists(ListNode* list1, ListNode* list2) {ListNode* dummy = new ListNode(0);ListNode* current = dummy;while (list1 != nullptr && list2 != nullptr) {if (list1->val <= list2->val) {current->next = list1;list1 = list1->next;} else {current->next = list2;list2 = list2->next;}current = current->next;}if (list1 != nullptr) {current->next = list1;}if (list2 != nullptr) {current->next = list2;}ListNode* res = dummy->next;delete dummy;return res;}
};

时间复杂度:O(n+m),设链表1的长度为 n,链表2的长度为 m。

空间复杂度:O(1)。

解法二

感兴趣的朋友可以尝试使用递归解答。

【后续如果感兴趣了,会在这里添加】

看的出来我不太感兴趣。

23. 合并 K 个升序链表 - 力扣(LeetCode)

给你一个链表数组,每个链表都已经按升序排列。

请你将所有链表合并到一个升序链表中,返回合并后的链表。

示例 1:

输入:lists = [[1,4,5],[1,3,4],[2,6]]
输出:[1,1,2,3,4,4,5,6]
解释:链表数组如下:
[
  1->4->5,
  1->3->4,
  2->6
]
将它们合并到一个有序链表中得到。
1->1->2->3->4->4->5->6
示例 2:

输入:lists = []
输出:[]

思考过程:

同时合并K个处理起来比较麻烦,但是算法与数据结构课程中有学习过合并两个升序链表。

合并两个链表的思路是同时遍历两个链表,每次比较当前节点的值,将较小的节点接入结果链表,并移动相应指针,当某个链表遍历完成后,直接将另一个链表的剩余部分接上即可。

由此我们可以依次遍历链表数组,每次只合并两个链表,就可以将问题简化。

解法一

/*** 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* mergeTwoLists(ListNode* list1, ListNode* list2) {// 使用哑节点简化操作ListNode* dummy = new ListNode(0);ListNode* current = dummy;while (list1 != nullptr && list2 != nullptr) {if (list1->val <= list2->val) {current->next = list1;list1 = list1->next;} else {current->next = list2;list2 = list2->next;}current = current->next;}// 连接剩余部分if (list1 != nullptr) {current->next = list1;} else {current->next = list2;}ListNode* result = dummy->next;delete dummy; // 清理哑节点return result;}ListNode* mergeKLists(vector<ListNode*>& lists) {if (lists.empty()) {return nullptr;}// 逐个合并链表ListNode* result = lists[0];for (int i = 1; i < lists.size(); i++) {result = mergeTwoLists(result, lists[i]);}return result;}
};

时间复杂度:O(N × k),设链表数组有 k 个链表,设所有链表的总节点数为 N。

空间复杂度:O(1)。

解法二:借助优先队列

我们可以借助优先队列来优化合并过程,具体思路是:首先将K个链表的头节点按节点值升序排列存入优先队列中,此时队首元素即为当前所有待合并节点中的最小值;每次从队首取出这个最小节点并入结果链表,然后判断该节点所在链表是否还有后续节点,若有则将其后续节点重新压入优先队列中,保持队列始终包含各链表当前待比较的最小节点;重复此过程直到优先队列为空,此时所有节点都已正确合并到结果链表中。

补充:优先队列三参数

T:存什么

Container:用什么容器存

Compare:怎么比较(决定是最大堆还是最小堆)

class Solution {
public:struct compare {bool operator()(ListNode* a, ListNode* b) { return a->val > b->val; }};ListNode* mergeKLists(vector<ListNode*>& lists) {if (lists.empty()) {return nullptr;}if (lists.size() == 1) {return lists[0];}// 以上两步特殊情况判断可省略priority_queue<ListNode*, vector<ListNode*>, compare> pq;for (int i = 0; i < lists.size(); i++) {if (lists[i] != nullptr)pq.push(lists[i]);}ListNode* dummy = new ListNode(0);ListNode* current = dummy;while (!pq.empty()) {ListNode* temp = pq.top();pq.pop();current->next = temp;current = current->next;temp = temp->next;if (temp != nullptr) {pq.push(temp);}}ListNode* res = dummy->next;delete dummy;return res;}
};

设链表数组有 k 个链表,设所有链表的总节点数为 N。

时间复杂度:O(N × logk)。

        建堆操作:初始化时插入k个链表头 → O(k × logk)。

        循环操作:每次弹出堆顶元素 O(logk),每次插入新元素(如果存在)O(logk),总共处理 N 个节点。

空间复杂度:O(k),优先队列最多同时存储 k 个头指针。

补充:为优先队列(priority_queue)编写比较器

方法一:结构体 + 重载 operator()
struct Compare {bool operator()(int a, int b) {return a > b;  // 最小堆:小的优先级高}
};// 使用
priority_queue<int, vector<int>, Compare> pq;
方法二:使用 lambda 表达式(C++11)
auto comp = [](int a, int b) { return a > b; };
priority_queue<int, vector<int>, decltype(comp)> pq(comp);

decltype:用于推导表达式类型,特别适用于 lambda 表达式等匿名类型

方法三:使用函数指针
bool compare(int a, int b) {return a > b;
}// 使用
priority_queue<int, vector<int>, decltype(&compare)> pq(compare);

如有问题或建议,欢迎在评论区中留言~

http://www.dtcms.com/a/469364.html

相关文章:

  • 初学者小白复盘14之——指针(3)
  • word和wps下分别设置签名或图片背景透明色的方法
  • 适合户外探险、物流、应急、工业,五款三防智能手机深度解析
  • Java 在 Word 文档中插入图片
  • Python 处理 Word 文档中的批注(添加、删除)
  • 做一个什么网站好软件推广联盟
  • 480元做网站昆明网
  • 使用 openpyxl 生成 excel 折线图
  • Java-idea编辑器中Jar方式打包启动
  • vim 编辑中,临时挂起编辑器进程,返回到终端命令行
  • 基于 Reactor 模式的 HTTP 协议扩展实现
  • 2025 FastExcel在Java的Maven项目的导出和导入,简单易上手,以下为完整示例
  • 做的好点的外贸网站有哪些网站建设实训指导书
  • 【Linux】Centos 8 默认OpenSSH 升级OpenSSH9.8【升级其他OpenSSH版本通用】
  • 【Nginx开荒攻略】深度解析基于域名的虚拟主机配置:从域名解析到实战部署
  • 互联网网站样式坪山建设网站建站
  • 全链路智能运维中的业务影响度评估与资源动态优化机制
  • 微信小程序学习(五)
  • Jmeter接口的负载测试概念
  • Linux-CentOS 7 上安装 MySQL 8.0.43(保姆级教程)
  • 视频分辨率4K,比特率50000kbps,电脑播放时卡顿的原因
  • 使用aspx做电影网站网站建设专用术语
  • Linux内核网络优化:两个网络调优解决方案
  • day7_vite 啊哈哈啊哈哈哈哈哈
  • 化妆品产品的自建网站哟哪些能想到的域名都被注册了
  • 网络协议的零拷贝 和 操作系统的零拷贝异同
  • Apache Drill:一款开源的分布式SQL查询引擎
  • 八年磨一剑:中品维度如何用“分布式电商”为商家打开增长新通路?
  • Linux下的Rust 与 C 的互操作性解析
  • 从“用框架”到“控系统”———架构通用能力(模块边界、分层设计、缓存策略、事务一致性、分布式思维)