C++合并K个有序链表
本篇博客介绍如何使用C++合并k个有序链表,在代码中会用到std::priority_queue,首先需要介绍一下std::priority_queue的用法,介绍完std::priority_queue后将介绍如何使用std::priority_queue来辅助合并k个有序链表。
一、C++ priority_queue用法介绍
1.1 priority_queue基本用法
C++ 中的 priority_queue 是标准模板库(STL)中的一种数据结构,它是一个基于堆(默认为最大堆)的实现,用于自动排序插入的元素。在 priority_queue 中,元素被按优先级取出,这意味着最大的元素总是第一个被取出(如果你想要一个最小堆,你需要在声明时指定比较函数)。
priority_queue 在 <queue> 头文件中定义,通常用于需要按特定顺序处理元素的场景,如任务调度、带权路径搜索(如 Dijkstra 算法)等。
下面是一些 priority_queue 的基本用法示例:
包含头文件和创建 priority_queue
#include <iostream>
#include <queue>int main() {// 默认是一个最大堆std::priority_queue<int> pq;// 创建一个最小堆,需要提供比较函数std::priority_queue<int, std::vector<int>, std::greater<int>> min_pq;return 0;
}
向 priority_queue 添加元素
pq.push(30);
pq.push(20);
pq.push(50);
pq.push(40);
访问 priority_queue 的顶部元素
std::cout << "The top element is: " << pq.top() << std::endl; // 输出最大元素,此处为 50
移除 priority_queue 的顶部元素
pq.pop(); // 移除最大元素,此处为 50
检查 priority_queue 是否为空
if (pq.empty()) {std::cout << "The priority queue is empty." << std::endl;
} else {std::cout << "The priority queue is not empty." << std::endl;
}
获取 priority_queue 的大小
std::cout << "The priority queue size is: " << pq.size() << std::endl;
1.2 priority_queue使用自定义类型
如果你想在 priority_queue 中使用自定义类型,则需要定义比较方式。例如,如果你有一个结构体 Point 并希望根据点的距离原点的远近来排序,可以这样做:
#include <queue>
#include <vector>
#include <iostream>struct Point {double x, y;Point(double _x, double _y) : x(_x), y(_y){} double distance() const { return x * x + y * y; }
};// 定义比较函数
struct Compare {bool operator()(const Point& a, const Point& b) {return a.distance() < b.distance(); // 更远的点优先级更高}
};int main() {std::priority_queue<Point, std::vector<Point>, Compare> pq;pq.push(Point(1, 2));pq.push(Point(3, 4));pq.push(Point(0, 0));while (!pq.empty()) {Point top = pq.top();std::cout << "Point with highest priority (farthest): (" << top.x << ", " << top.y << ")" << std::endl;pq.pop();}return 0;
}
在这个例子中,我们定义了一个比较结构体 Compare,它重载了 () 运算符来允许 priority_queue 判断哪个点应该先出列。这里我们让距离原点更远的点有更高的优先级。
1.3 问题 std::priority_queue<Point, std::vector, Compare> pq;为什么要填3个参数
在C++中,std::priority_queue是一个容器适配器,它提供常数时间的最大元素查找,以及对数时间的插入与删除。std::priority_queue默认使用std::vector作为其底层容器,并使用std::less作为比较函数来构造一个最大堆。
在你的例子中:
std::priority_queue<Point, std::vector<Point>, Compare> pq;
这里的三个参数分别是:
Point: 这是队列中要存储的元素类型。std::vector<Point>: 这指定了底层容器类型。在这里是std::vector,它将被用来存储Point类型的元素。Compare: 这是一个比较类(或者函数对象),用于定义优先队列中元素的优先级。
Compare参数需要是一个可以比较两个Point对象的函数或者函数对象。在std::priority_queue中,这个比较函数决定了队列中元素的排序方式。具体地,Compare定义了一个严格的弱序,当你想要从队列中取出元素时,队列会根据这个比较函数给出的优先级,提供最"高"优先级的元素。
默认情况下,如果不提供Compare类型,std::priority_queue使用std::less,这会创建一个最大堆,其中最大元素首先被移除。如果你想要创建一个最小堆,你可以传递std::greater作为Compare类型参数。
例如,如果你的Point类有一个x坐标和一个y坐标,你可能想根据x值或者y值的大小来比较Point对象。你可以定义一个比较类,如下所示:
struct Compare {bool operator()(const Point& p1, const Point& p2) {// 假设我们根据x坐标来比较两个点return p1.x > p2.x;}
};
使用这个Compare类,priority_queue会根据Point对象的x坐标进行排序,x值最大的点将会是队列中的第一个元素(如果你希望队列为最小堆,则应该将比较运算符改为<)。
在模板编程中,提供这种灵活性是非常有用的,因为这允许程序员自定义数据结构的行为以满足特定需求。
二、C++合并K个有序链表
C++合并K个有序链表,要求合并后链表依然有序。
代码:
#include <iostream>
#include <vector>
#include <queue>struct ListNode {int val;ListNode *next;ListNode(int x) : val(x), next(nullptr) {}
};struct compare {bool operator()(const ListNode* l1, const ListNode* l2) {return l1->val > l2->val;}
};ListNode* mergeKLists(std::vector<ListNode*>& lists) {std::priority_queue<ListNode*, std::vector<ListNode*>, compare> pq;for (auto list : lists) {if (list) {pq.push(list); // 这里push的是链表的头节点,而不是链表本身,因为链表本身是一个指针,都是较小的值}}ListNode* dummy = new ListNode(0);ListNode* tail = dummy;while (!pq.empty()) {tail->next = pq.top();pq.pop();tail = tail->next;if (tail->next) {pq.push(tail->next); // next值再push到链表,比较大小,再pop出来}}ListNode* merged = dummy->next;delete dummy;return merged;
}// 辅助函数:创建链表
ListNode* createList(const std::vector<int>& vals) {ListNode* dummy = new ListNode(0);ListNode* current = dummy;for (int val : vals) {current->next = new ListNode(val);current = current->next;}ListNode* head = dummy->next;delete dummy;return head;
}// 辅助函数:打印链表
void printList(ListNode* head) {while (head) {std::cout << head->val << " ";head = head->next;}std::cout << std::endl;
}int main() {// 创建一些链表std::vector<ListNode*> lists;lists.push_back(createList({1, 4, 5}));lists.push_back(createList({1, 3, 4}));lists.push_back(createList({2, 6}));// 合并链表ListNode* mergedList = mergeKLists(lists);// 打印合并后的链表std::cout << "Merged List: ";printList(mergedList);// 清理内存while (mergedList != nullptr) {ListNode* temp = mergedList;mergedList = mergedList->next;delete temp;}return 0;
}
运行结果:
Merged List: 1 1 2 3 4 4 5 6
mergeKLists函数说明
mergeKLists这个函数的目的是将 k 个有序链表合并成一个有序链表。下面是逐步的解释:
-
初始化优先队列(最小堆):
std::priority_queue<ListNode*, std::vector<ListNode*>, compare> pq;- 这行代码创建了一个最小堆,用于存储链表的节点。这里使用了一个自定义的比较函数
compare,确保队列总是按照节点的值从小到大排列。
-
将所有链表的头节点加入优先队列:
for (auto list : lists) { if (list) pq.push(list); }- 这个循环遍历每个链表,并将每个链表的头节点(如果存在)加入优先队列。这样,队列中始终保存着当前未合并链表中最小的几个节点。
-
合并链表:
- 创建一个虚拟头节点
ListNode* dummy = new ListNode(0);和一个尾指针ListNode* tail = dummy;用于构建新的合并后的链表。 - 然后进入一个循环,此循环会持续直到优先队列为空:
while (!pq.empty()) { ... }- 在循环内部,从优先队列中取出最小的节点(即队列顶部的节点)并将其加入到合并后的链表中。
tail->next = pq.top(); pq.pop();- 将
tail指针移动到新加入的节点上。 tail = tail->next;- 如果新加入的节点有后续节点(即
tail->next不为空),则将这个后续节点加入优先队列中,以便在后续的迭代中考虑。
- 创建一个虚拟头节点
-
返回合并后的链表:
- 最后,函数返回合并后链表的头节点,即
dummy->next。 ListNode* merged = dummy->next;- 在返回之前,删除虚拟头节点以避免内存泄漏。
delete dummy;
- 最后,函数返回合并后链表的头节点,即
总之,这个函数通过使用优先队列来有效地找到每次应该添加到结果链表中的最小节点。
每次从队列中取出一个节点后,将其下一个节点(如果存在)加入队列,确保队列始终包含所有链表当前的最小节点。这样就可以按顺序合并所有链表。
如何获取合并后的头结点
在代码 ListNode* merged = dummy->next; 中,merged 是一个 ListNode 类型的指针,它被赋值为 dummy->next。这里的 dummy 是一个虚拟(哨兵)头节点,它不包含实际的数据,而是作为合并链表过程中的辅助节点。
让我们详细解释一下这个过程:
-
虚拟头节点 (
dummy) 的作用:- 在合并链表的过程中,为了方便处理边界情况(如空链表),通常会使用一个虚拟头节点。这个节点在开始时被创建,但它的值不被使用(在这个例子中,它被初始化为
0)。 - 虚拟头节点的主要作用是提供一个统一的起始点,无论是否有实际的数据节点。这样可以简化代码,避免处理特殊情况。
- 在合并链表的过程中,为了方便处理边界情况(如空链表),通常会使用一个虚拟头节点。这个节点在开始时被创建,但它的值不被使用(在这个例子中,它被初始化为
-
构建合并后的链表:
- 在函数中,新的合并后的链表是通过不断地将节点添加到
dummy节点之后构建的。这是通过移动tail指针来完成的,它始终指向当前合并链表的最后一个节点。
- 在函数中,新的合并后的链表是通过不断地将节点添加到
-
设置
merged指针:- 当所有的链表都被合并后,
dummy的下一个节点 (dummy->next) 实际上是合并后链表的第一个真实数据节点。 - 通过
ListNode* merged = dummy->next;,我们将merged指针设置为指向这个第一个真实数据节点。 - 这样,
merged就成为了合并后链表的头节点,而dummy节点已经完成了它的任务。
- 当所有的链表都被合并后,
-
返回合并后的链表:
- 函数最终返回
merged,即指向合并后链表的头节点的指针。
- 函数最终返回
-
删除虚拟头节点:
- 在返回
merged之前,代码中删除了dummy节点(delete dummy;),这是为了避免内存泄漏。由于merged已经指向了合并后链表的实际起始位置,dummy节点就不再需要了。
- 在返回
总结来说,ListNode* merged = dummy->next; 这行代码的目的是将 merged 指针设置为指向合并后链表的实际起始节点,从而可以返回正确的链表头部,同时避免包含不必要的虚拟头节点。
相关文章:
C++合并K个有序链表
本篇博客介绍如何使用C合并k个有序链表,在代码中会用到std::priority_queue,首先需要介绍一下std::priority_queue的用法,介绍完std::priority_queue后将介绍如何使用std::priority_queue来辅助合并k个有序链表。 一、C priority_queue用法介…...
win10在启动游戏时报错,提示“d3dx9_25.dll文件丢失”,怎么办?d3dx9_25.dll丢失如何自动修复
一、d3dx9_25.dll文件是什么? d3dx9_25.dll是DirectX的一部分,DirectX是一种由微软开发的专门处理与多媒体、游戏程序和视频相关的应用程序接口。d3dx9_25.dll文件是DirectX9中一个重要的dll文件,主要负责处理3D图形程序,作用是帮…...
16. 蒙特卡洛强化学习基本概念与算法框架
文章目录 1. 是什么2. 有何优点3. 基本概念3.1 立即回报3.2 累积回报3.3 状态值函数3.4 行为值函数3.4 回合(或完整轨迹,episode)3.5 多个回合(或完整轨迹)的描述 4.MC强化学习问题的正式描述5. 蒙特卡洛(M…...
QT中程序执行时间精准计算的三种方法及对比
一.QT程序在提升程序性能的调试中经常要计算一段程序的执行时间,下面介绍两种简单的实现方式,精确度都可以达到ms。 1.方式一 (1)代码: #include <QDateTime> qDebug() << "Current_date_and_tim…...
js下载方法分享*
JavaScript可以使用浏览器的API实现文件的下载,以下是一种常用的方法: 假设你已经有了一个文件 URL,你可以创建一个新的 a 标签,并将 href 属性设置为文件的 URL,然后模拟点击这个标签以开始下载。 function downloa…...
C# Stopwatch类_性能_时间计时器
文章只含部分属性方法等,有想了解全面的在下面链接中可以查看:.NET API browser Stopwatch 类 (System.Diagnostics) | Microsoft Learn 一、什么是Stopwatch Stopwatch:提供一组方法和属性,可以准确的测量运行时间。使用的时候需…...
鸿蒙原生应用再添新丁!天眼查 入局鸿蒙
鸿蒙原生应用再添新丁!天眼查 入局鸿蒙 来自 HarmonyOS 微博1月12日消息,#天眼查启动鸿蒙原生应用开发#作为累计用户数超6亿的头部商业信息查询平台,天眼查可以为商家企业,职场人士以及普通消费者等用户便捷和安全地提供查询海量…...
HarmonyOS4.0——ArkUI应用说明
一、ArkUI框架简介 ArkUI开发框架是方舟开发框架的简称,它是一套构建 HarmonyOS / OpenHarmony 应用界面的声明式UI开发框架,它使用极简的UI信息语法、丰富的UI组件以及实时界面语言工具,帮助开发者提升应用界面开发效率 30%,开发…...
基于模块自定义扩展字段的后端逻辑实现(二)
目录 一:创建表 二:代码逻辑 上一节我们详细讲解了自定义扩展字段的逻辑实现和表的设计,这一节我们以一个具体例子演示下,如何实现一个订单模块的自定义扩展数据。 一:创建表 订单主表: CREATE TABLE t_order ( …...
图像中部分RGB矩阵可视化
图像中部分RGB可视化 今天室友有个需求就是模仿下面这张图画个示意图: 大致就是把图像中的一小部分区域的RGB值可视化了一下。他居然不知道该怎么画,我寻思这不直接秒了。 import cv2 as cv import numpy as np import matplotlib.pyplot as pltclass …...
RPA财务机器人在厦门市海沧医院财务管理流程优化汇总的应用
目前国内外研究人员对于RPA机器人在财务管理流程优化领域中的应用研究层出不穷,但现有研究成果主要集中在财务业务单一领域,缺乏财务管理整体流程一体化管控的研究。RPA机器人的功能绝非单一的财务业务处理,无论从自身技术发展,或…...
聚焦老年生活与健康,“老有所依·情暖夕阳”元岗街社区微型养老博览会顺利开展
尊老敬老是中华民族的传统美德, 爱老助老是全社会的共同责任。 家有一老,如有一宝, 长者的生活情况是一个家庭的头等大事, 做好长者服务是街道和社区的重要工作。 2024年1月6日,由元岗街道党工委、元岗街道办事处、…...
记录汇川:H5U与Factory IO测试12
主程序: 子程序: IO映射 子程序: 辅助出料 子程序: 自动程序 Factory IO配置: 实际动作如下: Factory IO测试12...
PingCAP 受邀参加 FICC 2023,获 Open100 世纪全球开源贡献奖
2023 年 12 月,2023 国际测试委员会智能计算与芯片联邦大会(FICC 2023)在海南三亚举办,中外院士和数十位领域专家莅临出席。 大会现场 ,开放源代码促进会创始人 Bruce Perens 颁发了 Open100 世纪全球开源贡献奖&…...
10-skywalking告警
https://github.com/apache/skywalking/blob/master/docs/en/setup/backend/backend-alarm.md 5.1:告警指标 ~$ vim /apps/apache-skywalking-apm-bin/config/oal/core.oal service_resp_time # 服务的响应时间 service_sla # 服务http请求成功率SLV,比…...
vue前端开发自学,插槽练习第二次,name属性的使用
vue前端开发自学,插槽练习第二次,name属性的使用!可以使用name属性,来自定义一个名字,这样,就可以在一个组件内同时出现多个插槽的内容了。在子组件内接收的时候,很简答,只需要在slot标签里面加上name“mz”࿱…...
AI副业拆解:人像卡通化,赋予你的形象全新生命力
大家好我是在看,记录普通人学习探索AI之路。 🔥让你的形象瞬间穿越二次元!🚀人像卡通化,捕捉你的独特魅力,让真实与梦幻在此刻交融。🎨 今天为大家介绍如何免费把人像卡通化--漫画风 https://w…...
宝塔面板安装MySQL8数据库
第一步:搜索mysql 第二步: 点击安装 我这里选择安装8版本 第三步:给宝塔配置mysql防火墙 第四步:修改数据库密码 第五步:想要使用navicat连接 需要修改root的权限 (1)使用secureCRT先登录mysql (2) 输入u…...
中科星图——Landsat9_C2_SR大气校正后的地表反射率数据
数据名称: Landsat9_C2_SR 数据来源: USGS 时空范围: 2022年1月-2023年3月 空间范围: 全国 数据简介: Landsat9_C2_SR数据集是经大气校正后的地表反射率数据,属于Collection2的二级数据产品&#…...
使用ros_arduino_bridge控制机器人底盘
使用ros_arduino_bridge控制机器人底盘 搭建了ROS分布式环境后,将ros_arduino_bridge功能包上传至Jetson nano,就可以在PC端通过键盘控制小车的运动了。实现流程如下: 系统准备;下载程序;程序修改;分别启动PC与Jetson…...
为什么需要建设工程项目管理?工程项目管理有哪些亮点功能?
在建筑行业,项目管理的重要性不言而喻。随着工程规模的扩大、技术复杂度的提升,传统的管理模式已经难以满足现代工程的需求。过去,许多企业依赖手工记录、口头沟通和分散的信息管理,导致效率低下、成本失控、风险频发。例如&#…...
ESP32 I2S音频总线学习笔记(四): INMP441采集音频并实时播放
简介 前面两期文章我们介绍了I2S的读取和写入,一个是通过INMP441麦克风模块采集音频,一个是通过PCM5102A模块播放音频,那如果我们将两者结合起来,将麦克风采集到的音频通过PCM5102A播放,是不是就可以做一个扩音器了呢…...
网络编程(UDP编程)
思维导图 UDP基础编程(单播) 1.流程图 服务器:短信的接收方 创建套接字 (socket)-----------------------------------------》有手机指定网络信息-----------------------------------------------》有号码绑定套接字 (bind)--------------…...
智能AI电话机器人系统的识别能力现状与发展水平
一、引言 随着人工智能技术的飞速发展,AI电话机器人系统已经从简单的自动应答工具演变为具备复杂交互能力的智能助手。这类系统结合了语音识别、自然语言处理、情感计算和机器学习等多项前沿技术,在客户服务、营销推广、信息查询等领域发挥着越来越重要…...
「全栈技术解析」推客小程序系统开发:从架构设计到裂变增长的完整解决方案
在移动互联网营销竞争白热化的当下,推客小程序系统凭借其裂变传播、精准营销等特性,成为企业抢占市场的利器。本文将深度解析推客小程序系统开发的核心技术与实现路径,助力开发者打造具有市场竞争力的营销工具。 一、系统核心功能架构&…...
在 Visual Studio Code 中使用驭码 CodeRider 提升开发效率:以冒泡排序为例
目录 前言1 插件安装与配置1.1 安装驭码 CodeRider1.2 初始配置建议 2 示例代码:冒泡排序3 驭码 CodeRider 功能详解3.1 功能概览3.2 代码解释功能3.3 自动注释生成3.4 逻辑修改功能3.5 单元测试自动生成3.6 代码优化建议 4 驭码的实际应用建议5 常见问题与解决建议…...
Spring AOP代理对象生成原理
代理对象生成的关键类是【AnnotationAwareAspectJAutoProxyCreator】,这个类继承了【BeanPostProcessor】是一个后置处理器 在bean对象生命周期中初始化时执行【org.springframework.beans.factory.config.BeanPostProcessor#postProcessAfterInitialization】方法时…...
aardio 自动识别验证码输入
技术尝试 上周在发学习日志时有网友提议“在网页上识别验证码”,于是尝试整合图像识别与网页自动化技术,完成了这套模拟登录流程。核心思路是:截图验证码→OCR识别→自动填充表单→提交并验证结果。 代码在这里 import soImage; import we…...
leetcode_69.x的平方根
题目如下 : 看到题 ,我们最原始的想法就是暴力解决: for(long long i 0;i<INT_MAX;i){if(i*ix){return i;}else if((i*i>x)&&((i-1)*(i-1)<x)){return i-1;}}我们直接开始遍历,我们是整数的平方根,所以我们分两…...
【Java】Ajax 技术详解
文章目录 1. Filter 过滤器1.1 Filter 概述1.2 Filter 快速入门开发步骤:1.3 Filter 执行流程1.4 Filter 拦截路径配置1.5 过滤器链2. Listener 监听器2.1 Listener 概述2.2 ServletContextListener3. Ajax 技术3.1 Ajax 概述3.2 Ajax 快速入门服务端实现:客户端实现:4. Axi…...
