关于线性结构的特点,请参考我的另外一篇博客:https://blog.csdn.net/black_carbon/article/details/81084932
栈:栈是限定仅在表尾进行插入或删除操作的线性表,表尾端称为栈顶,表头端称为栈底
不含元素的表称为空栈
栈的修改是按照后进先出的原则进行的,因此,栈又称为后进先出(last in first out)的线性表,简称LIFO结构
入栈:插入元素的操作为入栈
出栈:删除栈顶元素的操作为出栈
栈的表示:和线性表类似,栈也有两种存储表示方法:栈的顺序存储结构和链式存储结构
顺序栈:即栈的顺序存储结构,是利用一组地址连续的存储单元依次存放自栈底到栈顶的数据元素,同时附设指针top指示栈顶元素在顺序栈中的位置。
栈的顺序存储表示:
/*
栈的顺序存储结构
*/
# define STACK_INIT_SIZE 100
# define STACKINCREAMENT 10
typedef struct {
SElemType *base;
SElemType *top;
int stacksize;//当前已分配的存储空间,已元素为单位
}SqStack;
base为栈底指针,在顺序栈中,它始终指向栈底的位置,若base的值为NULL,则表明栈结构不存在。
top为栈顶指针,其初值指向栈底,即top=base可作为空栈的标记,入栈时,top++;出栈时,top--;
非空栈中的栈顶指针始终在栈顶元素的下一个位置上
下面列举几个对栈的基本操作:
构造一个空栈:
/*
构造一个空的顺序栈
*/
Status InitStack(SqStack &S)
{
S.base = (SElemType *)malloc(STACK_INIT_SIZE * sizeof(SElemType));
if(!S.base)
exit(OVERFLOW);
S.top = S.base;
S.stacksize = STACK_INIT_SIZE;
return OK;
}//InitStack
获取栈顶元素:
/*
获取栈顶元素
*/
Status GetTop(SqStack &S, SElemType &e)
{
//若栈不空,用e返回栈顶元素,并返回OK,否则返回ERROR
if(S.base == S.top)
return ERROR;
e = *(S.top-1);
return OK;
}//GetTop
入栈操作:
/*
入栈
*/
Status Push(SqStack &S, SElemType e)
{
//插入元素e为新的栈顶元素
if(S.top-S.base >= S.stacksize)//栈满,追加存储空间
{
S.base = (SElemType *)realloc(S.base,(S.stacksize+STACKINCREAMENT)*sizeof(SElemType));
if(!S.base)
exit(OVERFLOW);
S.stacksize += STACKINCREAMENT;
}
*S.top = e;//入栈
S.top++;
return OK;
}//Push
出栈操作:
/*
出栈
*/
Status Pop(SqStack &S, SElemType &e)
{
//若栈不空,则删除栈顶元素,用e返回其值,并返回OK,否则返回ERROR
if(S.top == S.base)
return ERROR;
--S.top;
e = *S.top;
return OK;
}//Pop
链栈:栈的链式表示
(大家觉得链栈的头指针是指向栈顶还是指向栈底)
答案是:链栈的头指针指向栈顶。为什么不是指向第一个元素栈底呢?
可以想想链式结构的特点,当访问某个结点时,需要从头指针开始逐个向后访问,而栈的特点是只在栈顶执行操作
所以,头指针指向栈顶元素就可以避免每次对栈进行操作时从栈底遍历到栈顶了,大大的减少的时间开销
关于链栈就介绍这么多,其实跟链表很相似,下面讲讲栈的应用
- 数制转换
- 括号匹配的检验
- 行编辑程序
- 迷宫求解
- 表达式求值
这里我们看一下数制转换的例子:
十进制转换成八进制:
/*
数值转换
*/
void conversion()
{
//任意输入的一个非负十进制整数,打印输出与其等值的八进制数
int number, e;
SqStack S;
InitStack(S);
cin>>number;
while(number)
{
Push(S, number%8);
number /= 8;
}
while(Pop(S, e))
cout<<e;
cout<<endl;
}
其他的实例有时间再一一整理,大家也可以自行完成。
栈还有一个重要应用是在程序设计语言中实现递归
一个直接调用自己或通过一系列的调用语句间接调用自己的函数,称做递归函数。
通常,当在一个函数的运行期间调用另一个函数时,在运行被调用函数之前,系统需要完成3件事:
- 将所有的实在参数、返回地址等信息传递给被调用函数保存
- 为被调用函数的局部变量分配存储区
- 将控制转移到被调用函数的入口
而从被调用函数返回调用函数之前,系统也应完成3件事:
- 保存被调用函数的计算结果
- 释放被调用函数的数据区
- 依照被调用函数保存的返回地址将控制转移到调用函数
当有多个函数构成嵌套调用时,按照“后调用先返回”的原则,上述函数之间的信息传递和控制转移必须通过“栈”来实现,即系统将整个程序运行时所需的数据空间安排在一个栈中,每当调用一个函数时,就为它在栈顶分配一个存储区,每当从一个函数退出时,就释放它的存储区。则当前正运行的函数的数据区必在栈顶。
欢迎各位大大指正