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

这篇博客用来记录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.mfbz.cn/a/573940.html

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

相关文章

【STM32HAL库】外部中断

目录 一、中断简介 二、NVIC 1.寄存器 2.工作原理 3.优先级 4.使用NVIC 三、EXTI 1.简介 2.AFIO&#xff1a;复用功能IO&#xff0c;主要用于重映射和外部中断映射配置​编辑 3. 中断使用 4.HAL库配置使用 一、中断简介 中断的意义&#xff1a;高效处理紧急程序&#xff0c;不会…

树莓派学习笔记--串口通信(配置硬件串口进行通信)

树莓派串口知识点 树莓派4b的外设一共包含两个串口&#xff1a;硬件串口&#xff08;/dev/ttyAMA0&#xff09;,mini串口&#xff08;/dev/ttyS0&#xff09; 硬件串口由硬件实现&#xff0c;有单独的波特率时钟源&#xff0c;性能高&#xff0c;可靠&#xff1b;而mini串口性能…

Java-AQS的原理

文章目录 基本概述1. 设计思想2. 基本实现 一些关键词语以及常用术语&#xff0c;主要如下&#xff1a; 信号量(Semaphore): 是在多线程环境下使用的一种设施&#xff0c;是可以用来保证两个或多个关键代码段不被并发调用&#xff0c;也是作系统用来解决并发中的互斥和同步问题…

数据挖掘 | Count数据去除批次效应后不是整数甚至还出现负值导致无法进行差异分析怎么办?

之前咱们介绍过数据挖掘 | 批次效应的鉴定与处理 | 附完整代码 注释 | 看完不会来揍我&#xff0c;但是很多小伙伴遇到了Count数据批次处理后不是整数甚至还出现负值的问题&#xff0c;这就导致无法使用某些包包进行差异分析&#xff08;对差异分析感兴趣的小伙伴可以查看&…

MySQL中如何随机获取一条记录

点击上方蓝字关注我 随机获取一条记录是在数据库查询中常见的需求&#xff0c;特别在需要展示随机内容或者随机推荐的场景下。在 MySQL 中&#xff0c;有多种方法可以实现随机获取一条记录&#xff0c;每种方法都有其适用的情况和性能特点。在本文中&#xff0c;我们将探讨几种…

word添加行号

打开页面设置&#xff0c;找到行号

2018-2023年上市公司富时罗素ESG评分数据

2018-2023年上市公司富时罗素ESG评分数据 1、时间&#xff1a;2018-2023年 2、来源&#xff1a;整理自WIND 3、指标&#xff1a;证券代码、简称、ESG评分 4、范围&#xff1a;上市公司 5、指标解释&#xff1a; 富时罗素将公司绿色收入的界定和计算作为公司ESG 评级打分结…

「白嫖」开源的后果就是供应链攻击么?| 编码人声

「编码人声」是由「RTE开发者社区」策划的一档播客节目&#xff0c;关注行业发展变革、开发者职涯发展、技术突破以及创业创新&#xff0c;由开发者来分享开发者眼中的工作与生活。 面对网络安全威胁日益严重的今天&#xff0c;软件供应链安全已经成为开发者领域无法避免的焦点…

OpenWRT设置自动获取IP,作为二级路由器

前言 上一期咱们讲了在OpenWRT设置PPPoE拨号的教程&#xff0c;在光猫桥接的模式下&#xff0c;OpenWRT如果不设置PPPoE拨号&#xff0c;就无法正常上网。 OpenWRT设置PPPoE拨号教程 但现在很多新装的宽带&#xff0c;宽带师傅为了方便都会把光猫设置为路由模式。如果你再外…

【A-024】基于SSH的房屋租赁管理系统(含论文)

【A-024】基于SSH的房屋租赁管理系统&#xff08;含论文&#xff09; 开发环境&#xff1a; Jdk7(8)Tomcat7(8)MySQLIntelliJ IDEA(Eclipse) 数据库&#xff1a; MySQL 技术&#xff1a; SpringStruts2HiberanteBootstrapJquery 适用于&#xff1a; 课程设计&#xff0c;毕…

半波整流220V转正5V负-5V100mA恒压WT5101A

半波整流220V转正5V负-5V100mA恒压WT5101A WT5101A 是一款专为 Buck 和 Buck-Boost 拓扑而设计的高效、具有成本优势的离线恒压稳压器&#xff0c;内嵌有500V MOSFET。在降低系统成本的同时&#xff0c;这款稳压器只需少量的外部元件就能输出默认的5V电压。在轻负载条件下&…

Sping源码(七)—context: component-scan标签如何扫描、加载Bean

序言 简单回顾一下。上一篇文章介绍了从xml文件context component-scan标签的加载流程到ConfigurationClassPostProcessor的创建流程。 本篇会深入了解context component-scan标签底层做了些什么。 component-scan 早期使用Spring进行开发时&#xff0c;很多时候都是注解 标…

智能算法 | Matlab基于CBES融合自适应惯性权重和柯西变异的秃鹰搜索算法

智能算法 | Matlab基于CBES融合自适应惯性权重和柯西变异的秃鹰搜索算法 目录 智能算法 | Matlab基于CBES融合自适应惯性权重和柯西变异的秃鹰搜索算法效果一览基本介绍程序设计参考资料效果一览 基本介绍 Matlab基于CBES融合自适应惯性权重和柯西变异的秃鹰搜索算法 融合自适应…

ds18b20温度传感器驱动程序

ds18b20驱动程序 有了之前延时的方法&#xff0c;那么实现一个单总线数据传输的传感器驱动程序就非常简单了。下面我们套用杂项驱动框架来编写ds18b20驱动程序。 实现需要明确的是&#xff1a;**ds18b20驱动的本质是通过2440的gpio&#xff0c;通过给定的时序对ds18b20的读写数…

【介绍下WebStorm开发插件】

&#x1f3a5;博主&#xff1a;程序员不想YY啊 &#x1f4ab;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f917;点赞&#x1f388;收藏⭐再看&#x1f4ab;养成习惯 ✨希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出…

保护你的网站:了解5种常见网络攻击类型及其防御方法

随着互联网的迅猛发展&#xff0c;针对网站的各种类型的网络攻击随之增加&#xff0c;网络攻击事件层出不穷&#xff0c;由此&#xff0c;如何保护网站安全成为每个网站所有者的重要议题。在下面的内容中&#xff0c;我们将探讨5种常见网络攻击类型及其防御方法&#xff0c;以帮…

SNETCracker--超级弱口令检查工具简介

一、简介 SNETCracker 超级弱口令检查工具是一款Windows平台的弱口令审计工具&#xff0c;支持批量多线程检查&#xff0c;可快速发现弱密码、弱口令账号&#xff0c;密码支持和用户名结合进行检查&#xff0c;大大提高成功率&#xff0c;支持自定义服务端口和字典。 二、SNE…

常见内网系统网络结构及nginx代理配置

系统网络结构图及nginx配置 1.系统网络结构图2.Nginx网络配置2.1请求从互联网区访问到内网区2.2 请求从内网访问互联网 1.系统网络结构图 传统公司服务部署网络都会分区&#xff0c;应用都部署在内网区&#xff0c;请求通过dmz区转出内网与互联网发生交互。 结构图详解&#…

springCloud集成activiti5.22.0流程引擎

springCloud集成activiti5.22.0流程引擎 点关注不迷路&#xff0c;欢迎再访&#xff01; 精简博客内容&#xff0c;尽量已行业术语来分享。 努力做到对每一位认可自己的读者负责。 帮助别人的同时更是丰富自己的良机。 小编最近工作需要涉及到流程&#xff0c;由于网络上5.22版…

CHARLS轻松发二区,只用了COX回归模型 | CHARLS CLHLS CFPS 公共数据库周报(4.3)...

零基础CHARLS发论文&#xff0c;不容错过&#xff01; 长期回放更新指导&#xff01;适合零基础&#xff0c;毕业论文&#xff0c;赠送2011-2020年CHARLS清洗后的数据全套代码&#xff01; CHARLS公共数据库 CHARLS数据库简介中国健康与养老追踪调查(China Health and Retireme…