stack,queue的模拟实现以及优先级队列

news2024/11/17 16:32:57

这篇博客用来记录stack,queue的学习。

stack的模拟实现

stack的模拟实现比较简单,先上代码

#pragma once
#include<vector>
#include<list>
#include<deque>
#include<iostream>
using std::deque;
using namespace std;

namespace bit
{
    template<class T, class Con = deque<T>>
    class stack
    {
    public:
        stack();
        void push(const T& x)
        {
            _c.push_back(x);
        }
        void pop()
        {
            _c.pop_back();
        }
        T& top()
        {
            return _c.back(); //容器的尾在这里就是栈的栈顶
        }
        const T& top() const
        {
            return _c.back();
        }
        size_t size() const
        {
            return _c.size();
        }
        bool empty() const
        {
            return _c.empty();
        }
    private:
        Con _c;
    };
}

这里要提及的点是: 首先,看到模板类处传的默认参数是deque,<其实这里是容器适配器>
deque属于既包含vector的一些优点又有一些list的优点,但由于他样样通样样松导致他不被我们广泛使用,这里不对deque进行详细介绍。

queue的模拟实现

和stack的模拟实现类似,还是比较好写的,直接上代码:

#pragma once
#include<vector>
#include<list>
#include<deque>
#include<iostream>
using std::deque;
using namespace std;
namespace bit
{
    template<class T, class Con = deque<T>>
    class queue
    {
    public:
        queue();
        void push(const T& x)
        {
            _c.push_back(x);
        }
        void pop()
        {
            _c.pop_front();
        }
        T& back()
        {
            return _c.back();
        }
        const T& back() const
        {
            return _c.back();
        }
        T& front()
        {
            return _c.front();
        }
        const T& front() const
        {
            return _c.front();
        }
        size_t size() const
        {
            return _c.size();
        }
        bool empty() const
        {
            return _c.empty();
        }
    private:
        Con _c;
    };
};

queue和stack对比起来,不同的点在于queue有了队头和队尾的区别。出现front和back访问队头和队尾的元素,而栈上是top获取栈顶元素。 队列在pop处是有差别的,queue的pop是对队头front进行删除。

接下来,介绍的才是重点:

优先级队列

优先级队列,就是我们对队列进行排序。 排序的过程类似于前面学过的堆,我们先来看几个操作。

template <class T, class Container = vector<T>, class Compare = less<T> >
class priority_queue
{
public:
    priority_queue();
    bool empty() 
    {
        return c.empty();
    }
    size_t size()
    {
        return c.size();
    }
    const T& top() 
    {
        return c[0];   // 这里我们默认用的容器是vector,因为我们后面都有进行排序,所以
                       //c[0]就可以访问到第一个元素(最大或者最小根据所传类参数决定)
    }
 private:
    Container c;
    Compare comp;

上面代码有一个地方,可能会比较迷惑。 就是传参数后面有一个class Compare的东西。
这个东西有个名字,叫仿函数

仿函数是什么?

仿函数可以理解成对类对象进行了包装,使得类对象能够像函数一样去使用。 但本质是在类中进行了操作符的重载而实现一个类似于函数功能的作用。
那么,这里我们传入这个仿函数,在接下来插入或删除中就可以用上了!
了解了仿函数之后,我们再来看看优先级队列的插入和删除操作

插入操作,我们进行排序类似于堆,所以我们回忆一下大堆小堆的插入删除。
以前我们向上调整时,判断条件是对parent和child的值进行判断。
但如果我们改变排序的顺序,例如从升序改成降序,我们这里就可能有多处代码要改。那为了简便,我们有了仿函数的概念。
仿函数该怎么具体使用,让我们来看看:
首先,我们要明确的点是,之前说了,仿函数其实是一个类。我们用仿函数,是因为他里面通过运算符重载实现了我们想要的操作
那么这里我们先定义这样的两个类

    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;
        }
    };

那么,我们了解这个点之后,我们再将他放入代码中,去实现push和pop操作
下面这块代码紧接上面的代码

template <class T, class Container = vector<T>, class Compare = less<T> >
class priority_queue
{
public:
    void Adjustup(size_t child)
    {
        Compare com;  //这里很重要! 我们通过传入的Compare类创建了这样一个对象,
                      //之后利用这个对象,去比较大小
        int parent = (child - 1) / 2;
        while (child > 0)
        {
            //这里就是不同点了,我们通过传入两个参数来比较大小!!
        
            // 这里三种写法都是对的,上面两种采用的是匿名对象的方式, 但还是推荐使用第三种!
            //if(Compare().operator()(c[parent],c[child]))
            //if(Compare()(c[parent],c[child]))
            //if(c[parent]<c[child])   //以前我们的方法
            if (com(c[parent], c[child])) 
            {
                swap(c[parent], c[child]);
                child = parent;
                parent = (child - 1) / 2;
            }
            else
            {
                break;
            }
        }
    }
    void push(const T& x)
    {
        c.push_back(x);
        Adjustup(c.size() - 1);  // 这里传参不要传错了,不然会出现问题
    }
    void Adjustdown(size_t parent)
    {
        Compare com;
        int child = parent * 2 + 1;
        while (child < c.size())
        {
            if (child + 1 < c.size() && com(c[child],c[child + 1]))
                child = child + 1;
            if (com(c[parent], c[child]))
            {
                swap(c[child], c[parent]);
                parent = child;
                child = parent * 2 + 1;
            }
            else
            {
                break;
            }
        }
    }
    void pop()
    {
        swap(c[0], c[c.size() - 1]);
        c.pop_back();
        Adjustdown(0);
    }

private:
    Container c;
    Compare comp;
};

可以看到,我们比较大小是直接通过对象,来充分使用operator()来比较大小,我们注意写好传入参数的先后顺序, 之后只需要控制传入的类类型就可以轻松控制排序。
接下来看看这段代码实现的结果吧!

void test1()
{
	// 用默认的less排降序(大堆)
	bit::priority_queue<int> pq1;
	pq1.push(10);
	pq1.push(20);
	pq1.push(30);
	pq1.push(40);

	while (!pq1.empty())
	{
		cout << pq1.top() << ' ';
		pq1.pop();
	}
	cout << endl;

	// 用greater排升序(小堆)
	bit::priority_queue<int, vector<int>, bit::greater<int>> pq2;
	pq2.push(10);
	pq2.push(20);
	pq2.push(30);
	pq2.push(40);

	while (!pq2.empty())
	{
		cout << pq2.top() << ' ';
		pq2.pop();
	}
	cout << endl;
}

在这里插入图片描述
接着,我们进行一下升级。 我们传入Date日期类类型,看看可不可以同样实现

对Date日期类进行排序

// 首先这一段是对Date类的操作
class Date
{
public:
	friend ostream& operator<<(ostream& _cout, const Date& d);

	Date(int year = 1900, int month = 1, int day = 1)
		: _year(year)
		, _month(month)
		, _day(day)
	{}

	bool operator<(const Date& d)const
	{
		return (_year < d._year) ||
			(_year == d._year && _month < d._month) ||
			(_year == d._year && _month == d._month && _day < d._day);
	}

	bool operator>(const Date& d)const
	{
		return (_year > d._year) ||
			(_year == d._year && _month > d._month) ||
			(_year == d._year && _month == d._month && _day > d._day);
	}
private:
	int _year;
	int _month;
	int _day;
};

ostream& operator<<(ostream& _cout, const Date& d)
{
	_cout << d._year << "-" << d._month << "-" << d._day;
	return _cout;
}

template<class T>
class greater  //这个类用来排小堆
{
 public:
   bool operator()(const T& x, const T& y)
   {
      return x > y;
   }
void test2()
{
	// 日期类进行排升序   
	bit::priority_queue<Date, vector<Date>, greater<Date>> pq;

	Date d1(2024, 4, 8);
	pq.push(d1);
	pq.push(Date(2024, 4, 10));
	pq.push({ 2024, 2, 15 });

	while (!pq.empty())
	{
		cout << pq.top() << " ";
		pq.pop();
	}
	cout << endl;


在这里插入图片描述
那么这依然是能实现的。

接下来有一个特殊情况,如果我们传入的是指针类型呢?那还能不能比较大小呢?

指针类型的比较大小

bit::priority_queue<Date*, vector<Date*>> pqptr;
pqptr.push(new Date(2024, 4, 14));
pqptr.push(new Date(2024, 4, 11));
pqptr.push(new Date(2024, 4, 15)); 

while (!pqptr.empty())
{
	cout << *(pqptr.top()) << " ";
	pqptr.pop();
}
cout << endl;  

很遗憾,这里并不能实现我们的效果。
因为如此,毕竟的是指针本身的大小, 而这个大小属于随机的,因此比较出来结果也会是个随机值。因此我们必须为此写一个指针解引用去比较的仿函数

在上一段代码前我们需要传入

// 这里没有给模版(template<>),所以后面调用这个类时,就不需要传入对应的类模板参数,
// 直接调用这个类就可以了
class GreaterPDate
{
public:
	bool operator()(const Date* p1, const Date* p2)
	{
		return *p1 > *p2;    // 这里是大于,就是后面形成小堆,(这个类叫Greater)
	}
};

bit::priority_queue<Date*, vector<Date*>, GreaterPDate> pqptr;
pqptr.push(new Date(2024, 4, 14));
pqptr.push(new Date(2024, 4, 11));
pqptr.push(new Date(2024, 4, 15));  
while (!pqptr.empty())
{
	cout << *(pqptr.top()) << " ";
	pqptr.pop();
}
cout << endl;

在这里插入图片描述
如此,我们才能保证每次比较的是指针解引用后的结果。这样才能稳定形成结果!
当然,这里我写的是升序的代码,老样子,把Greater改成less的代码,就是降序的了!

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

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

相关文章

【AI】Deepstream入门(2)Ubuntu20.04安装Deepstream

1、安装GPU驱动 本人显卡型号:RTX4060 Laptop(笔记本专用显卡) 【AI】惠普暗夜精灵9安装Ubuntu20.04+nvidia驱动 2、安装cuda、cuDNN 【AI】Ubuntu20.04安装cuda、cuDNN 3、安装TensorRT 1)下载 下载地址:https://docs.nvidia.com/deeplearning/tensorrt/archives/i…

数字藏品:重塑艺术与科技的新媒介

数字藏品&#xff0c;这个新兴的词汇&#xff0c;正在逐渐渗透到我们的日常生活中。它不仅是一种新的艺术表达方式&#xff0c;更是一种科技与艺术相结合的全新媒介。那么&#xff0c;数字藏品究竟是什么呢&#xff1f; 首先&#xff0c;我们需要明确一点&#xff0c;数字藏品并…

异地组网的供应商信息?

在当前信息时代&#xff0c;许多企业或个人需要跨地域进行网络连接&#xff0c;实现异地组网。异地组网是指通过网络技术将不同地区的计算机或网络设备连接起来&#xff0c;实现信息共享和远程访问的功能。本文将介绍一家供应商【天联】在异地组网领域的优势和相关服务。 【天联…

数据结构-二叉树-堆

一、物理结构和逻辑结构 在内存中的存储结构&#xff0c;逻辑结构为想象出来的存储结构。 二、完全二叉树的顺序存储结构 parent (child - 1)/2 leftchild 2*parent 1; rightchild 2*parent 2 上面的顺序结构只适合存储完全二叉树。如果存储&#xff0c;会浪费很多的空…

Hadoop 启动!

​2024/4/22 上个星期我们已经完成了Hadoop的安装及配置文件的修改 下面 我们将namenode进行一下初始化 hdfs namenode -format (创建文件存储目录&#xff1a;账本目录namenode datanode的目录) 我们在配置时 这就是用来设置账本目录的 我们做完格式化后 tmp目录就出现了 …

权威认证!瀚高股份IvorySQL通过强制性国标GB18030-2022最高级别认证

近日&#xff0c;GB 18030-2022《信息技术 中文编码字符集》应用推广大会暨“汉字守护计划”成果发布会在京召开。瀚高股份开源关系型数据库IvorySQL通过 GB 18030-2022《信息技术 中文编码字符集》强制性国家标准测试&#xff0c;达到最高实现级别&#xff08;3级&#xff09;…

管理之窗:调动下属积极性,难道就是涨工资么?

很多领导在日常管理中&#xff0c;总遇到下属积极性不强的局面&#xff0c;但是很多人总以为涨工资就能解决问题&#xff0c;其实不然&#xff0c;很多下属积极性的强弱和上级的领导力和调动积极性的政策密切相关。 前几天一个企业家来聊&#xff0c;提到了他们单位的人力资源部…

NXP应用随记(七):S32K3XX复位与启动阅读记录

目录 1、复位过程 1.1、概述 1.2、复位产生模块 1.2.1、上电复位 1.2.2、破坏性复位 1.2.3、功能复位 1.3、芯片复位及引导概述 1.4、重置和启动流程图 1.5、复位块序列 2、上电复位 3、破坏性复位 4、功能复位 5、设备配置格式(DCF) 6、重置专题 6.1、重置引脚行…

趋势分析是什么?市场趋势分析的经典方法,从数据中识别机会

趋势分析是把不同时期数据中的相同指标或比率进行比较&#xff0c;观察其增减变动情况及变动幅度&#xff0c;考查发展趋势&#xff0c;预测发展前景的一种方法。 基本原理是在一定时间范围内&#xff0c;通过观察数据的趋势性&#xff0c;发现数据背后的规律性变化。 趋势分析…

应用在智能手环测温功能中的数字温度传感芯片

智能手环是一种穿戴式智能设备。通过智能手环&#xff0c;用户可以记录日常生活中的锻炼、睡眠、部分还有饮食等实时数据&#xff0c;并将这些数据与手机、平等同步。智能手环是一种穿戴式智能设备。通过这款手环&#xff0c;用户可以记录日常生活中的锻炼、睡眠和饮食等实时数…

上位机图像处理和嵌入式模块部署(树莓派4b与mcu固件升级)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 在一个系统当中&#xff0c;可能不止需要树莓派4b一个设备&#xff0c;有的时候还需要搭载一个mcu&#xff0c;做一些运动控制的事情。比如说&…

react —— useState 深入

基础用法 useState Hook 提供了这两个功能&#xff1a; State 变量 在第一次重新渲染期间&#xff0c;这将具有作为参数传递的值State setter 函数 set 函数将允许将状态的值更新为不同的值&#xff0c;如果 set 函数中提供的值不同&#xff0c;则将触发重新渲染。 注意&…

【stomp 实战】spring websocket源码分析之握手请求的处理

上一节【搭建一套websocket推送平台】我们通过一个项目&#xff0c;实现了一套推送平台。由于spring框架对于websocket的支持和stomp协议的良好封装&#xff0c;我们很容易地就实现了websocket的消息推送功能。虽然搭建这么一套推送系统不难&#xff0c;但是如果不了解其底层原…

2024年Q1企业邮箱安全性研究报告:钓鱼邮件同比增长59.9%

4月23日&#xff0c;Coremail邮件安全联合北京中睿天下信息技术有限公司发布《2024年第一季度企业邮箱安全性研究报告》。对当前企业邮箱的应用状况和安全风险进行了分析。 1、垃圾邮件持续增长 根据Coremail邮件安全人工智能实验室最新数据显示&#xff0c;2024年第一季度&am…

【Web】第三次

【Web】第三次 1.完成学校官方网站页面制作2.使用动画完成过渡变换效果 1.完成学校官方网站页面制作 2.使用动画完成过渡变换效果 1.完成学校官方网站页面制作 html&#xff1a; <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://…

【从后端日志文件中过滤出sql语句】

从后端日志文件中过滤出sql语句 why?思路日志文件的格式 结果 why? 为什么会有这种需求&#xff1f;&#xff0c;mysql数据不小心被删了完全可以从备份数据恢复&#xff0c;或者从binlog中恢复&#xff0c;但是如果前面这两种方法没办法处理&#xff08;没有备份数据库文件、…

opencv基础篇 ——(八)图像平滑滤波

均值滤波blur 用于对图像进行均值滤波&#xff08;Mean Filtering&#xff09;的函数。它通过对图像中每个像素点邻域内所有像素值求平均来计算新的像素值&#xff0c;以此达到平滑图像、降低噪声和消除细节的目的 函数原型&#xff1a; void cv::blur(InputArray src,Output…

百面算法工程师 | 分类和聚类

目录 6.1 为什么正确率有时不能有效评估分类算法&#xff1f; 6.2 什么样的分类器最好&#xff1f; 6.3 什么是聚类&#xff0c;你知道哪些聚类算法&#xff1f; 6.4 K-Means聚类算法如何调优? 6.5 K-Means聚类算法如何选择初始点? 6.6 K-Means聚类聚的是特征还是样本 …

贪吃蛇身子改进加贪吃蛇向右移动

1. 蛇移动的思想&#xff1a; 其实就是删除头节点 &#xff0c;增加尾节点&#xff1b;一句代码搞定 struct Snake *p; p head; head head -> next; free(p) 防止造成多的空间节点 2.增加尾节点代码思想&#xff1a; 2.1 .开辟new 节点的空间 struct Snake *new (stru…

2015年中国电子地图数据

这是一份收藏得比较久的电子地图数据&#xff0c;虽然很比较旧&#xff0c;但数据内容很丰富&#xff0c;它可以在一些GIS系统中作测试用。 2015中国电子地图 2015年中国电子地图数据的压缩包有3.29GB大小&#xff0c;如下图所示。 压缩文件大小 该数据进行解压之后&…