[C++]stack queue的介绍及模拟实现

本文介绍了C++中的stack和queue容器适配器,包括它们的定义方式、常用操作和模拟实现。重点讨论了如何使用stack实现队列和队列实现栈,以及栈和队列在OJ训练中的应用,如最小栈和逆波兰表达式求值。
摘要由CSDN通过智能技术生成

目录

C++:stack queue的介绍及模拟实现

                                        stack:

                                                stack的定义方式

                                                stack的使用                                        queue:

                                                queue的定义方式

                                                queue的使用

                                        stack queueOJ训练:

                                                最小栈

                                                栈的弹出压入序列

                                                逆波兰表达式求值

                                                用栈实现队列

                                                用队列实现栈

                                        stack的模拟实现

                                        queue的模拟实现

                                        容器适配器


C++:stack queue的介绍及模拟实现

stack:

1.stack是一种容器适配器,专门用在具有后进先出操作的环境中,其删除只能从容器的一端进行元素的插入与提取操作。

2.stack是作为容器适配器被实现的,容器适配器即是对特定类封装作为其底层的容器,并提供一组特定的成员函数来访问其元素,元素特定容器的尾部(即栈顶)被压入和弹出。

3.stack的底层容器可以是任何标准的容器类模板或者是一些其他特定的容器类,这些容器应该支持以下操作:

empty判空
back获取尾部元素
push_back尾部插入元素
pop_back尾部删除元素

4.标准容器vector、deque、list均符号要求,默认情况下,如果没有为stack指定特定的底层容器,默认情况下使用deque。

stack的定义方式

方式一:使用默认的适配器定义栈

stack<int> st1;

方式二:使用特定的适配器定义栈

stack<int, vector<int>> st2;
stack<int, list<int>> st3;

stack的使用

stack当中常用的成员函数如下:

成员函数功能
empty判断栈是否为空
size获取栈中有效元素个数
top获取栈顶元素
push元素入栈
pop元素出栈
swap交换两个栈中的数据

示例:

#include<iostream>
using namespace std;
#include<stack>
#include<vector>
int main()
{
	stack<int, vector<int>> st;
	st.push(1);
	st.push(2);
	st.push(3);
	st.push(4);
	cout << st.size() << endl;
	while (!st.empty())
	{
		cout << st.top() << " ";
		st.pop();
	}
	cout << endl;
	return 0;
}

queue:

1.队列是一种容器适配器,专门用于在FIFO环境操作,其中从容器一端插入元素,另一端提取元素

2.队列作为容器适配器实现,容器适配器即将特定容器类封装作为其底层容器类,queue提供一组特定成员函数来访问其元素,元素从队尾入队列,从队头出数列。

3.底层容器可以是标准容器类模板之一,也可以是其他专门设计的容器类,该底层容器应至少支持以下操作:

empty检测队列是否为空
size返回队列中有效元素的个数
front返回队头元素的引用
back返回队尾元素的引用
push_back在队列尾部入队列
pop_front在队列头部出数列

4.标准容器类deque和list满足了这些要求。默认情况下,如果没有为queue实例化指定容器类,则使用标准容器deque。

queue的定义方式

方式一:使用默认的适配器定义队列

queue<int> q1;

方式二:使用特定的适配器定义队列

queue<int, list<int>> q2;

queue的使用

成员函数功能
empty判断队列是否为空
size获取队列中有效元素个数
front获取队头元素
back获取队尾元素
push队尾入数列
pop队头出数列
swap交换两个队列中的数据

示例:

#include<iostream>
using namespace std;
#include<stack>
#include<list>
#include<queue>
int main()
{
	queue<int, list<int>> q;
	q.push(1);
	q.push(2);
	q.push(3);
	q.push(4);
	cout << q.size() << endl;
	while (!q.empty())
	{
		cout << q.front() << " ";
		q.pop();
	}
	cout << endl;
	return 0;
}

stack queueOJ训练:

最小栈

class MinStack
{
public:
	MinStack()
	{
		//调用默认构造函数即可
	}

	void push(int val)
	{
		_st.push(val);
		if (_minst.empty() || val <= _minst.top())
		{
			_minst.push(val);
		}
	}

	void pop()
	{
		if (_minst.top() == _st.top())
		{
			_minst.pop();
		}
		_st.pop();
	}

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

	int getMin()
	{
		return _minst.top();
	}
private:
	stack<int> _st;
	stack<int> _minst;
};

栈的弹出压入序列

class Solution
{
public:
	bool IsPopOrder(vector<int>& pushV, vector<int>& popV)
	{
		stack<int> st;
		size_t popi = 0;
		for (auto e : pushV)
		{
			st.push(e);
			//跟出栈序列比较 能匹配就持续输出
			while (!st.empty() && st.top() == popV[popi])
			{
				st.pop();
				++popi;
			}
		}
		return st.empty();
	}
};

逆波兰表达式求值

class Solution 
{
public:
    int evalRPN(vector<string>& tokens) 
    {
        stack<int> st;
        for(auto str : tokens)
        {
            if(str == "+" || str == "-" || str == "*" || str == "/")
            {
                int right = st.top();
                st.pop();
                int left = st.top();
                st.pop();

                switch(str[0])
                {
                case '+':
                    st.push(left + right);
                    break;
                case '-':
                    st.push(left - right);
                    break;
                case '*':
                    st.push(left * right);
                    break;
                case '/':
                    st.push(left / right);
                    break;
                }
            }
            else
            {
                st.push(stoi(str));
            }
        }
        return st.top();
    }
};

用栈实现队列

class MyQueue 
{
public:
	MyQueue() 
    {
		//使用默认构造函数即可
	}

	void push(int x) 
    {
		_pushST.push(x); //入数据直接对pushST进行压栈
	}

	int pop() 
    {
		int front = peek(); //获取popST栈顶元素,即“队头”元素
		_popST.pop(); //将popST栈顶元素删除,即删除“队头”元素
		return front; //返回删除的元素
	}

	int peek() 
    {
		if (_popST.empty()) //如果popST为空,则先将pushST当中的全部元素压入popST
		{
			while (!_pushST.empty())
			{
				_popST.push(_pushST.top());
				_pushST.pop();
			}
		}
		return _popST.top(); //返回popST栈顶元素,即“队头”元素
	}

	bool empty() {
		return _pushST.empty() && _popST.empty(); //pushST和popST同时为空,则“队列”为空
	}
private:
	stack<int> _pushST;
	stack<int> _popST;
};

用队列实现栈

class MyStack 
{
public:
	MyStack() 
	{
		//使用默认构造函数即可
	}

	void push(int x) {
		//往非空的队列入数据
		if (!_q1.empty()) 
		{
			_q1.push(x);
		}
		else
		{
			_q2.push(x);
		}
	}

	int pop() 
	{
		queue<int>* emptyQ = &_q1;
		queue<int>* nonemptyQ = &_q2;
		if (!_q1.empty())
		{
			swap(emptyQ, nonemptyQ);
		}
		//将非空队列的前n-1个元素导入空队列
		while (nonemptyQ->size() > 1)
		{
			emptyQ->push(nonemptyQ->front());
			nonemptyQ->pop();
		}
		int top = nonemptyQ->front();
		nonemptyQ->pop();
		return top; 

	}

	int top() 
	{
		if (!_q1.empty())
		{
			return _q1.back();
		}
		else
		{
			return _q2.back();
		}
	}

	bool empty() 
	{
		return _q1.empty() && _q2.empty(); 
	}
private:
	queue<int> _q1;
	queue<int> _q2;
};

stack的模拟实现

namespace wjq
{
	template <class T, class Container = std::deque<T>>
	class stack
	{
	public:
		bool empty()const
		{
			return _con.empty();
		}
		void push(const T& x)
		{
			_con.push_back(x);
		}
		void pop()
		{
			_con.pop_back();
		}
		size_t size()const
		{
			return _con.size();
		}
		const T& top()const
		{
			return _con.back();
		}
	private:
		Container _con;
	};
}

queue的模拟实现

namespace wjq
{
	template <class T, class Container = std::deque<T>>
	class queue
	{
	public:
		bool empty()const
		{
			return _con.empty();
		}
		void push(const T& x)
		{
			_con.push_back(x);
		}
		void pop()
		{
			_con.pop_front();
		}
		size_t size()const
		{
			return _con.size();
		}
		const T& front()const
		{
			return _con.front();
		}
		const T& back()const
		{
			return _con.back();
		}
	private:
		Container _con;
	};
}

容器适配器

什么是适配器?

适配器是一种设计模式,设计模式是一套被反复使用的,多数人知晓的,经过分类编目的,代码设计经验的总结,该种模式是将一个类的接口转换成客户希望的另外一个接口

deque的原理介绍:

deque(双端队列):是一种双开口的连续空间的数据结构,双开口的含义是:可以在头尾两端进行插入和删除操作,且时间复杂度为O(1),与vector比较,头插效率高,不需要搬移元素,与list比较,高速缓存利用率高,

deque并不是真正连续的空间,而是由一段段连续的小空间拼接而成,实际deque类似于一个动态的二维数组,其底层结构如下图所示:

双端队列底层是一段假想的连续空间,实际上却是分段连续的,为了维护其"整体连续"以及随机访问的假象,落在了deque的迭代器身上,因此deque的迭代器设计就比较复杂,如下图:

deque的缺陷:

与vector比较,deque的优势是:头部插入和删除时,不需要搬移元素,效率特别高,而且在中控数组扩容时,也不要搬移大量元素,因此其效率是比vector高的。

与list比较,其底层是连续空间,高速缓存利用率比较高,不需要存储额外字段

但是deque也有很大的缺陷:

1.不适合遍历和排序

因为在遍历时,deque的迭代器要频繁的去检测其是否移动到某段小空间的边界,导致效率低下。

void TestOP()
{
	srand(time(0));
	const int N = 1000000;
	vector<int> v;
	v.reserve(N);
	deque<int> dq;

	for (int i = 0; i < N; ++i)
	{
		int e = rand();
		v.push_back(e);
		dq.push_back(e);
	}
	int begin1 = clock();
	sort(v.begin(), v.end());
	int end1 = clock();

	int begin2 = clock();
	sort(dq.begin(), dq.end());
	int end2 = clock();

	printf("vector sort:%d\n", end1 - begin1);
	printf("deque sort:%d\n", end2 - begin2);
}

2.中间插入,删除效率不如list

从deque的底层结构图中可以看出,中间插入、删除数据仍会产生数据的挪动。deque中间插入、删除数据的速度不如list。

3.随机访问速度不如vector

由于deque的中控数组中指向的一段段地址空间之间并不连续,所以随机访问时需要计算目标数据处于哪段buffer中的第几个数据,所以deque的随机访问速度并没有vector快。

为什么选择deque作为stack和queue的底层默认容器?

stack是一种后进先出的特殊线性数据结构,因此只要具有push_back()和pop_back()操作的线性结构都可以作为stack的底层容器,比如vector和list都可以,queue是先进先出的特殊线性数据结构,只要具有push_back和pop_front操作的线性结构都可以作为queue的底层容器,比如list,但是STL中对stack和queue默认选择deque作为其底层容器,主要是因为:

1.stack和queue不需要遍历,因此stack和queue没有迭代器,只需要在固定的一端或者两端进行操作

2.在stack中进行元素插入时,deque比vector的效率高,扩容时不需要搬移大量数据,结合了deque的优点而又完美的避开了它的缺陷。

  • 14
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 9
    评论
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值