双端队列和优先级队列
文章目录
- 前言
- deque
- deque底层设计
- 迭代器设计
- priority
- 仿函数
- 数组中的第k个最大元素
- 优先级队列模拟实现
- push
- pop
- 调整
- 仿函数
- 存储自定义类型
前言
今天要介绍比较特殊的结构,双端队列。
还有一个适配器,优先级队列。
deque
栈的默认容器用了一个deque的东西,这个东西是啥?
deque虽然是队列,但它不是正队列,它是双端队列。
为什么叫双端队列,因为它最适合头插头删,尾插尾删。
deque对标的是vector+list.
它的结构和前面没有什么差别。
这些功能只有我们之前讲的list能做到。
最牛逼的是还有这个
看,它既支持vector的功能又支持list的功能。
但是这个东西真的这么牛吗?
要回答这个问题,我们得看一下deque的底层设计。
deque底层设计
我们先来分析一下顺序表和链表的区别。
顺序表:
它最大的优点就是空间连续随机访问,但是也带来了,头插,中间插入删除的困难。
其实顺序表还有一个优点就是高速缓存效率高,但是这里学习的时候不作为重点。
链表:
能不能把list和vector的优点合起来呢?
有人用了一个折中的思路。
折中的思路,一次开一个的小数组,满了以后在开一段空间,不扩容。
怎样管理多端小数组?中控(指针数组)
假设我要头插或者尾插
大家看这个结构什么时候需要扩容?
中控满了就需要扩容。但是扩容的代价低。因为中控扩容中只拷贝指针。
假设我要访问第25个数据,怎么访问?
小数组要不要扩容,这是很灵活的,得你自己去看
为什么它的扩容代价低?
相比vector而言,只需要拷贝指针,代价低了很多。
但相比list而言,还是比不过list,list没有扩容的概念,增加一个数据它就开一次空间。
注意,deque的随机访问比vector低,但是比list还是高很多。
deque适合干嘛?
头插头删多,尾插尾删多可以用。
大家可以去测试一下deque和vector和list sort的效率
其实效率都不如拷贝到vector,进行排序然后拷贝回去。
迭代器设计
它的迭代器设计是我们目前为止接触到最复杂的迭代器。
它有四个指针,看它们分别干嘛的?
cur:当前指向数据的位置
first和last:buff数组开始和结束
node:反向指向中控数组
好像也没有那么难理解。
这里就不模拟实现了,deque在现实生活中用的其实不多,除了用来做头插头删,做deque的默认容器以外,
实际当中用的还是vector和list.
如果对deque有兴趣可以去看一下源码,但其实没必要,有些东西我们需要深入学习,但deque我们只需要
了解它的框架就可以了。
priority
这个东西还是挺适用的,很多地方都要用。
默认容器是vector,当然你也可以传deque,但是这里deque不如vector.
它有个要求,就是随机访问的迭代器。
它是优先级队列,看一下接口就知道了。
它的接口跟我们的堆很想。
它这里需不需要符合先进先出?
不需要,它是要求优先级高的先出。
它也不支持遍历,容器适配器都不支持遍历。
还是出一个走一个。
void test_priority_queue()
{
//默认是大堆,大的优先级高priority_queue<int> pq;pq.push(1);pq.push(0);pq.push(5);pq.push(2);pq.push(1);pq.push(7);while (!pq.empty()){cout << pq.top() << " ";pq.pop();}cout << endl;
}
它默认认为什么的优先级高?
大的优先级高。
它的底层是一个vector,vector的底层实际是一个堆,也就是一个完全二叉树。
默认是大堆。
那我现在有一个问题,以后要用topk的时候,还需要自己写一个堆出来吗?
不需要。
如果要变成小堆,怎么办?
用了一个仿函数。
这里用了一个less,默认是大堆。
// 小堆 -- 小的优先级高
priority_queue<int, vector<int>, greater<int>> pq;
包一下这个文件
#include <functional>
仿函数
假设我需要写一个大于或小于的仿函数,怎么写呢?
其实很简单, 仿函数都有一个特点,重载一个()的运算符。
这个()就是函数参数的括号的运算符。
如果但看这一行,你会觉得lessFunc是什么?
cout << lessFunc(1, 2) << endl;
函数名或者函数指针,但它其实是一个对象。
这个类叫做仿函数。表示它的对象可以像函数一样去使用。但它的本质是调用operator(), 是运算符重载。
仿函数有什么好处?
它的好处是能更好的去做类型。模拟实现优先级队列的时候能更好理解。
它的对象可以像函数一样调用,我们就可以控制很多行为。
C++搞出这个东西是由于函数指针太复杂了。具体是函数指针的类型太复杂了。有些地方是要去做回调。
C的qsort
C++的sort
把上面的仿函数更改为针对任意类型。
#include <functional>
template<class T>
struct Less
{bool operator()(const T& x, const T& y){return x < y;}
};int main()
{Less<int> lessFunc;cout << lessFunc(1, 2) << endl;return 0;
}
下面我们用优先级队列来做一个题目
数组中的第k个最大元素
数组中的第k个最大元素
返回第k个最大元素,好不好找?
排序就搞定了。
但是时间复杂度不满足
这道题最好的方式肯定还是快排的思想,我们这里不考虑这个,我们把堆用起来。
如果这道题优先级队列来解决,怎么解决?
pop k次,堆顶的元素就是我们要找的元素。
这样写,时间复杂度是多少?
第四行的时间复杂度是多少?N
我们知道向下建堆可以O(N).
pop的时间复杂是多少?
K*logN
所以合计的时间复杂度。
O(KlogN+N)
这里如果k==N,那就麻烦了,时间复杂度是NlogN
假设N远大于k,且N很大,有什么优化方式?
我们要找最大的前k个,除了全部建大堆,还有没有其他方式?
我们还可以建k个元素的堆。
那我们建大堆还是小堆?
Top k最大的前k个,我们是要建小堆。
看代码
这样解决时间复杂度是多少?
它最大的特点是消耗空间很小。
我们学会了优先级队列,根本不需要去手搓一个堆
优先级队列模拟实现
首先,底层要存数据还是用vector这样的东西来存.
接着我们在把要写的接口给写出来,这跟栈还是很像的。
push
想一想堆是怎么push数据的?
堆的底层是一个数组的结构。我们插入数据的时候向上调整。
pop
堆的数据删除怎么删?
堆的删除直接删,不然堆就挪乱了。效率极低。
堆删除数据是删除第一个数据,将第一个数据和最后一个数据交换,然后删除最后一个数据,然后从根开始向下调整。
调整
怎呢向上调整?
跟父结点比,挨着挨着往上走就可以了。
先搞大堆。
template<class T, class Container = vector<T>>class priority_queue{public:void adjust_up(int child){Comapre com;int parent = (child - 1) / 2;while (child > 0){if (_con[parent] < _con[child]){swap(_con[child], _con[parent]);child = parent;parent = (child - 1) / 2;}else{break;}}}void adjust_down(int parent){size_t child = parent * 2 + 1;while (child < _con.size()){Comapre com;if (child + 1 < _con.size() && _con[child] < _con[child + 1]){++child;}if (_con[parent] < _con[child]){swap(_con[child], _con[parent]);parent = child;child = parent * 2 + 1;}else{break;}}}void push(const T& x){_con.push_back(x);adjust_up(_con.size()-1);}void pop(){swap(_con[0], _con[_con.size() - 1]);_con.pop_back();adjust_down(0);}size_t size(){return _con.size();}bool empty(){return _con.empty();}private:Container _con;};
快速写了一个优先级队列,测试一下,自己写的代码。
它的适配器默认是vector,这里用deque,但是它照样能跑。
也就是说这里你只需要符合它的需求,满足上面的接口,这个程序都可以正常运行。
它们的底层已经天差地别了,一个是vector,一个是双端队列,但是对这个上层的优先级队列完全没有影响。这就是容器适配器,只要你那个东西满足我的需求,我就可以转换成我想要的东西。
仿函数
上面写的是大堆的代码,那我要变成小堆呢?
要变成小堆,就要控制上面调整的比较方式。
总不能纯手搓吧。我们肯定是先把大堆写出来,加东西,然后顺便改一下就变成小堆了。
那这一定是外部可以传一个东西去控制,它这里是通过模板参数去控制的。
怎么通过模板参数来控制呢?
先写两个可以比较的仿函数。
那就可以用这个类型的对象去比较。
//跟库里面保持一致,大堆用一个小于。
template<class T, class Container = vector<T>, class Compare = less<T>>
神奇的事情发生了。
大堆和小堆就可以随意更换了。
void adjust_up(int child)
{Comapre com;int parent = (child - 1) / 2;while (child > 0){//if (_con[parent] < _con[child])if (com(_con[parent], _con[child]))//if (Comapre()(_con[parent], _con[child])){swap(_con[child], _con[parent]);child = parent;parent = (child - 1) / 2;}else{break;}}
}void adjust_down(int parent)
{size_t child = parent * 2 + 1;while (child < _con.size()){Comapre com;//if (child + 1 < _con.size() // && _con[child] < _con[child + 1])if (child + 1 < _con.size() && com(_con[child],_con[child + 1])){++child;}//if (_con[parent] < _con[child])if (com(_con[parent], _con[child])){swap(_con[child], _con[parent]);parent = child;child = parent * 2 + 1;}else{break;}}
}
template<class T, class Container = vector<T>, class Compare =less<T>>
以前用的泛型是控制数据类型,不知道具体的数据类型是什么,你传什么就是什么。
现在控制的不是类型,控制的是比较方式。默认它是less,那com就是less对象,less对象去调用operator(),那它就是小于比较方式。
它能够通过模板参数传类型,类型里面实例化对象控制比较的方式。
跟函数指针的比较
模板传的是类型,仿函数是一个类。类型就可以在模板参数传,模板参数传有什么好处?
我整个类都可以用。
仿函数还有其他功能,我们在后面会C++的学习中会慢慢感受到
存储自定义类型
假设我们优先级数列存储的数据不再是内置类型。自定义类型,能不能用优先级队列。
也可以。
大堆:
小堆:
日期类需要重载大于,小于才行。
如果没有重载就需要写仿函数才能搞定。
给大家看一下更牛逼的东西。
结果每次都不一样
怎么回事呢?
现在存进去的T是Date*,不是Date.
Date* 可以进行比较,但这不是我想要的。
Date* 是new出来的,new是堆上申请的空间,那个地址可能先大先小,完全没有规律。
template<class T, class Container = vector<T>, class Compare =less<T>>
如果我非要这样比较呢?
我们的骚操作又要上场了。
默认是大堆。
再怎么运行他都不会变。
小堆:
再写一个仿函数
这是不是给了一个非常大的空间
它真正的优势在于,我就是个compare对象,有个默认比较方式,但是你想要其他的比较方式,全部都可以交给你控制。你想怎么比就怎么比。
相关文章:

双端队列和优先级队列
文章目录 前言dequedeque底层设计迭代器设计 priority仿函数数组中的第k个最大元素优先级队列模拟实现pushpop调整仿函数存储自定义类型 前言 今天要介绍比较特殊的结构,双端队列。 还有一个适配器,优先级队列。 deque 栈的默认容器用了一个deque的东西…...
c#读取CSV文件跟Excel导入成DataTble
1.读取CSV文件 /// <summary>/// 读取CSV文件/// </summary>/// <param name"fileName">文件路径</param>public static DataTable ReadCSV(string fileName){DataTable dt new DataTable();FileStream fs new FileStream(fileName, FileM…...
Python编程技巧 – 单字符函数
Python编程技巧 – 单字符函数 Python Programming Skills – Single Character Function By JacksonML 0. 前言 Python有其内建(built-in)的一系列函数,其中,有两个函数为长度为一的字符设计。这样的函数是单字符函数,尽管它们操作的对象…...
xcode-文件
IOSDeviceSupoprt 共享缓存库 当你使用新的 iOS 设备连接到 Xcode 时,Xcode 会自动下载并存储相应版本的设备支持文件。 每个 iOS 版本都有一个对应的设备支持文件集,这些文件包含有关设备架构和操作系统的信息,以便 Xcode 能够正确地调试和…...

云原生之深入解析网络服务Istio、eBPF和RSocket Broker
一、服务治理 ① “服务治理”简介 在微服务时代,一个复杂的应用程序被分解为多个组件化、协作和连接的单元,服务往往会承担越来越多的业务责任,这使得服务治理的难度前所未有,仅仅依靠微服务框架级的治理是不够的,构…...
文件系统和磁盘调度
文件系统 概述 文件系统:一种用于持久性存储的系统抽象 在存储器上:组织、控制、导航、访问和检索数据大多数计算机包含文件系统 文件:文件系统中一个单元的相关数据在操作系统中的抽象 文件系统功能 分配文件磁盘空间 管理文件块管理空…...

C++ stringOJ练习题
目录 把字符串转换成整数 反转字符串 字符串中的第一个唯一字符 字符串最后一个单词的长度 找出字符串中第一个只出现一次的字符 字符串相加 字符串最后一个单词长度 字符串相乘 反转字符串3 反转字符串2 验证回文串 把字符串转换成整数 通过遍历字符串并逐位转换…...

解决问题:ImportError: cannot import name ‘_update_worker_pids‘
在复现一些较早年份文献时,网络架构是较早的Pytorch模型,现阶段的高版本不兼容,所以就得安装比如低版本的torch0.4.0以解决问题。 目录 一、问题1.1 问题分析 二、解决办法2.1 Pytorch安装2.2 torchvision安装2.3 测试是否安装成功 三、总结…...

【面试总结】Java面试题目总结(一)
(以下仅为个人见解,如果有误,欢迎大家批评并指出错误,谢谢大家) 1.项目中的验证码功能是如何实现的? 第一步:在项目的pom.xml文件中导入 EasyCaptcha 的依赖; <dependency>…...

大白话数据中台,何为数据中台
文章目录 一、数据中台二、本质三、构建数据中台的几个方面四、总结 最近一直在研发Ai平台,忙碌非凡。 在之余,有小伙伴质疑数据中台其实就是一个web系统,无法就是添加一些业务逻辑的增删改查。 答曰: 回去好好把科普下什么是数…...

escapeshellarg参数绕过和注入的问题
escapeshellcmd escapeshellcmd(string $command): string command--要转义的命令。 escapeshellcmd() 对字符串中可能会欺骗 shell 命令执行任意命令的字符进行转义。 此函数保证用户输入的数据在传送到 exec() 或 system() 函数,或者 执行操作符 之前进行转义。 …...

CSS——标准流、浮动、Flex布局
1、标准流 标准流也叫文档流,指的是标签在页面中默认的排布规则,例如:块元素独占一行,行内元素可以一行显示多个。 2、浮动 作用:让块元素水平排列 属性名:float 属性值: left:…...

P21 类神经网络训练不起来怎么办- 自动调整学习率 Adapative learning rate
梯度大,学习率减小梯度小,学习率变大adam随时间变化 , decay / warm up 调整学习率方法一 adagrad 学习率除以 梯度的方差 方法二 RMSProp 目前最常用的: Adam: RMSProp Moment Learning rate schedule : decay/ warm up l…...

[Linformer]论文实现:Linformer: Self-Attention with Linear Complexity
文章目录 一、完整代码二、论文解读2.1 介绍2.2 Self-Attention is Low Rank2.3 模型架构2.4 结果 三、整体总结 论文:Linformer: Self-Attention with Linear Complexity 作者:Sinong Wang, Belinda Z. Li, Madian Khabsa, Han Fang, Hao Ma 时间&#…...

【Jeecg Boot 3 - 第二天】1.1、后端 docker-compose 部署 JEECGBOOT3
一、场景 二、实战 ▶ 2.1 修改配置文件 > 目的一:将 dev 变更为生产环境 prod > 目的二:方便spring项目调用docker同个network下的redis和mysql ▶ 2.2 编写dockerfile ▶ 2.3 编写docker-compose.yaml ▶ 2.4 打…...
Centos单用户模式修改root密码
在CentOS 7的单用户模式下,你可以按照以下步骤修改root用户密码: 启动CentOS 7并进入GRUB菜单。在启动时按下任意键进入GRUB菜单。 在GRUB菜单中,选择要启动的CentOS 7内核版本,并按下e键进行编辑。 找到以 ro 开头的行…...

[Unity]关于Unity接入Appsflyer并且打点支付
首先需要去官方下载Appsflyer的UnityPackage 链接在这afPackage 然后导入 导入完成 引入此段代码 using AppsFlyerSDK; using System.Collections; using System.Collections.Generic; using UnityEngine;public class AppflysManager : MonoBehaviour {public static App…...

AICore 带来了 Android 专属的 AI 能力,它要解决什么?采用什么架构思路?
前言 Google 最近发布的 Gemini 模型在全球引起了巨大反响,其在多模态领域的 Video demo 无比震撼。对于 Android 开发者而言,其中最振奋人心的消息莫过于 Gemini Nano 模型将内置到 Android 系统当中,并开放给开发者使用。 事实上…...

python学习1
大家好,这里是七七,今天开始又新开一个专栏,Python学习。这次思考了些许,准备用例子来学习,而不是只通过一大堆道理和书本来学习了。啊对,这次是从0开始学习,因此大佬不用看本文了,小…...
【SpringBoot】Spring Boot 单体应用升级 Spring Cloud 微服务
Spring Cloud 是在 Spring Boot 之上构建的一套微服务生态体系,包括服务发现、配置中心、限流降级、分布式事务、异步消息等,因此通过增加依赖、注解等简单的四步即可完成 Spring Boot 应用到 Spring Cloud 升级。 Spring Boot 应用升级为 Spring Cloud…...

手游刚开服就被攻击怎么办?如何防御DDoS?
开服初期是手游最脆弱的阶段,极易成为DDoS攻击的目标。一旦遭遇攻击,可能导致服务器瘫痪、玩家流失,甚至造成巨大经济损失。本文为开发者提供一套简洁有效的应急与防御方案,帮助快速应对并构建长期防护体系。 一、遭遇攻击的紧急应…...
synchronized 学习
学习源: https://www.bilibili.com/video/BV1aJ411V763?spm_id_from333.788.videopod.episodes&vd_source32e1c41a9370911ab06d12fbc36c4ebc 1.应用场景 不超卖,也要考虑性能问题(场景) 2.常见面试问题: sync出…...

高危文件识别的常用算法:原理、应用与企业场景
高危文件识别的常用算法:原理、应用与企业场景 高危文件识别旨在检测可能导致安全威胁的文件,如包含恶意代码、敏感数据或欺诈内容的文档,在企业协同办公环境中(如Teams、Google Workspace)尤为重要。结合大模型技术&…...
sqlserver 根据指定字符 解析拼接字符串
DECLARE LotNo NVARCHAR(50)A,B,C DECLARE xml XML ( SELECT <x> REPLACE(LotNo, ,, </x><x>) </x> ) DECLARE ErrorCode NVARCHAR(50) -- 提取 XML 中的值 SELECT value x.value(., VARCHAR(MAX))…...
使用Matplotlib创建炫酷的3D散点图:数据可视化的新维度
文章目录 基础实现代码代码解析进阶技巧1. 自定义点的大小和颜色2. 添加图例和样式美化3. 真实数据应用示例实用技巧与注意事项完整示例(带样式)应用场景在数据科学和可视化领域,三维图形能为我们提供更丰富的数据洞察。本文将手把手教你如何使用Python的Matplotlib库创建引…...
蓝桥杯 冶炼金属
原题目链接 🔧 冶炼金属转换率推测题解 📜 原题描述 小蓝有一个神奇的炉子用于将普通金属 O O O 冶炼成为一种特殊金属 X X X。这个炉子有一个属性叫转换率 V V V,是一个正整数,表示每 V V V 个普通金属 O O O 可以冶炼出 …...
【Elasticsearch】Elasticsearch 在大数据生态圈的地位 实践经验
Elasticsearch 在大数据生态圈的地位 & 实践经验 1.Elasticsearch 的优势1.1 Elasticsearch 解决的核心问题1.1.1 传统方案的短板1.1.2 Elasticsearch 的解决方案 1.2 与大数据组件的对比优势1.3 关键优势技术支撑1.4 Elasticsearch 的竞品1.4.1 全文搜索领域1.4.2 日志分析…...

数学建模-滑翔伞伞翼面积的设计,运动状态计算和优化 !
我们考虑滑翔伞的伞翼面积设计问题以及运动状态描述。滑翔伞的性能主要取决于伞翼面积、气动特性以及飞行员的重量。我们的目标是建立数学模型来描述滑翔伞的运动状态,并优化伞翼面积的设计。 一、问题分析 滑翔伞在飞行过程中受到重力、升力和阻力的作用。升力和阻力与伞翼面…...

Linux部署私有文件管理系统MinIO
最近需要用到一个文件管理服务,但是又不想花钱,所以就想着自己搭建一个,刚好我们用的一个开源框架已经集成了MinIO,所以就选了这个 我这边对文件服务性能要求不是太高,单机版就可以 安装非常简单,几个命令就…...
MFE(微前端) Module Federation:Webpack.config.js文件中每个属性的含义解释
以Module Federation 插件详为例,Webpack.config.js它可能的配置和含义如下: 前言 Module Federation 的Webpack.config.js核心配置包括: name filename(定义应用标识) remotes(引用远程模块࿰…...