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

目录

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

                                        stack:

                                                stack的定义方式

                                                stack的使用                                        queue:

                                                queue的定义方式

                                                queue的使用

                                        stack queueOJ训练:

                                                最小栈

                                                栈的弹出压入序列

                                                逆波兰表达式求值

                                                用栈实现队列

                                                用队列实现栈

                                        stack的模拟实现

                                        queue的模拟实现

                                        容器适配器


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

stack:

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

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

[C++]stack queue的介绍及模拟实现_第1张图片

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

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

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

[C++]stack queue的介绍及模拟实现_第2张图片

stack的定义方式

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

stack st1;

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

stack> st2;
stack> st3;

stack的使用

[C++]stack queue的介绍及模拟实现_第3张图片

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

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

示例:

#include
using namespace std;
#include
#include
int main()
{
	stack> 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;
}

[C++]stack queue的介绍及模拟实现_第4张图片

queue:

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

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

[C++]stack queue的介绍及模拟实现_第5张图片

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

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

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

queue的定义方式

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

queue q1;

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

queue> q2;

queue的使用

[C++]stack queue的介绍及模拟实现_第6张图片

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

示例:

#include
using namespace std;
#include
#include
#include
int main()
{
	queue> 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;
}

[C++]stack queue的介绍及模拟实现_第7张图片

stack queueOJ训练:

最小栈

[C++]stack queue的介绍及模拟实现_第8张图片

[C++]stack queue的介绍及模拟实现_第9张图片

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 _st;
	stack _minst;
};

栈的弹出压入序列

[C++]stack queue的介绍及模拟实现_第10张图片

[C++]stack queue的介绍及模拟实现_第11张图片

class Solution
{
public:
	bool IsPopOrder(vector& pushV, vector& popV)
	{
		stack 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();
	}
};

逆波兰表达式求值

[C++]stack queue的介绍及模拟实现_第12张图片

[C++]stack queue的介绍及模拟实现_第13张图片

class Solution 
{
public:
    int evalRPN(vector& tokens) 
    {
        stack 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();
    }
};

[C++]stack queue的介绍及模拟实现_第14张图片

用栈实现队列

[C++]stack queue的介绍及模拟实现_第15张图片

[C++]stack queue的介绍及模拟实现_第16张图片

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 _pushST;
	stack _popST;
};

用队列实现栈

[C++]stack queue的介绍及模拟实现_第17张图片

[C++]stack queue的介绍及模拟实现_第18张图片

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

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

	int pop() 
	{
		queue* emptyQ = &_q1;
		queue* 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 _q1;
	queue _q2;
};

stack的模拟实现

namespace wjq
{
	template >
	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;
	};
}

[C++]stack queue的介绍及模拟实现_第19张图片

queue的模拟实现

namespace wjq
{
	template >
	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;
	};
}

[C++]stack queue的介绍及模拟实现_第20张图片

容器适配器

什么是适配器?

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

deque的原理介绍:

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

[C++]stack queue的介绍及模拟实现_第21张图片

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

[C++]stack queue的介绍及模拟实现_第22张图片

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

[C++]stack queue的介绍及模拟实现_第23张图片

[C++]stack queue的介绍及模拟实现_第24张图片

deque的缺陷:

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

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

但是deque也有很大的缺陷:

1.不适合遍历和排序

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

void TestOP()
{
	srand(time(0));
	const int N = 1000000;
	vector v;
	v.reserve(N);
	deque 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);
}

[C++]stack queue的介绍及模拟实现_第25张图片

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的优点而又完美的避开了它的缺陷。

你可能感兴趣的:(c++,开发语言)