【C++】stack/queue/优先级队列的模拟实现

news2024/12/28 21:01:06

目录

  • 1. stack/queue
    • 1.1 模拟实现
  • 2. 优先级队列
    • 2.1 模拟实现
    • 2.2 仿函数

1. stack/queue

stack文档说明

queue文档说明

stack和queue被称为容器适配器。

容器适配器是什么?
它是一种特殊的容器类型,通过封装已有的容器类型来提供特定功能的接口函数,以满足不同的需求。

而stack和queue底层封装的是deque容器。

deque(双端队列)容器不仅支持头部和尾部数据的插入和删除,也支持随机存取、访问和修改(迭代器),但是每个方面的功能又不是特别突出,尤其是随机存取和中间位置插入删除数据,效率比较低,因此很少单独使用deque,而且将其作为某个容器的适配器。

stack和queue的结构特性都是只能在两端进行数据的操作,无需随机存取和中间位置插入删除,因此用deque来适配就非常合适。

1.1 模拟实现

#include <iostream>
#include <deque>
using namespace std;
namespace lzh {
	//stack
	//第二个模板参数默认使用deque
	template<class T, class Container = deque<T>>
	class stack {
	public:
		void push(const T& x) {
			_con.push_back(x);
		}

		void pop() {
			_con.pop_back();
		}

		T& top() {
			return _con.back();
		}

		const T& top() const {
			return _con.back();
		}

		size_t size()const {
			return _con.size();
		}

		bool empty()const {
			return _con.empty();
		}
	private:
		Container _con;
	};

	//queue
	template<class T, class Container = deque<T>>
	class queue {
	public:
		void push(const T& x) {
			_con.push_back(x);
		}

		void pop() {
			_con.pop_front();
		}

		T& front() {
			return _con.front();
		}

		T& back() {
			return _con.back();
		}

		const T& front() const {
			return _con.front();
		}
		const T& back()	const {
			return _con.back();
		}

		size_t size()const {
			return _con.size();
		}

		bool empty()const {
			return _con.empty();
		}
	private:
		Container _con;
	};
}

2. 优先级队列

priority_queue文档说明

priority_queue是一种特殊的队列,它可以根据元素的优先级(或者大小)自动进行排序,并且优先级最高的先出队

本质是二叉堆结构

在这里插入图片描述
它也是一个容器适配器,底层封装了vector容器,并且元素的优先级是通过第三个模板参数仿函数来控制的

在这里插入图片描述默认是大堆 - less<T>,若想实现小堆则要显式传递三个参数封装的容器类型和仿函数:

在这里插入图片描述

2.1 模拟实现

#pragma once
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
namespace lzh {
	template<class T, class Container = vector<T>>
	class priority_queue {
		void adjustDown(int fatherIdx) {
			int childIdx = fatherIdx * 2 + 1;
			while (childIdx < (int)_con.size()) {
				if (childIdx + 1 < (int)_con.size() && _con[childIdx + 1] > _con[childIdx]) {
					++childIdx;
				}
				if (_con[childIdx] > _con[fatherIdx]) {
					swap(_con[childIdx], _con[fatherIdx]);
					fatherIdx = childIdx;
					childIdx = fatherIdx * 2 + 1;
				}
				else {
					break;
				}
			}
		}

		void adjustUp(int childIdx) {
			int fatherIdx = (childIdx - 1) / 2;
			while (childIdx > 0 && _con[childIdx] > _con[fatherIdx]) {
				swap(_con[childIdx], _con[fatherIdx]);
				childIdx = fatherIdx;
				fatherIdx = (childIdx - 1) / 2;
			}
		}
	public:
		priority_queue() { }
		template <class InputIterator>
		priority_queue(InputIterator first, InputIterator last) {
			while (first != last) {
				_con.push_back(*first);
				++first;
			}
			for (int i = (_con.size() - 2) / 2; i >= 0; --i) {
				adjustDown(i);
			}
		}

		bool empty() const {
			return _con.empty();
		}

		size_t size() const {
			return _con.size();
		}

		// 堆顶元素不允许修改,因为:堆顶元素修改可以会破坏堆的特性
		const T& top() const {
			return _con.front();
		}

		void push(const T& x) {
			_con.push_back(x);
			adjustUp(_con.size() - 1);
		}

		void pop() {
			swap(_con.front(), _con.back());
			_con.pop_back();
			adjustDown(0);
		}
	private:
		Container _con;
	};
}

2.2 仿函数

默认实现的是大堆,因为比较那里写死了,若要实现小堆结构则需要修改向下和向上调整算法的比较逻辑,但实际上库里实现的优先级队列是没办法修改的,所以真正的做法是使用仿函数作为第三个模板参数传递给容器,来实现大小堆的切换。

仿函数本质是一个类类型,类中重载了()运算符:

//仿函数且类模板
template<class T>
class Less {
public:
	//若为自定义类型则该类中实现了小于运算符重载
	bool operator()(const T& x, const T& y) {
		return x < y;
	}
};

void test_less() {
	//函数对象
	Less lessFunc;
	cout << lessFunc(1, 2) << endl;
	//显式调用
	//cout << lessFunc.operator()(1, 2) << endl;
}

可以发现该对象可以像函数一样来使用。

类似于C语言中的函数指针

用仿函数来灵活调整调整算法的逻辑,控制是大堆还是小堆:

using namespace std;

template<class T>
class Less {
public:
	//若为自定义类型则该类中实现了小于运算符重载
	bool operator()(const T& x, const T& y) {
		return x < y;
	}
};

template<class T>
class Greater {
public:
	bool operator()(const T& x, const T& y) {
		return x > y;
	}
};

namespace lzh {
	template<class T, class Container = vector<T>, class Compare = less<T>>
	class priority_queue {
		void adjustDown(int fatherIdx) {
			//定义仿函数对象
			Compare cmp;
			int childIdx = fatherIdx * 2 + 1;
			while (childIdx < (int)_con.size()) {
				//通过函数对象进行比较
				if (childIdx + 1 < (int)_con.size() &&
				 cmp(_con[childIdx], _con[childIdx + 1])) {
					++childIdx;
				}
				//通过函数对象进行比较
				if (cmp(_con[fatherIdx], _con[childIdx])) {
					swap(_con[fatherIdx], _con[childIdx]);
					fatherIdx = childIdx;
					childIdx = fatherIdx * 2 + 1;
				}
				else {
					break;
				}
			}
		}

		void adjustUp(int childIdx) {
			//定义仿函数对象
			Compare cmp;
			int fatherIdx = (childIdx - 1) / 2;
			while (childIdx > 0 && cmp(_con[fatherIdx], _con[childIdx])) {
				swap(_con[fatherIdx], _con[childIdx]);
				childIdx = fatherIdx;
				fatherIdx = (childIdx - 1) / 2;
			}
		}
		//...其他成员
	}
}
int main() {
	//第三个模板参数
	//Less默认是建大堆
	//而Greater是建小堆
	//和库里的实现一致
	//第二和第三个模板参数有缺省值
	//若要大堆,可以只传第一个元素类型
	//而要小堆则三个都需要传递
	lzh::priority_queue<int, vector<int>, Less<int>> pq;
	pq.push(3);
	pq.push(5);
	pq.push(1);
	pq.push(4);

	while (!pq.empty()) {
		cout << pq.top() << ' ';
		pq.pop();
	}
	cout << endl;
	return 0;
}

通过传递不同的仿函数类型,便可以控制是建大堆还是小堆。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

探索1688 API的无限商机与应用

为了更好地满足用户需求&#xff0c;1688.com开放了丰富强大的API接口&#xff0c;使得开发者可以便捷地与平台进行集成&#xff0c;实现自动化的商务操作。 产品查询与筛选&#xff1a;通过调用1688 API&#xff0c;你可以根据自定义条件进行商品查询和筛选&#xff0c;获取符…

JAVA三种拦截方式

最近面试有遇到拦截方式的场景&#xff0c;结合网上xdm的代码整理了下&#xff0c;分为以下三种&#xff1a; java原生过滤器Filter、springMVC拦截器、aop切面 目录&#xff1a; 一、java原生过滤器Filter二、springMVC拦截器三、aop切面 一、java原生过滤器Filter package c…

C++初阶语法——new,delete开辟/销毁动态内存空间

前言&#xff1a;在C语言中&#xff0c;有malloc&#xff0c;realloc&#xff0c;calloc开辟动态内存空间&#xff0c;free销毁动态内存空间。而在C中&#xff0c;使用new开辟动态内存空间&#xff0c;delete销毁动态内存空间。不仅简化了操作&#xff0c;更为重要的是&#xf…

springcloud3 hystrix实现服务监控显示3(了解)

一 hystrix的服务监控调用 1.1 hystrix的服务监控调用 hystrix提供了准实时的监控调用&#xff08;hystrix dashbord&#xff09;&#xff0c;Hystrix 会持续的记录所有通过hystrix发送的请求的执行信息&#xff0c;并以统计报表和图形的形式展示给用户&#xff0c;包括每秒执…

在海外如何进行应用商店的关键词优化

分析市场&#xff0c;了解我们的应用类别&#xff0c;将我们的应用与竞争对手的优点和缺点进行比较&#xff0c;找到市场上的空白或所谓未满足的需求&#xff0c;并思考如何填补。 1、应用商店关键词优化。 关键词优化的目的是找到最相关的关键词 &#xff0c;并测试应用元数据…

菜鸟Vue教程 - 实现带国际化的注册登陆页面

初接触vue的时候觉得vue好难&#xff0c;因为项目中要用到&#xff0c;就硬着头皮上&#xff0c;慢慢的发现也不难&#xff0c;无外乎画个布局&#xff0c;然后通过样式调整界面。在通过属性和方法跟js交互。js就和我们写的java代码差不多了&#xff0c;复杂一点的就是引用这种…

PHP8的正则表达式-PHP8知识详解

在网页程序的时候&#xff0c;经常会有查找符合某些复杂规则的字符串的需求。正则表达式就是描述这些规则的工具。 正则表达式是把文本或者字符串按照一定的规范或模型表示的方法&#xff0c;经常用于文本的匹配操作。 例如&#xff1a;我们在填写手机号码的时候&#xff0c;…

LinkedBlockingQueue详解,深入探究LinkedBlockingQueue源码

目录 1、LinkedBlokingQueue是一个有界队列 2、LinkedBlokingQueue是一个单向队列 3、LinkedBlokingQueue中的非阻塞方法 4、LinkedBlokingQueue中的阻塞方法 LinkedBlockingQueue是通过ReentrantLock实现的&#xff08;有界/无界&#xff09;阻塞队列&#xff0c;在线程池…

PHP8的字符串操作3-PHP8知识详解

今天继续分享字符串的操作&#xff0c;前面说到了字符串的去除空格和特殊字符&#xff0c;获取字符串的长度&#xff0c;截取字符串、检索字符串。 今天继续分享字符串的其他操作。如&#xff1a;替换字符串、分割和合成字符串。 5、替换字符串 替换字符串就是对指定字符串中…

SUMO traci接口控制电动车前往充电站充电

首先需要创建带有停车位的充电站(停车场和充电站二合一)&#xff0c;具体参考我的专栏中其他文章。如果在仿真的某个时刻&#xff0c;希望能够控制电动车前往指定的充电站充电&#xff0c;并且在完成充电后继续前往车辆原来的目的地&#xff0c;那么可以使用以下API&#xff1a…

STM32 F103C8T6学习笔记8:0.96寸单色OLED显示屏显示字符

使用STM32F103 C8T6 驱动0.96寸单色OLED显示屏: OLED显示屏的驱动&#xff0c;在设计开发中OLED显示屏十分常见&#xff0c;因此今日学习一下。一篇文章从程序到显示都讲通。 文章提供源码、原理解释、测试工程下载&#xff0c;测试效果图展示。 目录 OLED驱动原理—IIC通信…

【论文解读】Hybrid-SORT: Weak Cues Matter for Online Multi-Object Tracking

因为Hybrid-SORT的baseline是基于OCSORT进行改进的&#xff0c;在这之前建议先了解byteTrack和【】的相关知识 1.介绍 1.1 基本框架 多目标跟踪(MOT)将问题分为两个子任务。第一个任务是检测每个帧中的对象。第二个任务是将它们在不同的框架中联系起来。关联任务主要通过显式…

搜狗拼音暂用了VSCode及微信小程序开发者工具快捷键Ctrl + Shit + K 搜狗拼音截图快捷键

修改搜狗拼音的快捷键 右键--更多设置--属性设置--按键--系统功能快捷键--系统功能快捷键设置--取消Ctrl Shit K的勾选--勾选截屏并设置为Ctrl Shit A 微信开发者工具设置快捷键 右键--Command Palette--删除行 微信开发者工具快捷键 删除行&#xff1a;Ctrl Shit K 或…

n5173b是德科技keysight N5173B信号发生器

产品概述 是德科技/安捷伦N5173B EXG模拟信号发生器 当您需要平衡预算和性能时&#xff0c;是德科技N5173B EXG微波模拟信号发生器是经济高效的选择。它提供解决宽带滤波器、放大器、接收机等参数测试的基本信号。执行基本LO上变频或CW阻塞&#xff0c;低成本覆盖13、20、31.…

FPGA应用学习笔记----I2S和总结

时序一致在慢时序方便得多 增加了时序分布和分析的复杂性 使用fifo会开销大量资源

SqlServer的with(nolock)关键字的用法介绍

举个例子 下面就来演示这个情况。 为了演示两个事务死锁的情况&#xff0c;我们下面的测试都需要在SQL Server Management Studio中打开两个查询窗口。保证事务不被干扰。 --1、 没有提交的事务&#xff0c;NOLOCK 和 READPAST处理的策略&#xff1a; --查询窗口一请执行如下…

python中可以处理word文档的模块:docx模块

前言 大家早好、午好、晚好吖 ❤ ~欢迎光临本文章 话不多说&#xff0c;直接开搞&#xff0c;如果有什么疑惑/资料需要的可以点击文章末尾名片领取源码 一.docx模块 Python可以利用python-docx模块处理word文档&#xff0c;处理方式是面向对象的。 也就是说python-docx模块…

设备文件和设备绑定

实验目的&#xff1a;使用函数让设备文件和设备绑定&#xff0c;完成对LED的简单控制 在test.c中完成硬件逻辑控制 test.c #include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #inclu…

openGauss学习笔记-44 openGauss 高级数据管理-存储过程

文章目录 openGauss学习笔记-44 openGauss 高级数据管理-存储过程44.1 语法格式44.2 参数说明44.3 示例 openGauss学习笔记-44 openGauss 高级数据管理-存储过程 存储过程是能够完成特定功能的SQL语句集。用户可以进行反复调用&#xff0c;从而减少SQL语句的重复编写数量&…

eUICC 识别号 (EIN)

GSMA 是业界指定的一级 EID&#xff08;eUICC 标识符&#xff09;分配机构&#xff0c;负责协调 eUICC 标识号的发行和使用。每个 eSIM 都需要具有唯一、持久且安全的 EID&#xff0c;以识别嵌入式或可移动 eUICC&#xff0c;如SGP.29中所定义。 GSMA eUICC 身份方案为每个 eUI…