一、定义
1、栈是限定仅在表尾进行插入或删除操作的线性表,因此,对栈来说,表尾端有特殊的含义,称为栈顶,表头端称为栈底,不含元素的空表称为空栈。
2、栈后进先出
3、栈和队列是限定插入和删除只能在表的“端点”进行的线性表。
二、顺序栈
1、栈的顺序存储结构是利用一组地址连续的存储单元依次存放自栈底到栈顶的数据元素,栈在使用的过程中所需的最大空间的大小很难估计,可以先为栈分配一个基本容量,当栈的空间不够使用时再扩大。
2、栈为空的时候,top和base指向同一个指针。非空的时候,栈顶指针top始终在栈顶元素的下一个位置上。
三、顺序栈的基本操作
#include <bits/stdc++.h>
#define STACK_INIT_SIZE 100//这一部分和顺序表的基本相同
#define STACKINCREMENT 10
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define OVERFLOW -2
typedef int Status;
typedef int SElemType;
typedef struct
{
SElemType *base;
SElemType *top;//栈顶指针
int stacksize;//允许的最大存储空间以元素为单位;
}SqStack;
这里在typedef struct 中虽然没有int length;
但是,S.length=S.top-S.base;
因此根本不需要对length进行定义;
1、顺序栈的初始化
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;
}
这里与顺序表中初始化方式类似,都是预先分配一个大约的空间,然后对表中的元素进行赋值。
2、返回栈顶元素
Status GetTop(SqStack S,SElemType &e)
{
if(S.top==S.base) return ERROR;
e=*(S.top-1);
return OK;
}
首先要判断栈是否为空的,如果不是空的才能返回栈顶元素;
3、向栈顶存入元素
Status Push(SqStack &S,SElemType e)
{
if(S.top-S.base>=S.stacksize)//顺序表中插入元素的时候还要判断位置是否合格,但是这里不需要判断,因为只能在栈顶插入元素;
{
S.base=(SElemType*)realloc(S.base,(S.stacksize+STACKINCREMENT)*sizeof(SElemType));
if(!S.base) exit(OVERFLOW);
S.top=S.base+S.stacksize;
S.stacksize+=STACKINCREMENT;
}
*S.top++=e;//先存入元素,之后指针再指向所存地址只上;
return OK;
}
(1)在顺序表中插入元素的时候还要判断位置是否合格,但是,在栈中插入元素的时候,不需要判断,因为只能在栈顶插入元素。
(2)对S.base进行重新分配空间,这个时候存储空间增大了,并且S.base的位置改变。所以,在S.base改变位置之后, S.top要紧跟着S.base进行改变。之所以还是加S.stacksize是因为S.stacksize的大小实际上还没改变,S.base与 S.top的相对位置不能改变。在确定好 S.top的位置之后,再对S.stacksize的大小进行改变。
(3)S.top-S.base等同于S.length。
(4)*S.top++=e;这一步在if语句之外,不要忘记了;
4、栈顶元素的删除
Status Pop(SqStack &S,SElemType &e)
{
if(S.top==S.base) return ERROR;
e=*--S.top;
return OK;
}
(1)e=*–S.top的意思是先变位置,之后再赋值。
四、链栈(相当于没有头结点的链表)
1、
2、
#include <bits/stdc++.h>
#define STACK_INIT_SIZE 100//这一部分和顺序表的基本相同
#define STACKINCREMENT 10
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define OVERFLOW -2
typedef int Status;
typedef int SElemType;
typedef struct Snode
{
SElemType data;
struct Snode *next;
}Snode,*Slink;
3、链栈插入元素(相当于插入到没有头结点的链表的头上)
Status Push(Slink L,SElemType x)
{
Slink p;
p=(Slink)malloc(sizeof(Snode));//建立新的结点;
if(!p) exit(OVERFLOW);
p->data=x;
p->next=L;// 链接到原来的栈顶
L=p;// 移动栈顶指针
return OK;
}
4、链栈删除元素
与链表的不同点是:
(1)没有头结点;
(2)已经明确要删除的位置;
(3)在删除前要判断是否非空;
Status Pop(Slink L,SElemType &x)
{
Slink p;
if(!L) return ERROR;//在删除的时候,要先判断链栈是否为空;
else
{
x=L->data;
p=L;
L=L->next;
free(p);
}
return OK;
}
五、顺序栈的应用举例
1、数制转换
#include <bits/stdc++.h>
#define STACK_INIT_SIZE 100//这一部分和顺序表的基本相同
#define STACKINCREMENT 10
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define OVERFLOW -2
typedef int Status;
typedef int SElemType;
typedef struct
{
SElemType *base;
SElemType *top;//栈顶指针
int stacksize;//允许的最大存储空间以元素为单位;
}SqStack;
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;
}
Status Push(SqStack &S,SElemType e)
{
if(S.top-S.base>=S.stacksize)//顺序表中插入元素的时候还要判断位置是否合格,但是这里不需要判断,因为只能在栈顶插入元素;
{
S.base=(SElemType*)realloc(S.base,(S.stacksize+STACKINCREMENT)*sizeof(SElemType));
if(!S.base) exit(OVERFLOW);
S.top=S.base+S.stacksize;
S.stacksize+=STACKINCREMENT;
}
*S.top++=e;//先存入元素,之后指针再指向所存地址只上;
//printf("%d",e);
return OK;
}
Status Empty(SqStack S)
{
if(S.top==S.base) return TRUE;
else return FALSE;
}
Status Pop(SqStack &S,SElemType &e)//这里当时在e前面忘记了&符号
{
if(S.top==S.base) return ERROR;
e=*--S.top;
return OK;
}
void conversion()
{
SqStack S;
SElemType e;
InitStack(S);
int n;
scanf("%d",&n);
while(n)
{
Push(S,n%8);
n=n/8;
}
while(!Empty(S))
{
Pop(S,e);
printf("%d",e);
}
}
int main()
{
conversion();
return OK;
}
2、括号匹配的检验
假设在表达式中
([]())或[([ ][ ])]等为正确的格式,[( ])或([( ))或 (()])均为不正确的格式,则 检验括号是否匹配的方法可用“期待的急迫程度”这个概念来描述。