【力扣】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);
如有问题或建议,欢迎在评论区中留言~