牛客热题:合并K个升序链表

2024-05-01 05:04

本文主要是介绍牛客热题:合并K个升序链表,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

📟作者主页:慢热的陕西人

🌴专栏链接:力扣刷题日记

📣欢迎各位大佬👍点赞🔥关注🚓收藏,🍉留言

在这里插入图片描述

文章目录

  • 牛客热题:合并K个升序链表
    • 题目链接:
    • 方法一:复用2个升序链表的方法
      • 思路:
      • 代码:
      • 复杂度:
    • 方法二:第一种方法的分治优化-->借鉴牛客题解
      • 思路:
      • 代码:
      • 复杂度:
    • 方法三:优先队列-->借鉴牛客题解
      • 思路:
      • 代码:
      • 复杂度:

牛客热题:合并K个升序链表

题目链接:

合并k个已排序的链表_牛客题霸_牛客网 (nowcoder.com)

方法一:复用2个升序链表的方法

思路:

  • 首先我们知道如何合并两个升序链表
  • 那么我们先将k个的前两个合并,然后再将和这个合并的链表和下一个链表合并…直到所有的链表都被合并

代码:

 ListNode* Merge(ListNode* pHead1, ListNode* pHead2){//申请一个哨兵位ListNode* head = new ListNode(0);ListNode* cur = head;while(pHead1 != nullptr && pHead2 != nullptr){if(pHead1->val <= pHead2->val){cur->next = pHead1;cur = cur->next;pHead1 = pHead1->next;}else{cur->next = pHead2;cur = cur->next;pHead2 = pHead2->next;}}//p1未完的情况while(pHead1 != nullptr){cur->next = pHead1;cur = cur->next;pHead1 = pHead1->next;}//p2未完的情况while(pHead2 != nullptr){cur->next = pHead2;cur = cur->next;pHead2 = pHead2->next;}return head->next;}ListNode* mergeKLists(vector<ListNode*>& lists) {if(lists.size() == 0) return nullptr;ListNode* ret = lists[0];for(int i = 1; i < lists.size(); ++i){ret = Merge(ret, lists[i]);}  return ret;}

复杂度:

时间复杂度:O( N 2 N^2 N2), 但其实一般达不到O( N 2 N^2 N2);

  • 对于第一个链表:我们遍历了k-1次
  • 对于第二个链表:我们遍历了k-2次
  • 对于最后一个链表:我们遍历了1次

由于所有的链表的长度加起来为 n n n,那么平均长度为 n / k n / k n/k,

每个链表最多被遍历k - 1次,我们放缩为k次,那么需要最多 n ∗ n n*n nn次运算.

空间复杂度:O(1), 使用了常数个额外的空间。

方法二:第一种方法的分治优化–>借鉴牛客题解

思路:

具体做法:

  • step 1:从链表数组的首和尾开始,每次划分从中间开始划分,划分成两半,得到左边 n / 2 n/2 n/2个链表和右边 n / 2 n/2 n/2个链表。
  • step 2:继续不断递归划分,直到每部分链表数为1.
  • step 3:将划分好的相邻两部分链表,按照两个有序链表合并的方式合并,合并好的两部分继续往上合并,直到最终合并成一个链表。

合并k个升序链表

代码:

     ListNode* Merge(ListNode* pHead1, ListNode* pHead2){//申请一个哨兵位ListNode* head = new ListNode(0);ListNode* cur = head;while(pHead1 != nullptr && pHead2 != nullptr){if(pHead1->val <= pHead2->val){cur->next = pHead1;cur = cur->next;pHead1 = pHead1->next;}else{cur->next = pHead2;cur = cur->next;pHead2 = pHead2->next;}}//p1未完的情况while(pHead1 != nullptr){cur->next = pHead1;cur = cur->next;pHead1 = pHead1->next;}//p2未完的情况while(pHead2 != nullptr){cur->next = pHead2;cur = cur->next;pHead2 = pHead2->next;}return head->next;}ListNode* DivideMerge(vector<ListNode*> & lists, int l, int r){//不存在区间if(l > r) return nullptr;//已到达最小的区间if(l == r) return lists[l];int mid = l + r >> 1;return Merge(DivideMerge(lists, l, mid), DivideMerge(lists, mid + 1, r));}ListNode* mergeKLists(vector<ListNode*>& lists) {return DivideMerge(lists, 0, lists.size() - 1);}

复杂度:

  • 时间复杂度: O ( n l o g 2 K ) O(nlog_2K) O(nlog2K),其中 n n n为所有链表的总节点数,分治为二叉树型递归,最坏情况下二叉树每层合并都是O(N)个节点,因为分治一共有 O ( l o g 2 K ) O(log_2K) O(log2K)
  • 空间复杂度: O ( l o g 2 K ) O(log_2K) O(log2K),最坏的情况需要向下递归 l o g 2 K log_2K log2K层,需要 l o g 2 K log_2K log2K个函数栈帧

方法三:优先队列–>借鉴牛客题解

思路:

如果非要按照归并排序的合并思路,双指针不够用,我们可以直接准备k个指针,每次比较得出k个数字中的最小值,我们可以借助堆,也就是优先队列—>priority_queue来完成这一点。

代码:

   struct cmp{bool operator()(ListNode* a, ListNode* b){return a->val > b->val;}};ListNode* mergeKLists(vector<ListNode*>& lists) {//小根堆priority_queue<ListNode*, vector<ListNode*>, cmp> pq;//遍历所有链表的第一个元素,并且把不为空的加入到优先队列for(int i = 0; i < lists.size(); ++i){if(lists[i] != nullptr) pq.push(lists[i]);}ListNode* res = new ListNode(-1);ListNode* cur = res;while(!pq.empty()){//取出最小的元素ListNode* t = pq.top();pq.pop();//链接到尾部cur->next = t;cur = cur->next;//将该链表的下一个指针加入到优先队列if(t->next != nullptr)pq.push(t->next);}return res->next;}

复杂度:

  • 时间复杂度: O ( n l o g 2 K ) O(nlog_2K) O(nlog2K),其中 n n n为所有链表的总节点数,每次加入优先队列的复杂度为 O ( l o g 2 K ) O(log_2K) O(log2K)
  • 空间复杂度: O ( K ) O(K) O(K),优先队列的大小为K

这篇关于牛客热题:合并K个升序链表的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/950775

相关文章

hdu2241(二分+合并数组)

题意:判断是否存在a+b+c = x,a,b,c分别属于集合A,B,C 如果用暴力会超时,所以这里用到了数组合并,将b,c数组合并成d,d数组存的是b,c数组元素的和,然后对d数组进行二分就可以了 代码如下(附注释): #include<iostream>#include<algorithm>#include<cstring>#include<stack>#include<que

csu1329(双向链表)

题意:给n个盒子,编号为1到n,四个操作:1、将x盒子移到y的左边;2、将x盒子移到y的右边;3、交换x和y盒子的位置;4、将所有的盒子反过来放。 思路分析:用双向链表解决。每个操作的时间复杂度为O(1),用数组来模拟链表,下面的代码是参考刘老师的标程写的。 代码如下: #include<iostream>#include<algorithm>#include<stdio.h>#

day-51 合并零之间的节点

思路 直接遍历链表即可,遇到val=0跳过,val非零则加在一起,最后返回即可 解题过程 返回链表可以有头结点,方便插入,返回head.next Code /*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode() {}*

深入手撕链表

链表 分类概念单链表增尾插头插插入 删尾删头删删除 查完整实现带头不带头 双向链表初始化增尾插头插插入 删查完整代码 数组 分类 #mermaid-svg-qKD178fTiiaYeKjl {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-

建立升序链表

题目1181:遍历链表 时间限制:1 秒 内存限制:32 兆 特殊判题:否 提交:2744 解决:1186 题目描述: 建立一个升序链表并遍历输出。 输入: 输入的每个案例中第一行包括1个整数:n(1<=n<=1000),接下来的一行包括n个整数。 输出: 可能有多组测试数据,对于每组数据, 将n个整数建立升序链表,之后遍历链表并输出。 样例输

【每日一题】LeetCode 2181.合并零之间的节点(链表、模拟)

【每日一题】LeetCode 2181.合并零之间的节点(链表、模拟) 题目描述 给定一个链表,链表中的每个节点代表一个整数。链表中的整数由 0 分隔开,表示不同的区间。链表的开始和结束节点的值都为 0。任务是将每两个相邻的 0 之间的所有节点合并成一个节点,新节点的值为原区间内所有节点值的和。合并后,需要移除所有的 0,并返回修改后的链表头节点。 思路分析 初始化:创建一个虚拟头节点

每日一题|牛客竞赛|四舍五入|字符串+贪心+模拟

每日一题|四舍五入 四舍五入 心有猛虎,细嗅蔷薇。你好朋友,这里是锅巴的C\C++学习笔记,常言道,不积跬步无以至千里,希望有朝一日我们积累的滴水可以击穿顽石。 四舍五入 题目: 牛牛发明了一种新的四舍五入应用于整数,对个位四舍五入,规则如下 12345->12350 12399->12400 输入描述: 输入一个整数n(0<=n<=109 ) 输出描述: 输出一个整数

学习记录:js算法(二十八):删除排序链表中的重复元素、删除排序链表中的重复元素II

文章目录 删除排序链表中的重复元素我的思路解法一:循环解法二:递归 网上思路 删除排序链表中的重复元素 II我的思路网上思路 总结 删除排序链表中的重复元素 给定一个已排序的链表的头 head , 删除所有重复的元素,使每个元素只出现一次 。返回 已排序的链表 。 图一 图二 示例 1:(图一)输入:head = [1,1,2]输出:[1,2]示例 2:(图

【Python从入门到进阶】64、Pandas如何实现数据的Concat合并

接上篇《63.Pandas如何实现数据的Merge》 上一篇我们学习了Pandas如何实现数据的Merge,本篇我们来继续学习Pandas如何实现数据的Concat合并。 一、引言 在数据处理过程中,经常需要将多个数据集合并为一个统一的数据集,以便进行进一步的分析或建模。这种需求在多种场景下都非常常见,比如合并不同来源的数据集以获取更全面的信息、将时间序列数据按时间顺序拼接起来以观察长期趋势等

【LeetCode热题100】前缀和

这篇博客共记录了8道前缀和算法相关的题目,分别是:【模版】前缀和、【模版】二维前缀和、寻找数组的中心下标、除自身以外数组的乘积、和为K的子数组、和可被K整除的子数组、连续数组、矩阵区域和。 #include <iostream>#include <vector>using namespace std;int main() {//1. 读取数据int n = 0, q = 0;ci