栈与队列

前言

注意区别,这里的栈与内存管理中的栈不是同一个概念,关于堆栈的概念请参考其他资料,本文介绍数据结构层面上的栈。

栈是一种线性数据结构,存储以及查找数据时只能访问栈的一端(这是与另外一种线性结构数组的明显区别之一),栈类似于餐厅中的一叠盘子,最后放在顶端,却可以最先被取出,因此栈被称为LIFO(last in/first out)。栈是有空间限制的,有盘子的时候才能取出盘子,放满了以后就不能再添加盘子。对于栈有如下常用操作

  • clear() ------------清空
  • isEmpty() --------是否为空
  • push(el) ----------弹出栈顶元素
  • pop() --------------弹出栈顶元素
  • topEI() ------------获取栈顶元素,但不删除该元素
    最基本的操作包括出栈和入栈操作:
操作push10push5poppush7push15
15
577
栈底1010101010

一般来说栈适用于数据存储后以相反方式检索的情形。
栈有两种较为常见的实现方式,第一种是可变数组<vector>,第二种是<list>
由于vector中常常需要空间数大于元素数,链表被视为更匹配抽象栈的数据结构。此外由于数列常常需要额外内存而将自身复制,所以最好情况下出栈入栈的复杂度都为O(1),而在最坏情况下为O(n)。
数组实现代码:

#include <vector>

template<class T, int capacity = 30>
class Stack {
public:
    Stack() {
        pool.(capacity);
    } //构造函数,翻转数组顺序。
    void clear() { 
        pool.clear();
    } //清空栈。
    bool isEmpty() const { 
        return pool.empty();
    }
    T& topEl() { 
        return pool.back();
    } //返回栈顶元素。
    T pop() { 
        T el = pool.back();
        pool.pop_back();
        return el;
    } //弹出栈顶元素。
    void push(const T& el) { 
        pool.push_back(el);
    } //插入新元素。
private:
    vector<T> pool;
};

对于链表的实现方式与之类似且更加简单。

队列

队列是一个简单的队列。在尾部增加元素队列加长,在前端删除元素时队列缩短,栈是后进先出,那么队列是先进先出(first in/first out, FIFO)。
队列的操作如下:

  • clear() ------------清空
  • isEmpty() --------是否为空
  • enqueue(el) -----在队列尾部加入元素el
  • dequeue() -------取出队列的第一个元素
  • firstEI() ------------返回队列的第一个元素,但不删除
操作enqueue10enqueue5dequeueenqueue15enqueue7
5
10515
队尾1055157

与栈相同,队列也可以通过数组还有链表的形式实现。

template<class T, int size = 100>
class ArrayQueue
{
public:
	ArrayQueue()
	{
		first = last = -1;
	}
	void enqueue(T);
	T dequeue();
	bool isFull()
	{
		return first == 0 && last == size - 1 || first == last + 1;
	}
	bool isEmpty()
	{
		return first == -1;
	}
private:
	int first, last;
	T storage[size];
}
template <class T, int size>
void ArrayQueue<T,size>::enqueue(T el)
{
	if(!isFull())
		if(last == size - 1 || last == -1)
		{
			storage[0] = el;
			last = 0;
			if(first == -1)
			first = 0;
		}
		else storage[++last] = el;
	else cout << "Full queue.\n";
}

template <class T, int size>
T ArrayQueue<T,size>::dequeue()
{
	T tmp;
	tmp = storage[first];
	if(first == last)
		last = first = -1;
	else if (first == size - 1)
	{
		first = 0;
	}
	return first++;
	return tmp;
}

以上代码是队列的数组实现形式,链表实现形式同理。
著名的排队论(queuing theory)就是队列用法得一种基本体现,例如一个银行需要处理客户的各种业务,就需要对开通服务窗口的数量进行权衡,数量太多,效率低下,数量太少,超过负荷。通常根据每分钟涌进的客户量以及每个客户所处理的时间长度决定 开放的工作窗口数量,以对堆积的负荷进行消化。而开放几个窗口(队列)会影响银行工作的效率,这就是排队论思想的具体实现之一。

优先队列

在道路上,警车,消防车,私家车,救护车会有不同的优先级,即等待队列中,P1可能会比P2优先级更高,就算P2排在队列前边,也会优先执行P1。此类情况需要一种修正队列,就是优先队列(priority queue)。
优先队列的关键在于如何实现一种有效的方法,使得出队入队操作更加快速。

STL中的栈和队列

STL中的栈容器不是重新创建的,而是对已有容器做调整。

栈:
默认底层为deque容器,也可以自选链表或向量。

stack<int> stack1; //双端队列
stack<int, vector<int>> stack2; //向量
stack<int, list<int>> stack3; //链表

队列:
默认底层为deque容器,也可以自选以链表的形式实现。
注意vector可以实现,但STL不支持这种用法,会导致编译错误。

queue<int> q1;
queue<int, list<int>> q2;

优先队列:
优先队列(priority queue)默认使用vector容器实现,用户也可以使用deque。

priority_queue<int> pq1; //大元素优先
priority_queue<int, vector<int>, greater<int>> pq2; //小元素优先
int a[]={4,5,6};
priority_queue<int> pq3(a,a+3); //大元素优先,且用[a,a+3)中的元素填充

双端队列:
双端队列(double-ended queue)是允许在两端访问的线性表。因此可以使用双向链表实现。该链表具有数据成员head以及tail。因此这种结构的成员函数与链表的基本相似,只有少许不同。

deque<int> dq1;

有趣的是STL并没有使用双向链表,而是使用指针数组,指向块或者数据数组。块的数量随需求动态变化,指针数组的大小也随之变化。双端队列示意图
该结构由指针数组,指向块或者数据数组,可以e1为起始点,往上往下均可添加新元素,同理可以同时取出head和tail节点的数据。如果一个小块向上或向下溢出,就分配新的结构。在内存上,块间不一定连续,但在map上,它们是连续的。与之前的队列结构不同的是,双端队列重载了[ ]运算符,这意味着可以向vector一样直接访问其中的某一个元素。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值