主要内容
栈的定义
栈和队列是两种重要的线性结构,与一般线性表不同,它们是操作受限的特殊线性表,主要用于辅助其他数据结构的操作和处理,基本不用于存储数据元素信息。
栈(stack)是限定仅在表尾进行插入或删除操作的线性表,即后进先出(Last In First Out,LIFO)。因此,对栈来说,表尾端有其特殊含义,称为栈顶(top),相应地,表头端称为栈底(bottom)。不含元素的空表称为空栈。
日常生活中有许多栈的例子。例如,盘子洗好从下往上叠,用的时候从上往下拿;货物进仓从里往外堆放,取货的时候从外往里取出等等。
在程序设计时,如果需要按照保存数据时相反的顺序来使用数据,则可以用栈来实现。例如,括号(字符)的嵌套,从里往外嵌套,从外往里求解;算术运算符的优先级,按一定条件入栈和出栈运算等等。
顺序栈的存储结构
顺序栈是指利用顺序存储结构实现的栈,即利用一组地址连续的存储单元依次存放自栈底到栈顶的元素。
附设指针top指向栈顶元素,指针bottom指向栈底元素。
但是,空栈时,指针top == 指针bottom;
一个元素A进栈后,指针bottom依然指向栈底(顶)元素A,但是指针top不能继续指向A了,不然就会跟判断空栈的条件冲突,所以指针top只能 +1,如此类推,指针top始终指向栈顶元素的上一个位置。
由于指针bottom始终指向栈底元素,若指针bottom的值为NULL,则表示栈结构不存在。
typedef struct
{
Elemtype *top; /*栈顶指针*/
Elemtype *bottom; /*栈底指针,同时作为栈的存储基地址*/
int stacksize; /*栈的容量*/
} SqStack;
顺序栈的各项操作
由于顺序栈的插入和删除只在栈顶进行,因此顺序栈的基本操作比顺序表要简单很多。
初始化
为顺序栈动态分配一个预定义大小(最大容量)的数组空间。
void InitStack(SqStack &S)
{
S.bottom = new Elemtype[MAXSIZE]; /*new即是动态分配的标志*/
if(!S.bottom) exit(-1); /*存储分配失败,用exit()函数退出程序*/
/*exit(0)表示程序正常退出,exit⑴/exit(-1)表示程序异常退出*/
S.top = S.bottom; /*top初始为bottom,表示空栈*/
S.stacksize = MAXSIZE; /*设置栈长*/
}
入栈
在栈顶插入元素。
在下面这段代码中,有一个容易出错的误区:*S.top++ = e; 。
问题1:*S.top++ = e; 能否改写成 S.top++ = &e;
问题2:*S.top++ = e; 有没有可能出现阅读二义性,如果有,那会是怎样的
答:
1)不能写成 S.top++ = &e; 。若取e的地址赋给指针top,指针top将由指向栈变为指向栈外的一个变量,指针top再+1也不能回到栈上了;
2)不能将语句理解为 *S.top = e; *S.top++; ,即最终效果为 *S.top = ++e; 。
void Push(SqStack &S, Elemtype e)
{
if(S.top - S.bottom == stacksize) exit(1); /*栈满,程序异常退出*/
*S.top++ = e; /*自增运算符++在后,先使*S.top = e; 再S.top++*/
}
出栈
删除栈顶元素。
void Pop(SqStack &S, Elemtype &e)
{
if(S.top == S.bottom) exit(-1);
e = *--S.top; /*自减运算符--在前,先移动指针,再赋值给e*/
/*因为在程序中改变了e的引用,所以不需要返回任何值*/
}
取栈顶元素
当栈非空时,此操作返回当前栈顶元素的值,栈顶指针保持不变。
Elemtype Gettop(SqStack S)
{
if(S.top != S.bottom) /*栈非空*/
return *(S.top-1) /*返回栈顶元素的值,栈顶指针不变,所以需要括号*/
}