合并 k 个排序链表,返回合并后的排序链表。请分析和描述算法的复杂度。

示例:

输入:
[
  1->4->5,
  1->3->4,
  2->6
]
输出: 1->1->2->3->4->4->5->6

我们熟悉的是两个链表之间的合并,这道题的难点在于k值是不固定的,并不知道要合并多少链表。一种很常见的想法就是使用分治法,因为合并两个有序链表是合并k个有序链表的子问题。

分治法一

多次进行两个链表之间的合并,将k个有序链表转化为(k+1)/2个有序链表。例如有6个链表时,想要合并为3个链表,则链表0与链表3合并得到新的链表0,链表1与链表4合并得到新的链表1,链表2与链表5合并得到新的链表2。最终所有链表结点都合并到了链表0,即为所求结果。

举例说明该过程:

[[1,4,5],[1,3,4],[2,6]]->[[1,2,4,5,6],[1,3,4]]->[[1,1,2,3,4,4,5,6]]

    ListNode*mergeTwoLists(ListNode*l1,ListNode*l2){
ListNode*dummy=new ListNode(),*cur=dummy;
while(l1&&l2){
if(l1->val<l2->val){
cur->next=l1;
l1=l1->next;
}
else{
cur->next=l2;
l2=l2->next;
}
cur=cur->next;
}
if(l1) cur->next=l1;
if(l2) cur->next=l2;
return dummy->next;
}
ListNode* mergeKLists(vector<ListNode*>& lists) {
int n=lists.size();
//n=0返回空,n=1返回lists[0]
if(n==0) return NULL;
while(n>1){
//k为合并的步长
int k=(n+1)/2;
//如果n为奇数则正中间链表不参与合并
for(int i=0;i<n/2;++i){
//将lists[i]和lists[i+k]合并到lists[i]
lists[i]=mergeTwoLists(lists[i],lists[i+k]);
}
//将n个链表经过两两合并之后转为(n+1)/2个链表
n=k;
}
//n=1退出循环,此时所有链表都合并进了lists[0]
return lists[0];
}

分治法二

类似归并排序

    ListNode*mergeTwoLists(ListNode*l1,ListNode*l2){
ListNode*dummy=new ListNode(),*cur=dummy;
while(l1&&l2){
if(l1->val<l2->val){
cur->next=l1;
l1=l1->next;
}
else{
cur->next=l2;
l2=l2->next;
}
cur=cur->next;
}
if(l1) cur->next=l1;
if(l2) cur->next=l2;
return dummy->next;
}
ListNode*helper(vector<ListNode*>&lists,int left,int right){
if(left==right) return lists[left];
int mid=(left+right)/2;
ListNode*l1=helper(lists,left,mid);
ListNode*l2=helper(lists,mid+1,right);
//当前递归层的返回值
return mergeTwoLists(l1,l2);
}
ListNode* mergeKLists(vector<ListNode*>& lists) {
int n=lists.size();
if(n==0) return NULL;
return helper(lists,0,n-1);
}

最小堆

首先将k个链表的首元素添加到最小堆,取出最小元素加入到结果链表中。再将所加元素的下一个元素加入到最小堆(此时的最小元素要么还在最小堆中,要么是之前所加元素的下一个元素),取出最小元素加入到结果链表中。以此类推,直到堆中没有元素为止。

    public ListNode mergeKLists(ListNode[] lists) {
int n=lists.length;
PriorityQueue<ListNode> pq=new PriorityQueue<ListNode>(new Comparator<ListNode>(){
public int compare(ListNode l1,ListNode l2){
return l1.val-l2.val;
}
});
ListNode dummy=new ListNode(-1);
ListNode cur=dummy;
for(int i=0;i<n;++i) {
if(lists[i]!=null){
pq.add(lists[i]);
lists[i]=lists[i].next;
}
}
while(!pq.isEmpty()){
cur.next=pq.poll();
cur=cur.next;
//将取出元素的下一个元素加入堆中
if(cur.next!=null) pq.add(cur.next);
}
return dummy.next;
}

计数排序

依次遍历所有链表,记录所有元素中的最小值和最大值,那么每个元素值必定在它们两者之间。同时记录所有元素出现的次数。然后就可以从最小值遍历到最大值,根据其出现的次数建立相应个数的结点。

    public ListNode mergeKLists(ListNode[] lists) {
int n=lists.length;
ListNode dummy=new ListNode(-1),cur=dummy;
HashMap<Integer,Integer> hashmap=new HashMap<>();
int min=Integer.MAX_VALUE;
int max=Integer.MIN_VALUE;
for(int i=0;i<n;++i){
while(lists[i]!=null){
if(hashmap.containsKey(lists[i].val))
hashmap.put(lists[i].val,hashmap.get(lists[i].val)+1);
else hashmap.put(lists[i].val,1);
if(lists[i].val<min) min=lists[i].val;
if(lists[i].val>max) max=lists[i].val;
lists[i]=lists[i].next;
}
}
for(int i=min;i<=max;++i){
if(!hashmap.containsKey(i)) continue;
for(int j=0;j<hashmap.get(i);++j){
cur.next=new ListNode(i);
cur=cur.next;
}
}
return dummy.next;
}

leetcode原题:https://leetcode-cn.com/problems/merge-k-sorted-lists

23.合并k个有序链表的更多相关文章

  1. [LeetCode] 23. Merge k Sorted Lists 合并k个有序链表

    Merge k sorted linked lists and return it as one sorted list. Analyze and describe its complexity. E ...

  2. [LeetCode] Merge k Sorted Lists 合并k个有序链表

    Merge k sorted linked lists and return it as one sorted list. Analyze and describe its complexity. 这 ...

  3. LeetCode 23. 合并K个排序链表(Merge Two Sorted Lists)

    23. 合并K个排序链表 23. Merge k Sorted Lists 题目描述 合并 k 个排序链表,返回合并后的排序链表.请分析和描述算法的复杂度. LeetCode23. Merge k S ...

  4. leetcode python 012 hard 合并k个有序链表

    #[LeetCode] Merge k Sorted Lists 合并k个有序链表(升序) import numpy as npimport time class Node(object):    d ...

  5. 023 Merge k Sorted Lists 合并K个有序链表

    合并K个有序链表,并且作为一个有序链表的形式返回.分析并描述它的复杂度. 详见:https://leetcode.com/problems/merge-k-sorted-lists/descripti ...

  6. 【LeetCode】23.合并K个排序链表

    题目描述 23.合并K个排序链表 合并k个排序链表,返回合并后的排序链表.请分析和描述算法的复杂度. 示例: 输入: [ 1->4->5, 1->3->4, 2->6 ] ...

  7. Java实现 LeetCode 23 合并K个排序链表

    23. 合并K个排序链表 合并 k 个排序链表,返回合并后的排序链表.请分析和描述算法的复杂度. 示例: 输入: [ 1->4->5, 1->3->4, 2->6 ] 输 ...

  8. [LeetCode题解]23. 合并K个升序链表 | 分治 + 递归

    方法一:分治 + 递归 解题思路 在21. 合并两个有序链表,我们知道如何合并两个有序链表.而本题是合并 k 个有序链表,可以通过大问题拆分成小问题解决,即把 k 个链表,拆分成 k/2 个链表组,俩 ...

  9. LeetCode 23 ——合并 K 个排序链表

    1. 题目 2. 解答 2.1. 方法一 在 合并两个有序链表 的基础上,我们很容易想到第一种解法,首先我们将第一个链表和第二个链表合并成一个新的链表,然后再往后依次合并接下来的每个链表即可. 假设每 ...

随机推荐

  1. springboot通过AOP和自定义注解实现权限校验

    自定义注解 PermissionCheck: package com.mgdd.sys.annotation; import java.lang.annotation.*; /** * @author ...

  2. Redis哨兵机制的实现及与SpringBoot的整合

    1. 概述 前面我们聊过Redis的读写分离机制,这个机制有个致命的弱点,就是主节点(Master)是个单点,如果主节点宕掉,整个Redis的写操作就无法进行服务了. 为了解决这个问题,就需要依靠&q ...

  3. JDK7u21反序列化详解

    目录 前言 环境 倒序分析 TemplatesImpl AnnotationInvocationHandler HashMap 总结 前言 听说jdk7u21的反序列化涉及的知识量很多,很难啃,具体来 ...

  4. Spring表达式

    一.SpEL 其中,直接写也可以赋值,' ' 单引号引起来后成为一个字符串对象,可以调用String的方法: 二.引用另外一个bean 装配这个类的bean: 1.第一种方法,property标签中使 ...

  5. IO流实现简单的文件的剪切

    思路: 判断 即将 复制的文件是文件夹还是文件 遍历需要复制的源文件夹 如果是文件夹,就通过流创建一个同样的子文件夹 如果是文件,就复制过去 接下来上代码 public class Demo1 { p ...

  6. Jupyter Notebook 修改字体和大小

    1 打开这个目录 D:\SoftWare\Anaconda\Lib\site-packages\notebook\static\custom 点开这个 也就是你安装的 python下面的 jupyte ...

  7. Java基础系列(30)- 命令行传参

    命令行传参 有时候你希望运行一个程序的时候再传递给它消息.这就要靠传递命令行参数main()函数实现 package method; public class CommandLine { public ...

  8. Centos7 搭建sonarQube

       可以支持包括java,C#,C/C++,PL/SQL,Cobol,JavaScrip,Groovy等等二十几种编程语言的代码质量管理与检测: SonarQube官网:https://www.so ...

  9. python学习笔记(七)-函数

    函数 方法 功能说白了,函数就是把一堆代码组合到一起,变成一个整体.函数不调用不会被执行.作用在于提高代码的复用性.定义函数 def greet_user(): """关 ...

  10. abp 以或的方式验证多个 AuthorizeAttribute

    前言 在使用 abp 开发业务功能时,会遇到公用同一个类的情况,在给这个类配置权限时,就要添加多个 AuthorizeAttribute,类似下面这样: [Authorize(DcsPermissio ...