Leetcode23.合并k个升序链表
文章目录
一、题目描述
给你一个链表数组,每个链表都已经按升序排列。
请你将所有链表合并到一个升序链表中,返回合并后的链表。
二、求解思路
因为需要将链表合并到一个新的链表中,因此需要创建一个新的链表用于存放这个合成链表。假设一共有k个链表那么如何快速得到 k 个节点中的最小节点,接到结果链表上?
- 此处可以想到使用小顶堆,它会自动按由小到大的顺序排列。(c++中可以用优先队列)
- 因为链表数组中的每个链表都是按照升序进行排列,因此只需将每个链表的头节点存入到小顶堆中,然后每次取出小顶堆的头节点(即这些节点中最小的节点),然后再将这个最小节点所在链表的下一节点存入到小顶堆中,依此类推…此时在队列中只需维持这k个节点即可。
- 另外,对于优先队列需要自定义比较函数,存在几种定义方式,会在下面的代码中给出。
三、代码
1.采用lambda表达式自定义比较函数
class Solution {
public:
ListNode* mergeKLists(vector<ListNode*>& lists) {
int k=lists.size();
ListNode* dummy=new ListNode(0);
ListNode* p=dummy;
//采用lambda表达式自定义比较函数
priority_queue< ListNode*,vector<ListNode*>,function<bool(ListNode*,ListNode*)>> que([] (ListNode* l1,ListNode* l2){return l1->val>l2->val;});
for(ListNode* head:lists){
if(head!=nullptr)
que.push(head);
}
while(!que.empty()){
ListNode* temp=que.top();
que.pop();
p->next=temp;
p=p->next;
if(temp->next!=nullptr)
que.push(temp->next);
}
return dummy->next;
}
};
这种自定义方式参考的labuladong<双指针技巧>
2.创建一个结构体,重载()符号
class Solution {
public:
class cmp{//自定义数据类型
public:
bool operator()(ListNode* l1,ListNode* l2){
return l1->val>l2->val;
}
};
ListNode* mergeKLists(vector<ListNode*>& lists) {
int k=lists.size();
ListNode* dummy=new ListNode(0);
ListNode* p=dummy;
priority_queue< ListNode*,vector<ListNode*>,cmp> que;
for(ListNode* head:lists){
if(head!=nullptr)
que.push(head);
}
while(!que.empty()){
ListNode* temp=que.top();
que.pop();
p->next=temp;
p=p->next;
if(temp->next!=nullptr)
que.push(temp->next);
}
return dummy->next;
}
};
这种自定义方式参考的代码随想录<栈与队列8.>
3.使用function.自定义比较函数
class Solution {
public:
static bool Mycmp(ListNode* l1,ListNode* l2){ //注意需要加static
return l1->val>l2->val;
}
ListNode* mergeKLists(vector<ListNode*>& lists) {
int k=lists.size();
ListNode* dummy=new ListNode(0);
ListNode* p=dummy;
priority_queue< ListNode*,vector<ListNode*>,function<bool(ListNode*,ListNode*)>> que(Mycmp);
for(ListNode* head:lists){
if(head!=nullptr)
que.push(head);
}
while(!que.empty()){
ListNode* temp=que.top();
que.pop();
p->next=temp;
p=p->next;
if(temp->next!=nullptr)
que.push(temp->next);
}
return dummy->next;
}
};
注意:要在自己定义的Mycmp函数前加上static,不然会遇到如下错误:
此错误显示:必须调用对非静态成员函数的引用
解决办法: 在Mycmp函数的返回类型前加上static。
function.的定义方式参考的这里
四、拓展
另外还用到大小顶堆的有Leetcode347.前 K 个高频元素。
1.题目描述
给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。
2.思路
同样采用小顶堆的方式,因为要统计最大前k个元素,只有小顶堆每次将最小的元素弹出,最后小顶堆里积累的才是前k个最大元素。
3.代码
具体代码及相关思路讲解如下:(时间复杂度为nlogk)
class Solution {
public:
class cmp{//注意是class类型,struct也可以
public:
bool operator()(const pair<int,int> l,const pair<int,int> r){
return l.second>r.second;
}
};
vector<int> topKFrequent(vector<int>& nums, int k) {
unordered_map<int,int>map;
for(int num:nums){
map[num]++;
}
priority_queue<pair<int,int>,vector<pair<int,int>>,cmp>que;//注意vector里面的元素也是pair<int,int>
for(unordered_map<int,int>::iterator it=map.begin();it!=map.end();it++){
que.push(*it);//注意是*it
if(que.size()>k){//在队列中只维持k个元素,当超出k个元素时就把出现频率低的元素弹出
que.pop();
}
}
vector<int>res(k);
for(int i=k-1;i>=0;i--){
res[i]=que.top().first;
que.pop();
}
return res;
}
};
最后是按照出现频率由高到低的顺序进行输出的。
五、附上面试时的算法题
有N个链表均为升序排列方式,找出这N个链表中第k小的元素。
例:现有3个链表,找出第4小的元素。
{2,3,4,5,7}{3,5,9,11,14}{1,6,7,8},第4小的元素为3
思路:也是采用优先队列的方式,队列中只需维护N个链表中一个节点即可,依次找到
struct ListNode{
int val;
ListNode* next;
ListNode():val(0),next(nullptr){}
ListNode(int x):val(x),next(nullptr){}
};
class cmp{
public: //注意!!别忘了写public
bool operator()(ListNode* l1,ListNode* l2){
return l1->val>l2->val;
}
};
class Solution{
public:
int mergeKLists(vector<ListNode*>& lists,int k) {
priority_queue<ListNode*,vector<ListNode*>,cmp> que; //注意第二个参数代表容器类型
for(auto head:lists){
if(head!=nullptr) //注意!
que.push(head);
}
ListNode* cur;
while(k){
ListNode* temp=que.top();
que.pop();
k--;
que.push(temp->next);
cur=temp;
}
return cur->val;
}
};
int main(){
vector<ListNode*> vec;
ListNode* dummy1=new ListNode(0);
ListNode* l1=dummy1;
l1->next=new ListNode(2);
l1=l1->next;
l1->next=new ListNode(3);
l1=l1->next;
l1->next=new ListNode(4);
vec.push_back(dummy1->next);
ListNode* dummy2=new ListNode(0);
ListNode* l2=dummy2;
l2->next=new ListNode(3);
l2=l2->next;
l2->next=new ListNode(5);
l2=l2->next;
l2->next=new ListNode(9);
vec.push_back(dummy2->next);
ListNode* dummy3=new ListNode(0);
ListNode* l3=dummy3;
l3->next=new ListNode(1);
l3=l3->next;
l3->next=new ListNode(6);
l3=l3->next;
l3->next=new ListNode(7);
vec.push_back(dummy3->next);
int k=4;
Solution s;
int result=s.mergeKLists(vec,k);
cout<<result<<endl;
return 0;
}
或者直接采用lambda表达式的形式自定义比较函数:
priority_queue<ListNode*,vector<ListNode*>,function<bool(ListNode*,ListNode*)>>
que([](ListNode* l1,ListNode* l2){return l1->val>l2->val;}); //注意第二个参数代表容器类型
运行结果:
总结
以上就是用到小顶堆的两道题目,分别为链表和数组类型。概括了一下比较函数不同的自定义方式。