stack和list

news2025/1/16 17:43:47

前言

stack和list的使用就不讲了,讲一下模拟实现,然后讲一下deque,最后讲一下优先队列

1. stack的模拟实现

	template<class T,class container>//这个container是vector,或者list或者deque(后面会说),这就叫做适配器,
	//用适配器来实现stack
	//就免去了很多我们要实现的东西
	class stack
	{
	public:
		//stack();可以不用写
		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.enmpty();
		}

	private:
		container _con;
	};
template<class T,class container>
这个container是vector,或者list或者deque(后面会说),这就叫做适配器,
用适配器来实现stack
就免去了很多我们要实现的东西
因为vector,list这些适配器都有pop_back,这些函数,所以不管是哪个适配器都是可以实现这个栈的
		stack<int,vector<int>> s;
		s.push(1);
		s.push(2);
		s.push(3);
		s.push(4);
		s.push(5);
		s.push(6);
		while (!s.empty())
		{
			cout << s.top() << endl;
			s.pop();
		}
		cout << endl;

在这里插入图片描述

然后这样调用,这个模版参数,模版列表,就相当于函数那样,int传给T,vector传给container,然后就可以正常操作了,因为像函数那样,所以模版参数也可以设置缺省值

template<class T,class container=deque<T>>

这里既可以设置vector为缺省值,也可以设置list,但我们一般设置deque,队列也是这样的

stack<int> s;

2. queue的模拟实现

namespace bit
{
	template<class T, class container = deque<T>>
	class queue
	{
	public:
		//stack();可以不用写//因为有默认构造

		void push(const T& x)
		{
			_con.push_back(x);
		}

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

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

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

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

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

	private:
		container _con;
	};

	void test2()
	{
		queue<int> s;
		s.push(1);
		s.push(2);
		s.push(3);
		s.push(4);
		s.push(5);
		s.push(6);
		while (!s.empty())
		{
			cout << s.top() << endl;
			s.pop();
		}
		cout << endl;
	}
}

在这里插入图片描述
这个就没什么好说的了,和stack差不多,唯一值得说的就是,那个模版参数的缺省值不能设置vetor,第一是因为vector没有头插这个函数,第二就是vector头插效率太低了

3. deque相关讲解

deque也叫做双端列表
在这里插入图片描述

双端列表的底层大致结构就是这样的,有一个指针数组,指针数组也不是从头开始的,而是从中间某个位置开始指向一些小数组,小数组的大小一般是固定的,比如都是10个
在这里插入图片描述
如果要尾插入数据,就会在末尾的小数组后面插入数据,小数组满了,就在这个末尾小数组后面在开辟,头插入数据,就在最前面的小数组前面插入数据,满了继续开辟就是了
deque还支持随机访问,也就是[]访问,因为每个小数组长度固定,就可以通过/10和%10来快速确定位置,所以访问也很快,但是访问没有vector快
但是呢,deque的中间插入就很坑了,要大量挪动后面的小数组
所以说deque这个东西呢,头插头删,尾插尾删很方便,其余的都一般,正是因为其余的一般般,所以无法替代vector和list,因为头插头删,尾插尾删很方便,所以适合作为栈和队列的适配器
下面讲一下大致怎么实现的
在这里插入图片描述
deque最主要的内容就是迭代器了,上图的starrt和finish就是迭代器,deque就是全程依靠迭代器实现的cur指向那个小数组里的某个数据当前位置,first指向小数组的头,last指向小数组的尾,node是个二级指针,指向指针数组里的指向小数组的值,就这样就可以很快实现deque了
所以说呢,deque就相当于是vector和list的结合体
还有就是,它的头文件就是deque

4. 优先级队列priority_queue

这个的头文件就是queue,这个东西就类似堆,或者说就是堆,底层是一个数组,使用和堆一摸一样的,因为底层是一个数组,所以我们可以用vector作为适配器
讲priority_queue的实现前,我们先讲一下使用
在这里插入图片描述
priority_queue首先它没有initializer_list的构造,所以不能这样,但它支持迭代器的赋值

	int arr[] = { 1,2,34,5,7,8,9,07,6,5,5,4,3,3 };
	priority_queue<int> a(arr, arr + sizeof(arr) / sizeof(arr[0]));
	while (!a.empty())
	{
		cout << a.top() << " ";
		a.pop();
	}

在这里插入图片描述
首先要说的就是,对于正常的数组,它的指针就是就是它的迭代器
然后因为是堆嘛,每次出数据,调整数据,那肯定是有序的,看的出来,我们这个实现的是大堆

	int arr[] = { 1,2,34,5,7,8,9,07,6,5,5,4,3,3 };
	priority_queue<int,vector<int>,greater<int>> a(arr, arr + sizeof(arr) / sizeof(arr[0]));
	while (!a.empty())
	{
		cout << a.top() << " ";
		a.pop();
	}

如果要实现成小堆的话,就要加上greater了,其实如果是排序的话,也是一样的,我们调用的排序,默认是升序的,但是如果加上greater,那么就是降序的了,因为编译器默认的模版参数是less,less是升序的,因为greater在priority_queue中是第三个参数,所以要传greater就要先传vector,vector是第二个模版参数,而且还是默认值
下面我们开始priority_queue的模拟实现
先实现一个大堆,先不管greater怎么搞的
在此之前,先提醒一点模版中的语法错误是不会直接用红线报出来的,比如你用的中文符号就不会直接报错来

	template<class T,class container=vector<T>>
	class priority_queue
	{
	public:

		priority_queue() = default;

		//我们先写个迭代器区间构造
		template<class my_iterator>
		priority_queue(my_iterator first, my_iterator end)
		{
			while (first != end)
			{
				_con.push_back(*first);
				first++;
			}
			//在把这个写成堆
			int child = (_con[(_con.size() - 1 - 1) / 2]);//从这个位置开始向下调整
			while (child >= 0)
			{
				Adjust_down(child);
				child--;
			}
		}

		void Adjust_down(int parent)
		{
			int child = 2 * parent + 1;
			while (child < _con.size())
			{
				if ((child + 1) < _con.size() && _con[child + 1] > _con[child])
				{
					child++;
				}
				if (_con[parent] < _con[child])
				{
					swap(_con[parent], _con[child]);
					parent = child;
					child = 2 * child + 1;
				}
				else
				{
					break;
				}
			}
		}

		void Adjust_up(int child)
		{
			int parent = (child - 1) / 2;
			while (child > 0)//child等于0,就说明已经比较完了
			{
				if (_con[child] > _con[parent])
				{
					swap(_con[child], _con[parent]);
					child = parent;
					parent = (child - 1) / 2;
				}
				else
				{
					break;
				}
			}
		}

		void push(const T&x)
		{
			//先插入vector,然后在向上调整
			_con.push_back(x);
			Adjust_up((int)_con.size() - 1);
		}

		void pop()
		{
			swap(_con[0], _con[_con.size() - 1]);
			_con.pop_back();
			Adjust_down(0);
		}

		T& top()
		{
			return _con[0];
		}

		const T& top()const
		{
			return _con[0];
		}

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

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

	private:
		container _con;
	};
		int arr[] = { 1,2,34,5,7,8,9,7,6,5,5,4,3,3 };

		priority_queue<int> a(arr, arr + sizeof(arr) / sizeof(arr[0]));
		while (!a.empty())
		{
			cout << a.top() << " ";
			a.pop();
		}

在这里插入图片描述
我们这个建立的是大堆,如何建立小堆呢,其实只需要将大堆的函数的>改成<,<改成>就可以了
但是这样还要写一个模版吗,其实还有一个更简单的方法,就是仿函数的方法

	template<class T>
	class myless
	{
		bool operator()(T& t1, T& t2)
		{
			return t1 < t2;
		}
	};

如上图,这就是个仿函数,所谓仿函数就是对()的重载,使类产生的对象可以像函数那样去使用

		myless<int> m;
		cout << m(1, 2) << endl;

在这里插入图片描述
以前的重载,比如operator<;就是这样使用的m<T,小于始终只有一个操作数,但是()的重载,操作数就可以有很多个,而且返回值不唯一,也可以有很多

	template<class T>
	class myless
	{
	public:
		bool operator()(const T& t1,const T& t2)
		{
			return t1 < t2;
		}
	};

	template<class T>
	class mygreater
	{
	public:
		bool operator()(const T& t1, const T& t2)
		{
			return t1 > t2;
		}
	};

所以两个仿函数就这样定义


		void Adjust_down(int parent)
		{
			int child = 2 * parent + 1;
			while (child < _con.size())
			{
				if ((child + 1) < _con.size() && _con[child + 1] > _con[child])
				{
					child++;
				}
				compare a;
				if (a(_con[parent] , _con[child]))
				{
					swap(_con[parent], _con[child]);
					parent = child;
					child = 2 * child + 1;
				}
				else
				{
					break;
				}
			}
		}

		void Adjust_up(int child)
		{
			int parent = (child - 1) / 2;
			while (child > 0)//child等于0,就说明已经比较完了
			{
				compare a;
				if (a(_con[parent], _con[child]))
				{
					swap(_con[child], _con[parent]);
					child = parent;
					parent = (child - 1) / 2;
				}
				else
				{
					break;
				}
			}
		}

对应函数就这样写,这样的话,就实现了大于小于的切换了,只需要传入less和greater就可以了,这里我们防止与库里的less冲突,所以才这样命名

		int arr[] = { 1,2,34,5,7,8,9,7,6,5,5,4,3,3 };
		priority_queue<int,vector<int>,mygreater<int>> a(arr, arr + sizeof(arr) / sizeof(arr[0]));

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

在这里插入图片描述
以后还会经常用到仿函数的

5. 练习题

5.1 最小栈

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

class MinStack {
public:
    MinStack() {
        //不用写
    }

    void push(int val) {
        stack1.push(val);
        if (stack2.empty() || stack2.top() >= val)//为空,或者val就是最小数据
        {
            stack2.push(val);
        }
    }

    void pop() {
        int tmp = stack1.top();
        stack1.pop();
        if (tmp == stack2.top())
        {
            stack2.pop();
        }
    }

    int top() {
        return stack1.top();
    }

    int getMin() {
        return stack2.top();
    }
private:
    stack<int> stack1;
    stack<int> stack2;
};

5.2 栈的压入、弹出序列

在这里插入图片描述

在这里插入图片描述

class Solution {
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     *
     * @param pushV int整型vector
     * @param popV int整型vector
     * @return bool布尔型
     */
    bool IsPopOrder(vector<int>& pushV, vector<int>& popV) {
        // write code here
        //stack<int> s1;
        //int i = 0;
        //int j = 0;
        //s1.push(pushV[i]);
        //i++;
        //while (i < pushV.size())//如果这样设计的话,那么最后一个元素入栈,后就直接跳出来了,还没有判断后面的是否对应
    //    while (i < pushV.size())//但取了等于就永远死循环了,太麻烦了
    //    {
    //        while(!s1.empty()&&popV[j] == s1.top())//因为为空无法访问
    //        {
    //            s1.pop();
    //            j++;
    //        }
    //        if(s1.empty()|| popV[j] != s1.top())
    //        {
    //            s1.push(pushV[i]);
    //            i++;
    //        }
    //    }
    //    if (!s1.empty())
    //    {
    //        return false;
    //    }
    //    else
    //    {
    //        return true;
    //    }
    //}
        stack<int> s1;
        int i = 0;
        int j = 0;
        while (i < pushV.size())//交换一下位置呢//先入栈在判断是否对应
        {
            if (s1.empty() || popV[j] != s1.top())
            {
                s1.push(pushV[i]);
                i++;
            }
            while (!s1.empty() && popV[j] == s1.top())//因为为空无法访问
            {
                s1.pop();
                j++;
            }
        }
        if (!s1.empty())
        {
            return false;
        }
        else
        {
            return true;
        }
    }
};

5.3 数组中的第K个最大元素

在这里插入图片描述

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

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

相关文章

基于树莓派的收银系统-KwickPOS

基于树莓派的收银系统在繁忙的餐厅和零售场所大受欢迎 低成本、功能强大、紧凑和稳定的Raspberry Pi计算模块提供平滑的收银解决方案&#xff0c;为北美和中美洲的数千名KwickPOS客户提供不间断的运行时间。 解决方案 Compute Module 3 Compute Module 4 企业规模 中小企业…

java之抽象类以及如何优化

抽象类的作用是什么: 当我们抽取共性时候,无法确定方法体,就把方法定义为抽象的,强制让子类按照某种格式重写,抽象方法所在的类,必须是抽象类.我们先定义一个抽象类 ,abstract的意思就是抽象类, 一般来说,在抽象类里面定义的方法也是抽象方法. public abstract class Animal {p…

【Linux操作系统】关于系统中内存文件与进程的关系以及文件描述符fd、重定向的理解

目录 一、关于文件和进程关系的简介二、了解文件操作的系统接口和C语言文件操作接口1.C语言文件操作接口2.文件操作的系统接口 三、关于C语言接口和系统接口的关系四、文件描述符&#xff08;fd&#xff09;1.FILE* 结构体2.文件描述符表&#xff08;fd的本质&#xff09;3.文件…

透明显示屏方案介绍

透明显示屏方案是一种创新的显示技术解决方案&#xff0c;它结合了透明材料和高性能显示技术&#xff0c;实现了在显示内容的同时保持屏幕背后物体或场景的可见性。以下是对透明显示屏方案的详细介绍&#xff1a; 一、技术原理 透明显示屏方案主要采用了LED透明屏和OLED透明屏两…

基于PHP+MySQL组合开发的微信活动投票小程序源码系统 带完整的安装代码包以及搭建部署教程

系统概述 在当今数字化时代&#xff0c;微信作为社交媒体的巨头&#xff0c;为企业和个人提供了丰富的互动营销平台。其中&#xff0c;投票活动作为一种有效的用户参与和互动方式&#xff0c;被广泛应用于各种场景。为了满足这一需求&#xff0c;我们推出了一款基于PHPMySQL组…

zabbix“专家坐诊”第249期问答

问题一 Q&#xff1a;zabbix server服务每次重启监控主机就会触发大量的“10分钟未获取到数据”的告警&#xff0c;同时还会触发zabbix的history进程繁忙的告警。&#xff08;后面检查实际上监控主机在告警时间段内是有数据的&#xff09;感觉是server一重启&#xff0c;数据库…

Python | ValueError: invalid literal for int() with base 10: ‘example’

Python | ValueError: invalid literal for int() with base 10: ‘example’ 在Python编程中&#xff0c;遇到ValueError: invalid literal for int() with base 10: example这样的错误通常意味着你试图将一个字符串转换为整数&#xff0c;但该字符串包含非数字字符。这种错误…

美团 AIGC产品经理面经(已拿 offer)

背景&#xff1a;211本科毕业&#xff0c;毕业之后在北京一家中型电商公司做了3年商家后台产品经理&#xff0c;目前通过老薛的朋友关系拿到了美团的offer。 目前还有几家在面试流程中&#xff0c;继续加油&#x1f4aa; 美团AIGC产品面经-业务面 &#x1f4a5;1、自我介绍&a…

找不到vcruntime140_1.dll,无法执行此代码

电脑运行某些软件提示&#xff1a;由于找不到vcruntime140_1.dll,无法继续执行代码 解决方法 1、下载这个软件 https://download.csdn.net/download/szdenny/89605688 2、 通过这个网址下载软件修复&#xff1a;http://xn--dll-8n0e103b00dgy4e.site/

前端:Vue学习 - 智慧商城项目

前端&#xff1a;Vue学习 - 智慧商城项目 1. vue组件库 > vant-ui2. postcss插件 > vw 适配3. 路由配置4. 登录页面静态布局4.1 封装axios实例访问验证码接口4.2 vant 组件 > 轻提示4.3 短信验证倒计时4.4 登录功能4.5 响应拦截器 > 统一处理错误4.6 登录权证信息存…

ArcGIS导出的shp不带prj文件怎么办?

0序 遇到了好多个做测绘的朋友&#xff0c;拿到的shp文件都没有prj文件&#xff0c;在图新地球当中无法加载。 而prj文件是其他软件解析shp&#xff0c;和地图做叠加的核心。否则就不知道shp要素的坐标按照什么坐标去解析。 经过排查发现&#xff0c;大部分是在arcgis中导出sh…

数据同步策略概览

数据同步在业务开发中比较普遍&#xff0c;例如 订阅MySQL的binlog将数据同步至异构数据库。数据同步方案需要考虑一下几点&#xff1a; 数据实时性要求数据量级是否有数据转换逻辑 可分为两种模式 发布订阅模式&#xff1a;分为订阅数据库log还是订阅应用层发的消息点对点模…

适合印刷企业使用的MES管理系统具备哪些特点

在当今竞争激烈的印刷行业中&#xff0c;提高生产效率、优化资源配置、确保产品质量与交期已成为企业生存与发展的关键。为此&#xff0c;引入一套高效、智能的印刷企业MES管理系统显得尤为重要。MES管理系统作为连接企业资源计划ERP系统与车间生产现场的桥梁&#xff0c;其设计…

Linux下的nc命令:网络工具的瑞士军刀

在 Linux 系统中&#xff0c;有许多强大的网络工具可用于网络连接、数据传输和端口扫描。其中一个非常强大的工具是 nc&#xff0c;也称为 Netcat&#xff0c;被称为网络工具的瑞士军刀&#xff0c;因为它可以执行各种网络任务。本文将深入探讨 nc 命令的使用方法&#xff0c;提…

第127天:内网安全-隧道搭建穿透上线FRPNPSSPPNgrokEW 项目

目录 案例一&#xff1a;内网穿透-Ngrok-入门-上线 案例二&#xff1a;内网穿透-Frp-简易型-上线 案例三&#xff1a; 内网穿透-Nps-自定义-上线 案例四&#xff1a;内网穿透-Spp-特殊协议-上线 案例一&#xff1a;内网穿透-Ngrok-入门-上线 这里我是用了一台云服务器&…

zy青岛实训day19 8/1

接着昨天的 npm run serve 构建项目 npm run build ls ls dist/ vim dist/index.html [rootweb eleme_web]# cd /usr/local/nginx/conf/ [rootweb conf]# ls 将静态的项目移动到nginx中 [rootweb nginx]# cd conf.d/ [rootweb conf.d]# ls qd.conf [rootweb conf.…

信息学奥赛初赛天天练-52-CSP-J2019基础题3-抽屉原理、鸽巢原理、乘法原理、二叉树遍历、前序遍历、中序遍历、后序遍历

PDF文档公众号回复关键字:20240801 2019 CSP-J 基础题3 单项选择题&#xff08;共15题&#xff0c;每题2分&#xff0c;共计30分&#xff1a;每题有且仅有一个正确选项&#xff09; 11 新学期开学了&#xff0c;小胖想减肥&#xff0c;健身教练给小胖制定了两个训练方案。 …

个人的知识点小分享

type查看命令类型&#xff08;内建命令/外部命令&#xff09; [rootlocalhost ~]# type cd cd is a shell builtin [rootlocalhost ~]# type ls ls is aliased to ls --colorauto Ps 命令 --help 查看命令的常见选项 info 命令 #比man更详细的帮助命令。 回车…

arduino程序-MC猜数字1(基础知识)

arduino程序-MC猜数字1&#xff08;基础知识&#xff09; 1-18 MC猜数字0-介绍1-19 MC猜数字1-电路搭建电路图所需元件10根杜邦线&#xff08;公对公&#xff09;10根左右面包板跳线数码管按键电阻1个&#xff08;200~500欧姆&#xff09;面包板Arduino UNO R3*1 搭建电路相关程…

实验2-4-4 求简单交错序列前N项和

//实验2-4-4 求简单交错序列前N项和//计算序列 1 - 1/4 1/7 - 1/10 ... 的前N项之和。 #include<stdio.h> #include<math.h> int main(){int n;scanf("%d",&n);//输入在一行中给出一个正整数N。double sum0;for(int i1;i<n*3;i3){//i3>题目…