C++抽象编程——STL(2)——stack类

来源:互联网 发布:淘宝客内部优惠券 编辑:程序博客网 时间:2024/05/20 04:49

The Stack class

本来我是打算把vector作为第一个总结的部分的。但是我们今天的数据结构老师刚刚讲到了stack(栈)这么一个结构体,对其内部的方法有了更为深入的了解,所以我就先把stack和queen(队列)放在前面总结。

当我们根据一个类的操作数进行测量的时候,最简单的收集类就是stack类了。撇开它的简便性不谈,它常常在很多的编程应用上面被证明是极为有用的。从概念上来说,一个栈提供了为一组数组存储的空间,但受限于值必须以与添加它们相反的顺序从堆栈中移除(即它们加进来的地方)。 这个限制意味着添加到栈的最后一个元素始终是被移除的第一个项目。
鉴于它们在计算机科学中的重要性,stack有一个属于它们自己术语。将一个新的值添加到栈称为推送(push)该值; 删除堆栈中的最顶层的值称为弹出(pop)栈。以后我们就直接用英文来表示了。在stack中这种的顺序处理方式我们通常称为LIFOMoreover, the order inwhich stacks are processed is sometimes called LIFO, which stands for “last in,first out.”
一个通俗的(但可能不严谨的)解释,就对于stack,push和pop的通俗解释是栈模型的存储模式是在自助餐厅的方式得出的。假设你在一个自助餐厅,客人在自助餐线上开始时拿起自己的盘子,那些盘子就放在弹簧柱上,这样子是为了方便顾客从自助餐线上拿起顶部的盘子,就像下图的模式那样:
增加盘子前
当洗碗机添加新的盘子时,它会放在堆叠的顶部,推动弹簧的压缩,其他的略微下降,如图所示:
增加盘子后
对于顾客来说,他们只能从stack的顶部取走盘子,一旦当他们这么做之后,其余的盘子就会弹回原来的位置上去。因此,最后加进来的盘子就是第一个被取走的盘子。
stack在编程中如此重要还有一个小原因,那就是嵌套函数(nested function)的调用形式就是以这种方式进行的。举个例子,如果main函数中我们调用f函数,那么一个f函数的栈框架就会放在main函数栈框架的顶部,就像这样:
main函数调用f函数
如果f函数中还调用了g函数,那么就会在f的栈框架(stack frame)的上方为g函数开辟一个全新的栈框架。就像这样:
f调用g
当g函数返回的时候,它的栈结构就会被pop回f函数中,同样的f函数也会pop会main函数中,最终返回初始的模式。
所以总结起来就是:

调用顺序(入栈):main -> f()->g();返回顺序(出栈):g() -> f() ->main;

The structure of the Stack class

跟以后的vector一样,stack也是一个需要指定元素类型的收集类。例如stack< int >代表一个全是整型数字的栈,stack< string >代表的就是一个全是字符串的一个stack。同样的,如果你想定义一个Plate和Frame类,你可以用Stack< Plate >Stack< Frame >.来为它们的对象建立一个栈。下图就是stack接口出口的一些常用的方法了:
这里写图片描述
具体的一些常见的用法如下:

方法 用法 pop() 移除栈顶元素,但是不返回它的值 top() 返回栈顶的值,但是不移除 push() 添加一个元素到栈顶 size() 返回栈中的元素个数 empty() 判断栈中的元素是否为空,返回的为bool类型 emplace() 向栈的首位置插入一个元素

这些我们都可以通过在编译器中点击右键,跳到声明或者是定义去了解,就比如:
pop
我们就可以知道,这个函数的返回类型为void,而且功能为去除栈中最后的一个元素,不返回它的值。至于为什么,来一段官方的解释
One might wonder why pop() returns void, instead of value_type. That is, why must one use top() and pop() to examine and remove the top element, instead of combining the two in a single member function? In fact, there is a good reason for this design. If pop() returned the top element, it would have to return by value rather than by reference: return by reference would create a dangling pointer. Return by value, however, is inefficient: it involves at least one redundant copy constructor call. Since it is impossible for pop() to return a value in such a way as to be both efficient and correct, it is more sensible for it to return no value at all and to require clients to use top() to inspect the value at the top of the stack.

功能拓展

在学习《programming abstraction in C++》这本书的时候,一直强调函数的复用与扩展。所以他们为了教学提供了很多方便教学的函数。其实我们也知道我们可以用上面的方法写一些常用的。很好用的函数。下面的是我自己写的一些好用的函数:

/*移除并返回栈顶的元素*/int popAndReturn(stack<int> & operandStack) {    int n = operandStack.top();    operandStack.pop();    return n;    }/*清空栈顶的元素*/void clear(stack<int> & operandStack) {    while(!operandStack.empty()) {    operandStack.pop();    }}

更多的应用可以参考我的下一篇文章: C++抽象编程——STL(2)——括号匹配与口袋计算器

其他

因为我们今天讲了一下stack的数据结构,所以在这里谈一下push跟pop的具体实现,代码如下:

Status Push( SqStack &S, SElemType e)  {    if( S.top - S.base== S.stacksize ) // 栈满        return ERROR;         /*这下面才是重点*/     *S.top=e;    S.top++;    return OK;}

这里我们不管类型先,我们只要知道top是指向栈顶的元素上端的指针,随着元素的增加,它一直往上移动,而对于base指针它一直指向最后的一个元素。所以push一个值(假设stack未满),就是把要push的值(假设为e)赋值给top指针指向的位置,然后top指针自增1(即再往上移一位)。
那么对于pop方法,也是一样的:

Status Pop( SqStack &S, SElemType &e)  {    if( S.top == S.base ) // 栈空    return ERROR;         /*这下面才是重点*/     --S.top;    e=*S.top;    return OK;}

这里我们注意到,指针先自减1(即指针后移一位),然后再把减后的值赋给e,这个时候仅仅是指针向下移动了一位,但是原栈顶的元素(假设为a)没有改变,并不是说就消失了,这个时候如果把指针往上移动一位,那么栈顶的元素还是a,就是这个意思。

ps:下一篇我再写stack的一些运用例子吧

0 0
原创粉丝点击