C++的stack和Queue

1.简单实现stack

构建一个模板,俩个参数,这里第一个一般是数据的类型,第二个是由什么来实现栈,在主函数里传了int和vector<int>,第二个不传参也可以,因为是缺省参数,默认为vector,这样就可以使用vector封装好的函数使用,push为尾插,就可以使用vector的push_back函数,pop是尾删,top是栈顶的元素,size是栈的大小,最后一个是判断是否为空。

cpp 复制代码
namespace zym
{
	template<class T, class Container = vector<T>>
	class stack
	{
	public:
		void push(const T& x)
		{
			_con.push_back(x);
		}
		void pop()
		{
			_con.pop_back();
		}
		const T& top() const
		{
			return _con.back();
		}
		size_t size() const
		{
			return _con.size();
		}

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

//test.cpp

#include<iostream>
#include<vector>
#include<list>
#include<stack>
#include<queue>
#include<algorithm>
using namespace std;

#include"Stack.h"
#include"Queue.h"
//#include"PriorityQueue.h"


int main()
{
	//bit::stack<int, vector<int>> st;
	//bit::stack<int, list<int>> st;
	zym::stack<int, vector<int>> st;

	// 类模板实例化时,按需实例化,使用哪些成员函数就实例化哪些,不会全实例化
	st.push(1);
	st.push(2);
	st.push(3);
	st.push(4);

	cout << st.top() << endl;
	st.pop();

	zym::queue<int, list<int>> q;
	//zym::queue<int> q;
	//q.push(1);
	//q.push(2);
	//q.push(3);
	//q.push(4);

	//cout << q.front() << endl;
	//q.pop();

	return 0;
}

2.简单实现Queue

跟之前的stack基本是一样的,除了实现原理不一样,队列需要链表,剩下就是使用对应的库函数。

cpp 复制代码
namespace zym
{
	template<class T,class Container=list<int>>
	class queue
	{
	public:
		void push(const T& x)
		{
			_con.push_back(x);
		}
		void pop()
		{
			_con.pop_front();
		}
		const T& front() const
		{
			return _con.front();
		}
		const T& back()
		{
			return _con.back();
		}

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



	zym::queue<int> q;
	q.push(1);
	q.push(2);
	q.push(3);
	q.push(4);

	cout << q.front() << endl;
	q.pop();

3.deque

deque概念

deque就是把list和vector合在一起了,把这俩个互补了,vector因为是连续的存储空间,所以尾插和下标访问效率好,且缓存利用率也高,但数组需要扩容,扩容就会带来效率低和空间浪费,在中间位置插入数据效率低,因为要挪动数据。list则是按需申请和释放空间,不需要扩容,链表可以在任意位置插入和删除,缺点是不能像数组一样通过下标访问。而deque是结合俩者的优点,但是效率并不是很好。

deque的底层

链表是一个一个分散的空间,vector则是连续空间,则deque是分散的连续空间,list的每个节点只有一个元素的空间大小,而deque是每个节点变为了一个数组buf,用一个二级指针数字map储存每一个buf的地址,这个map是从中间作为第一个的,如果要头插就会在前面插入。

类里面有四个成员变量,都是迭代器,cur是指向当前元素的下一个,first是当前buf(数组)的第一个,last是最后一个元素的下一个,node是指向储存当前buf的位置。

移动下一个节点

重载的++是先判断cur和last是否一样,如果一样就调用set_node函数,set_node是把node+1赋值给node,first指向node+1的第一个位置,last等于first+这个数组的大小。

下标访问

如果是在第一个buf,要访问第n个,则可以先找到是在哪一个buf,n/sizeof(buf)表示是在第几个buf,然后是去找是在buf里面的第几个位置,n%sizeof(buf)表示是在buf里面的第几个位置,如果是在前面(头插后),则这样的办法就不太行,需要另外的,vs的编译器是把下标n和cur-first相加。

头插

注意的是头插的话,是从last位置开始的,这时候last就是最后一个元素的下一个,cur就是指向当前元素而不是下一个了。

插入和删除

如果在中间要插入和删除,都会挪动数据,就会有效率低的问题,如果是让buf的容量加减在插入和删除时,这样又会有问题,每个buf的大小不一样,在下标访问就需要更复杂的计算了,vs则是buf不变。

总结

1.deque头插尾插效率高

2.下标访问也还不错(计算以及判断多有影响)

3.中间插入删除效率很低,要挪动数据

4.用deque来实现stack和queue

注意:对于模板在没有按需实例化时,不会检查细节的代码,只会检查大概,所以可能运行没问题,后面有问题是因为有写函数没有调用,调用了编译器才会仔细检查代码的合理性。

queue:

cpp 复制代码
namespace zym
{
	template<class T,class Container=deque<int>>
	class queue
	{
	public:
		void push(const T& x)
		{
			_con.push_back(x);
		}
		void pop()
		{
			_con.pop_front();
		}
		const T& front() const
		{
			return _con.front();
		}
		const T& back()
		{
			return _con.back();
		}

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

stack:

cpp 复制代码
namespace zym
{
	template<class T, class Container = deque<T>>
	class stack
	{
	public:
		void push(const T& x)
		{
			_con.push_back(x);
		}
		void pop()
		{
			_con.pop_back();
		}
		const T& top() const
		{
			return _con.back();
		}
		size_t size() const
		{
			return _con.size();
		}

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

5.priority_queue

priority_queue跟堆很像,堆是用来实现优先级队列的,所以底层还是堆的实现那些。

仿函数

这里的类Less和Greater里面只有一个成员函数,没有成员变量,但我们要建大堆和小堆时需要改变代码的大于号或者小于号,是不方便的,所以可以在priority_queue模板再加一个参数,来接受仿函数,需要小于就传Less,需要大于就传Greater,这样就可以一个代码实现大堆和小堆。

cpp 复制代码
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;
	}
};

实现代码:

cpp 复制代码
#pragma once

#include<vector>

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 zym
{
	template<class T, class Container = vector<T>, class Compare = Less<T>>
	class priority_queue
	{
	public:
		void AdjustUp(int child)
		{
			Compare com;
			int parent = (child - 1) / 2;
			while (child > 0)
			{
				if (com(_con[parent], _con[child]))
				{
					swap(_con[child], _con[parent]);
					child = parent;
					parent = (child - 1) / 2;
				}
				else
					break;
			}
		}

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

		void AdjustDown(int parent)
		{
			size_t child = parent * 2 + 1;
			Compare com;
			while (child < _con.size())
			{
				if (child + 1 < _con.size() && com(_con[child], _con[child + 1]))
				{
					++child;
				}
				if (com(_con[parent], _con[child]))
				{
					swap(_con[child], _con[parent]);
					parent = child;
					child = parent * 2 + 1;
				}
				else
					break;
			}
				
		}
		void pop()
		{
			swap(_con[0], _con[_con.size() - 1]);
			_con.pop_back();
			AdjustDown(0);
		}
		const T& top()
		{
			return _con[0];
		}

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

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

		
	private:
		Container _con;
	};
}
相关推荐
唐诺4 分钟前
几种广泛使用的 C++ 编译器
c++·编译器
黄油饼卷咖喱鸡就味增汤拌孜然羊肉炒饭8 分钟前
SpringBoot如何实现缓存预热?
java·spring boot·spring·缓存·程序员
暮湫25 分钟前
泛型(2)
java
南宫生34 分钟前
力扣-图论-17【算法学习day.67】
java·学习·算法·leetcode·图论
转码的小石42 分钟前
12/21java基础
java
高山我梦口香糖1 小时前
[react]searchParams转普通对象
开发语言·前端·javascript
李小白661 小时前
Spring MVC(上)
java·spring·mvc
m0_748235241 小时前
前端实现获取后端返回的文件流并下载
前端·状态模式
冷眼看人间恩怨1 小时前
【Qt笔记】QDockWidget控件详解
c++·笔记·qt·qdockwidget