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

力扣hot100:23. 合并 K 个升序链表

23. 合并 K 个升序链表
在这里插入图片描述
这题非常容易想到归并排序的思路,俩升序序列合并,可以使用归并的方法。

不过这里显然是一个多路归并排序;包含多个子数组的归并算法,这可以让我们拓展归并算法的思路。

假设n是序列个数,ni是单个序列长度,length是单个序列最大长度

1、顺序单次归并

从左往右依次进行归并,但是这种方法存在一定的缺点。假设n是序列个数,ni是单个序列长度,根据题设,这个方法的最大比较次数至少是:
n 1 + ( n 1 + n 2 ) + ( n 1 + n 2 + n 3 ) ⋅ ⋅ ⋅ = n ∗ n 1 + ( n − 1 ) ∗ n 2 + ( n − 2 ) ∗ n 3 ⋅ ⋅ ⋅ < = n ∗ ( n 1 + n 2 + n 3 + ⋅ ⋅ ⋅ ) = n 2 ∗ l e n g t h < = 1 0 4 ∗ 500 ∗ 1 0 4 n1+(n1+n2)+(n1+n2+n3)··· = n*n1 + (n-1)*n2 + (n-2)*n3··· <= n*(n1+n2+n3+···) = n^2 * length <=10^4*500*10^4 n1+(n1+n2)+(n1+n2+n3)⋅⋅⋅=nn1+(n1)n2+(n2)n3⋅⋅⋅<=n(n1+n2+n3+⋅⋅⋅)=n2length<=104500104
这相当于每个序列都需要被比较序列个数次,这换成是多路归并的数组合并也是一样的。

官方:
在这里插入图片描述
在这里插入图片描述

class Solution {
public:ListNode* mergeKLists(vector<ListNode*>& lists) {ListNode * head = nullptr;for(int i = lists.size() - 1;i >= 0; --i){head = merge(head, lists[i]);ListNode * temp = head;}return head;}
private:ListNode * merge(ListNode * p,ListNode * q){if(!p) return q;if(!q) return p;ListNode * head = p;if(head->val > q->val) head = q;if(head == p) p = p->next;else q = q->next;ListNode * temp = head;while(p && q){if(p->val > q->val){head->next = q;q = q->next;}else{head->next = p;p = p->next;}head = head->next;}head->next = p ? p : q;return temp;}
};

2、分治归并

使用分治的思想排序是很容易想到的,但是不能很容易的知道分治归并速度一定更快,接下来让我详细思考一下是否会更快:
我们可以考虑,每次将序列数量减半合并,那么每一层合并使用的时间是 O ( l e n g t h ∗ n ) O(length*n) O(lengthn),我们知道每层数量减半,那么一共是有 O ( l o g n ) O(logn) O(logn)层,所以时间复杂度为 O ( n l o g n ∗ l e n g t h ) O(nlogn * length) O(nlognlength)

为什么分治归并会比普通顺次归并要快?
可以这样看一下,使用分治,将所有数分为两个区间[l,mid][mid+1,r],左区间的数 和 右区间的数只会在最后合并时比较一次,其他时候打死不相往来。而使用顺序归并,左区间的数 和 右区间的数会比较很多次,在考虑到
在这里插入图片描述
在这里插入图片描述

class Solution {
public:ListNode* mergeKLists(vector<ListNode*>& lists) {if(lists.size() == 0) return nullptr;return mergesort(lists, 0, lists.size() - 1);}
private:ListNode * merge(ListNode * p,ListNode * q){if(!p) return q;if(!q) return p;ListNode * head = p;if(head->val > q->val) head = q;if(head == p) p = p->next;else q = q->next;ListNode * temp = head;while(p && q){if(p->val > q->val){head->next = q;q = q->next;}else{head->next = p;p = p->next;}head = head->next;}head->next = p ? p : q;return temp;}ListNode * mergesort(vector<ListNode*>& lists,int left,int right){if(left == right) return lists[left];int mid = (left + right) >> 1;ListNode * p = mergesort(lists,left,mid);ListNode * q = mergesort(lists,mid + 1, right);return merge(p, q);}
};

3、使用优先队列合并

这种方式非常牛。

我们将所有序列,依据序列头部的元素大小放入一个优先队列,那么这个优先队列的深度是 l o g n logn logn,然而我们每次取出一个结点它的头部必然是现在里面最小的,将它放入待合并的目标序列中,然后将该序列后移一位,插入到优先队列中。因此每个元素插入时间是 O ( l o g n ) O(logn) O(logn),一共有 ( l e n g t h ∗ n ) (length * n) (lengthn)个元素。所以总时间为 O ( n l o g n ∗ l e n g t h ) O(nlogn*length) O(nlognlength)
在这里插入图片描述

class Solution {
public:ListNode* mergeKLists(vector<ListNode*>& lists) {if(lists.size() == 0) return nullptr;priority_queue<ListNode *,vector<ListNode *>,Sort> q;for(int i = lists.size() - 1;i >= 0; --i) if(lists[i]) q.push(lists[i]);ListNode * dummy = new ListNode;ListNode * temp = dummy;while(!q.empty()){ListNode * head = q.top();q.pop();temp->next = head;temp = temp->next;head = head->next;if(head) q.push(head);}temp = dummy->next;delete dummy;return temp;}
private:struct Sort{bool operator ()(const ListNode * a,const ListNode * b){return a->val > b->val;}};
};

能够实现优先队列,那么这个问题就很容易被解决。

  • 我们需要注意两个问题
    • 优先队列使用的比较函数必须自定义为结构体或者符号重载
    • 优先队列使用的比较函数的大于号小于号取值,和sort刚好相反。

使用方式:

priority_queue<Exp,vector<Exp>,cmp> q;
struct cmp{bool operator() (Exp a, Exp b){if() return true;return false;}
}

唯一变化的就是括号里面的类型Exp和你想要定义的比较方式。

http://www.lryc.cn/news/356972.html

相关文章:

  • Lightweight Robust Size Aware Cache Management——论文泛读
  • 搜索自动补全-elasticsearch实现
  • 连接远程的kafka【linux】
  • 简单的 Cython 示例
  • Laravel时间处理类Carbon
  • 2024年5月软考架构题目回忆分享
  • 香橙派 AIpro开发板初上手
  • 如何使用DotNet-MetaData识别.NET恶意软件源码文件元数据
  • LeetCode---栈与队列
  • 【教程】利用API接口添加本站同款【每日新闻早早报】-每天自动更新,不占用文章数量
  • 僵尸进程,孤儿进程,守护进程
  • Nuxt3 中使用 ESLint
  • 【Jmeter】性能测试之压测脚本生成,也可以录制接口自动化测试场景
  • Go 编程技巧:零拷贝字符串与切片转换的高效秘籍
  • 音视频开发—FFmpeg 音频重采样详解
  • 统计本地端口占用情况
  • 【MySQL精通之路】SQL优化(1)-查询优化(9)-外部联接优化
  • Python应用开发——30天学习Streamlit Python包进行APP的构建(1)
  • 轻兔推荐 —— 一个好用的软件服务推荐平台
  • LeetCode hot100-57-G
  • 基于Vue uni-app的自定义列表表格信息展示组件
  • 计网(部分在session学习章)
  • TypeScript 枚举
  • (1) 初识QT5
  • 2024年认证杯二阶段数学建模赛题浅析
  • Redis教程(十八):Redis的Redisson的看门狗机制
  • docker-compose 映射端口失败! docker端口映射失败 ,docker映射只能使用老端口,映射无法使用
  • AIGC笔记--基于PEFT库使用LoRA
  • yolo 算法 易主
  • 用这8种方法在海外媒体推广发稿平台上获得突破-华媒舍