最近在复习数据结构(我用的是数据结构与算法分析c++描述 第三版的书,很不错的书,有兴趣的可以去看看)的相关知识,顺便就做了些整理,以供参考。
栈(stack)是限制插入和删除操作只能在一个位置进行的表,该位置是表的末端,称为栈的顶。对栈的操作是push(入栈)和pop(出栈),前者相当于插入,后者则是删除最后插入的元素。最后插入的元素可以通过使用top方法在执行pop之前进行访问。对空栈的pop或top操作,在栈ADT中一般认为是一个错误。另一方面,当运行push时超出栈空间的最大内存时候,这是栈在实现过程中所受的限制,并不是ADT错误。
下面打个比方,以便于更好理解栈。其实对于栈,是一种后进先出的表,我们可以想象这样一种现实中的场景,当汽车依次进入了一个狭窄的巷子的时候,突然发现前面有个障碍物挡住了道路,那么这个时候该怎么办?显而易见,这个时候先进去的车辆是无法挪动的,只有在最后的车辆可以通过倒车而进行行驶,当最后一辆车出去后,相应的倒数第二辆车才可以出去,一直到最后第一辆车才有可能出了这个巷子,这也正是我们的栈的工作原理。
例如,将元素6、3、7、6、2依次插入到栈中的话,则栈的示意图如下:
由于栈是是一个表,因此任何实现表的方法都可以用来实现栈。其实栈就是对我们比较熟悉的表的操作进行了限制,对数组来说,我们可以通过下标来访问数组中的任意一个元素,而如果我们想数组表现出栈的特性,那么很容易做到的,只需要对数组进行包装,封装成一个类,该类只有显著的push和pop操作,也就是对数组的下标操作做一些限制,每次只能对数组中的最后位置进行操作(这里的最后位置指的是存放有元素的最大下标的位置)。在实际中,很明显list和vector支持栈操作,99%的情况下,它们都是合理的选择。当然对于栈的实现的选择,需要具体情况具体分析,有时候为了特殊目的而设计的实现可以运行的更快。由于栈的所有操作都是常量时间的操作,所以,除非是在很特别的环境下,否则不太可能有明显的改进。
当前两个比较流行的栈的实现方式,一个是使用链表结构,另一个则是使用数组。
1、栈的链表实现
栈的一种实现方法是使用单向链表。我们通过在表顶端插入元素来实现push,通过删除表顶端的元素实现pop。top操作知识访问表顶端的元素并返回它的值。有时pop操作 和top操作可以合二为一。这里面为什么说要将元素放在表的顶端而不是末尾或其它位置呢?这个是因为在栈中我们只关心栈定的元素,那么这个栈顶元素的访问效率问题就必须要考虑了,所以将元素放在链表的顶端,可以通过链表的头指针直接进行访问,这个访问时间是常量,而如果放在末尾,那么则需要遍历链表找到栈顶的元素,显然浪费了访问的时间,这是不可取的。
2、栈的数组实现
另一种可选的实现避免了使用链并且可能是更流行的解决方案。由于使用vector中的back、push_back、pop_back方法,实现栈特别容易。更一般的方法是使用数组进行栈的构建,下面给出一个基本的示例:
#include<iostream> using namespace std; #define MAXSIZE 20 template <typename Object> class CStack { public: CStack():m_topOfStack(-1){} bool empty() const; bool pop(); bool top(Object &item) const; bool push(const Object &item); private: Object m_theArray[MAXSIZE]; int m_topOfStack; }; template <typename Object> bool CStack<Object>::empty() const { if(m_topOfStack==-1) return true; return false; } template <typename Object> bool CStack<Object>::pop() { if(m_topOfStack==-1) return false; m_topOfStack--; return true; } template <typename Object> bool CStack<Object>::top(Object &item) const { if(m_topOfStack==-1) return -1; item=m_theArray[m_topOfStack]; return true; } template <typename Object> bool CStack<Object>::push(const Object &item) { if(m_topOfStack>=MAXSIZE-1) return false; m_theArray[++m_topOfStack]=item; }在栈中,这些操作都是以常数时间运行的,而且是以非常快的常数时间运行。在某些机器上,若在带有自增和自减寻址功能的寄存器上操作,则(整数)push和pop都可以写成一条指令。最现代的计算机将栈操作作为其指令系统的一部分,这个事实强化了这样一种思想,即在计算机科学中,栈很可能是继数组之后的最基本的数据结构。