合并K个升序链表(LeetCode 23)

文章目录

  • 1.问题描述
  • 2.难度等级
  • 3.热门指数
  • 4.解题思路
    • 方法一:顺序合并
    • 方法二:分治合并
    • 方法三:使用优先队列合并
  • 参考文献

1.问题描述

给你一个链表数组,每个链表都已经按升序排列。

请你将所有链表合并到一个升序链表中,返回合并后的链表。

示例 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 = []
输出:[]

示例 3:

输入:lists = [[]]
输出:[]

2.难度等级

Hard。

3.热门指数

★★★★☆

4.解题思路

方法一:顺序合并

我们可以想到一种最朴素的方法,依次将链表数组中的链表与最终结果合并。问题便退化成合并两个有序链表。

如何合并两个有序链表呢?

遍历两个链表选择值较小的结点链接到结果链表中即可。当一个节点被添加到结果链表之后,将对应链表中的节点向后移一位。

为了简化对结果链表边界条件的判断,可以引入哨兵结点。哨兵结点的 Next 指针便是结果链表的头结点。

遍历 list1 或 list2 链表,选择 list1 与 list2 链表当前结点值较小的结点,挂接到结果链表,并将较小结点后移一位。
如果有一个为空,结束遍历,则将未遍历完的链表,直接挂接到结果链表。

时间复杂度: 假设每个链表的最长长度是 n。在第一次合并后,结果链表的长度为 n;第二次合并后,结果链表的长度为 2n,第 i 次合并后,结果链表的长度为 in。第 i 次合并的时间代价是 O(n+(i−1)×n)= O(in),那么总的时间代价为
O ( ∑ i = 1 k ( i × n ) ) = O ( ( 1 + k ) ⋅ k 2 × n ) = O ( k 2 n ) O(\sum_{i = 1}^{k} (i \times n)) = O(\frac{(1 + k)\cdot k}{2} \times n) = O(k^2 n) O(i=1k(i×n))=O(2(1+k)k×n)=O(k2n)
故渐进时间复杂度为 O ( k 2 n ) O(k^2n) O(k2n),其中 k 为链表个数。

空间复杂度: 没有用到与 k 和 n 规模相关的辅助空间,故渐进空间复杂度为 O(1)。

下面以 Golang 为例给出实现。

func merge(head1, head2 *ListNode) *ListNode {
	dummyHead := &ListNode{}
	temp, temp1, temp2 := dummyHead, head1, head2
	for temp1 != nil && temp2 != nil {
		if temp1.Val < temp2.Val {
			temp.Next = temp1
			temp1 = temp1.Next
		} else {
			temp.Next = temp2
			temp2 = temp2.Next
		}
		temp = temp.Next
	}
	if temp1 != nil {
		temp.Next = temp1
	} else {
		temp.Next = temp2
	}
	return dummyHead.Next
}

func mergeKLists(lists []*ListNode) *ListNode {
	var head *ListNode
	for _, l := range lists {
		head = merge(head, l)
	}
	return head
}

方法二:分治合并

可以优化方法一,采用分治的方法进行合并。

  • 将 k 个链表配对并将同一对中的链表合并;
  • 第一轮合并以后, k 个链表被合并成了 k 2 \frac{k}{2} 2k个链表,然后是 k 4 \frac{k}{4} 4k 个链表, k 8 \frac{k}{8} 8k 个链表等等。
    重复这一过程,直到我们得到了最终的有序链表。
  • 重复这一过程,直到我们得到最终的有序链表。

在这里插入图片描述
时间复杂度: 考虑递归「向上回升」的过程,每一轮合并的时间复杂度都是 O(kn),需要合并 logk 轮,所以总的时间复杂度是 O(kn*logk)。

空间复杂度: 递归会使用到 O(log⁡k) 空间代价的栈空间。

下面以 Golang 为例给出实现。

func merge(head1, head2 *ListNode) *ListNode {
	dummyHead := &ListNode{}
	temp, temp1, temp2 := dummyHead, head1, head2
	for temp1 != nil && temp2 != nil {
		if temp1.Val < temp2.Val {
			temp.Next = temp1
			temp1 = temp1.Next
		} else {
			temp.Next = temp2
			temp2 = temp2.Next
		}
		temp = temp.Next
	}
	if temp1 != nil {
		temp.Next = temp1
	} else {
		temp.Next = temp2
	}
	return dummyHead.Next
}

func divideMerge(lists []*ListNode, l, r int) *ListNode {
	if l == r {
		return lists[l]
	}

	mid := (l + r) / 2
	// 合并左链表
	lhead := divideMerge(lists, l, mid)
	// 合并右链表
	rhead := divideMerge(lists, mid+1, r)
	// 合并左右链表
	return merge(lhead, rhead)
}

func mergeKLists(lists []*ListNode) *ListNode {
	if len(lists) == 0 {
		return nil
	}
	if len(lists) == 1 {
		return lists[0]
	}
	return divideMerge(lists, 0, len(lists)-1)
}

方法三:使用优先队列合并

这个方法和前两种方法的思路有所不同,我们需要维护当前每个链表没有被合并的元素的最前面一个,kkk 个链表就最多有 kkk 个满足这样条件的元素,每次在这些元素里面选取 val\textit{val}val 属性最小的元素合并到答案中。在选取最小元素的时候,我们可以用优先队列来优化这个过程。

时间复杂度: 考虑优先队列中的元素不超过 k 个,那么插入和删除的时间代价为 O(log⁡k),这里最多有 kn 个结点,对于每个结点都被插入删除各一次,故总的时间代价为 O(kn*log⁡k)。

**空间复杂度:**这里用了优先队列,优先队列中的元素不超过 k 个,故渐进空间复杂度为 O(k)。

下面以 C++ 为例给出实现。

class Solution {
public:
    struct Status {
        int val;
        ListNode *ptr;
        bool operator < (const Status &rhs) const {
            return val > rhs.val;
        }
    };

    priority_queue <Status> q;

    ListNode* mergeKLists(vector<ListNode*>& lists) {
        for (auto node: lists) {
            if (node) q.push({node->val, node});
        }
        ListNode head, *tail = &head;
        while (!q.empty()) {
            auto f = q.top(); q.pop();
            tail->next = f.ptr; 
            tail = tail->next;
            if (f.ptr->next) q.push({f.ptr->next->val, f.ptr->next});
        }
        return head.next;
    }
};

参考文献

23. 合并K 个升序链表 - LeetCode

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/331379.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

GNSS数据下载软件 -- 武汉大学 Fast软件(体验感极佳~)

目录 一、简介与下载地址 1.介绍 2.软件特点 3.下载地址 4.以github下载链接为例 二、下载方法(三种方法&#xff0c;以windows系统为例) 1.双击"Fast.exe"根据提示引导下载 2.手动输入"cmd"进入命令行界面&#xff0c;通过输入相关命令进行下载 …

【GAMES101】Lecture 07 着色(shading)

目录 着色 Blinn-Phong反射模型 漫反射 光衰减 着色 这个着色&#xff08;shading&#xff09;就是将不同的材质应用到不同的物体上&#xff0c;像一个物体&#xff0c;它可以是木头的、金属的、塑料的…… Blinn-Phong反射模型 我们来看一个简单的着色模型&#xff0c;…

机器学习--人工智能概述

人工智能概述 入门人工智能&#xff0c;了解人工智能是什么。为啥发展起来&#xff0c;用途是什么&#xff0c;是最重要也是最关键的事情。大致有以下思路。 人工智能发展历程机器学习定义以及应用场景监督学习&#xff0c;无监督学习监督学习中的分类、回归特点知道机器学习…

NoClassDefFoundError: org/mybatis/logging/LoggerFactory

NoClassDefFoundError: org/mybatis/logging/LoggerFactory 问题描述问题分析问题解决 问题描述 org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name userServiceImpl: Unsatisfied dependency expressed through field baseM…

QT 绘图与重绘事件

代码实现仪表盘 .cpp #include "widget.h" #include "ui_widget.h"#include <QPainter> #include <QPen> #include <QBrush>#include <QDebug> Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->…

【网站项目】基于springboot与vue的电子商城项目

&#x1f64a;作者简介&#xff1a;多年一线开发工作经验&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。&#x1f339;赠送计算机毕业设计600个选题excel文件&#xff0c;帮助大学选题。赠送开题报告模板&#xff…

C++设计模式(李建忠)笔记3

C设计模式&#xff08;李建忠&#xff09; 本文是学习笔记&#xff0c;如有侵权&#xff0c;请联系删除。 参考链接 Youtube: C设计模式 Gtihub源码与PPT&#xff1a;https://github.com/ZachL1/Bilibili-plus 豆瓣: 设计模式–可复用面向对象软件的基础 文章目录 C设计模…

STL常用容器—vector容器

STL常用容器—vector容器 vector基本概念容器的基本操作容器的常见方法容器迭代器&#xff08;遍历&#xff09;容器的插入与删除容器的嵌套及存放自定义数据容器的嵌套容器存放自定义数据 vector基本概念 功能&#xff1a; vector数据结构和数组非常相似&#xff0c;也称为单…

Chrome 开发者工具

Chrome 开发者工具 介绍控制面板时间线下载信息概要请求列表单个请求时间线优化时间线上耗时项 lighthouse 插件Performance&#xff08;性能指标&#xff09;Accessibility&#xff08;可访问性&#xff09;Best Practices&#xff08;最佳实践&#xff09;SEO&#xff08;搜索…

Iris微服务框架_golang web框架_完整示例Demo

Iris简介 Iris是一款Go语言中用来开发web应用的框架&#xff0c;该框架支持编写一次并在任何地方以最小的机器功率运行&#xff0c;如Android、ios、Linux和Windows等。该框架只需要一个可执行的服务就可以在平台上运行了。 Iris框架以简单而强大的api而被开发者所熟悉。iris…

寒武纪显卡实现softmax的pingpong流水并行

在上一篇文章添加链接描述中我们介绍了寒武纪显卡实现基本的softmax代码&#xff0c;这里我们借助于寒武纪的流水并行来编写进一步的策略。 pingpongGDRAM2NRAM流水 仅仅计算max和sum使用流水 我们先考虑不使用SRAM的流水&#xff0c;我们设置两个NRAM上的长度为maxNum上的数…

STM32标准库开发——USART串口外设

USART外设介绍 USART (Universal Synchronous/AsynchronousReceiver/Transmitter&#xff09;通用同步/异步收发器USART是STM32内部集成的硬件外设&#xff0c;可根据数据寄存器的一个字节数据自动生成数据帧时序&#xff0c;从TX引脚发送出去&#xff0c;也可自动接收RX引脚的…

WebGL中开发AR应用

WebGL在本质上是用于在浏览器中进行3D和2D图形渲染的技术&#xff0c;而增强现实&#xff08;AR&#xff09;通常需要与现实世界的环境进行交互。要在WebGL中开发AR应用&#xff0c;您可以采取以下步骤&#xff0c;希望对大家有所帮助。北京木奇移动技术有限公司&#xff0c;专…

Arm Generic Interrupt Controller v3 and v4(GICv3v4)学习(一)

提示 该博客主要为个人学习&#xff0c;通过阅读官网手册整理而来&#xff08;个人觉得阅读官网的英文文档非常有助于理解各个IP特性&#xff09;。若有不对之处请参考参考文档&#xff0c;以官网参考文档为准。 Arm Generic Interrupt Controller v3 and v4学习一共分为三章&…

RHEL8 Samba服务器详细配置用户模式

任务&#xff1a; 配置server01为samba服务器&#xff0c;samba服务器的/companydata/sales为共享目录&#xff0c;共享名为sales&#xff0c;里面创建测试文件test_share.tar&#xff0c;创建用户组sales&#xff0c;创建组内用户sale1&#xff0c;要求配置用户模式访问&#…

Uniapp多选Popup(弹出层)

uniapp中多选组件很少&#xff0c;故个人简单开发了一个&#xff0c;可简单使用&#xff0c;也可根据个人需求稍微改进 支持的功能 单选多选&#xff08;默认&#xff09;限制选择数量默认选中禁用选项 属性说明 属性默认值说明singlefalsetrue为开启单选&#xff0c;否则为…

无需信用卡注册美区Apple ID指南

第一步 准备工作 1、一个没有注册过AppleID的邮箱&#xff0c;建议最好是Gmail邮箱 2、一个苹果手机&#xff0c;当然这个是必须的 3、需要科学上网 第二步 苹果网站注册 为了避免cookie的干扰&#xff0c;最好是在无痕模式下打开以上网页&#xff0c;创建你的AppleID&#…

rabbitmq-java基础详解

一、rabbitmq是什么&#xff1f; 1、MQ定义 MQ&#xff08;Message Queue&#xff09;消息队列 主要解决&#xff1a;异步处理、应用解耦、流量削峰等问题&#xff0c;是分布式系统的重要组件&#xff0c;从而实现高性能&#xff0c;高可用&#xff0c;可伸缩和最终一致性的架…

Spring+SpringMVC+Mybatis进行项目的整合

Spring SpringMVCM Mybatis 整合 一、 通过idea创建maven工程 二、 引入依赖项以及导入mybatis逆向工程的插件 将如下的文件替换所在工程的pom文件 <?xml version"1.0" encoding"UTF-8"?><project xmlns"http://maven.apache.org/POM/4…

HCIA的访问控制列表ACL

ACL -----access control-list 允许/拒绝 ACL作用&#xff1a; 1.实现访问控制 2.定义感兴趣流量 ACL分类&#xff1a; 标准ACL 2000-2999&#xff08;只关注源IP地址&#xff0c;使用时应该尽量靠近目标&#xff09; 扩展ACL 3000-3999&#xff1a;写ACL不能写在源上&…