堆栈
堆栈
堆栈的抽象数据类型描述
类型名称:堆栈(Stack)
对象数据集:一个有0个或多个元素的有穷线性表
操作集:长度为MaxSize的堆栈\(S\in Stack\),堆栈元素\(item\in ElementType\)
- Stack CreateStack(int MaxSize):生成空堆栈,其最大长度为MaxSize;
- int IsFull(Stack S, int MaxSize):判断堆栈\(S\)是否已满;
- void Push(Stack S, ElementType item):将元素\(item\)压入堆栈;
- int IsEmpty(Stack S):判断堆栈\(S\)是否为空;
- ElementType Pop(Stack S):删除并返回栈顶元素;
栈的顺序存储实现
栈的顺序存储结构通常由一个一维数组和一个记录栈顶元素位置的变量组成
#define MaxSize <存储数据元素的最大个数>
typedef struct SNode *Stack;
struct SNode{
ElementType Data[MaxSize];
int Top;
};
入栈
void Push(Stack PtrS, ElementType item){
if (PtrS->Top == MaxSize-1){
printf(" 堆栈满 ");
return;
}
else{
PtrS->Data[++(PtrS->Top)] = item;
return;
}
}
出栈
ElementType Pop(Stack PrtS){
if (PtrS->Top == -1){
printf(" 堆栈空 ");
return ERROR;//ERROR是ElementType的特殊值
}
else
return (PtrS->Data[(PtrS->Top)--]);
}
例子
用一个数组实现两个堆栈,要求最大地利用数组空间,使数组只要有空间入栈操作就可以成功
一种比较聪明的方法是使这两个栈分别从数组的两头开始向中间生长;当两个栈的栈顶指针相遇时,表示两个栈都满了
#define MaxSize <存储数据元素的最大个数>
struct DStack{
ElementType Data[MaxSize];
int Top1;//堆栈1的栈顶指针
int Top2;//堆栈2的栈顶指针
}S;
S.Top1=-1;
S.Top2=MaxSize;
void Push(struct DStack *PtrS, ElementType item, int Tag){
if (PtrS->Top2 - PtrS->Top1 == 1){//堆栈满
printf(" 堆栈满 ");
return;
}
if (Tag == 1)//对第一个堆栈进行操作
PtrS->Data[++(PtrS->Top1)] = item;
else //对第二个堆栈进行操作
PtrS->Data[--(PtrS->Top2)] = item;
}
ElementType Pop(struct DStack *PtrS, int Tag) {//Tag作为区分两个堆栈的标志
if (Tag == 1) {//对第一个堆栈操作
if (PtrS->Top1 == -1) {//堆栈1空
printf(" 堆栈1空 ");
return NULL;
} else
return PtrS->Data[(PtrS->Top1)--];
} else {//对第二个堆栈操作
if (PtrS->Top2 == MaxSize) {//堆栈2空
printf(" 堆栈2空 ");
return NULL;
} else
return PtrS->Data[(PtrS->Top2)++];
}
}
堆栈的链式存储实现
栈的链式存储结构实际上就是一个单链表,叫做链栈。插入和删除操作只能在链栈的栈顶进行
栈顶的指针Top应该在链表的头上,因为如果在尾巴上,因为是单向链表,删除操作时找不到前面一个节点
typedef struct SNode *Stack;
struct SNode{
ElementType Data;
struct SNode *Next;
};
(1)堆栈初始化(建立空栈)
(2)判断堆栈S是否为空
Stack CreateStack(){
Stack S;//构建一个堆栈的头结点,返回指针
S = (Stack)malloc(sizeof(struct SNode));
S->Next = NULL;
return S;
}
int IsEmpty(Stack S){
//判断堆栈S是否为空,若为空,函数返回整数1,否则返回0
return (S->Next == NULL);
}
void Push(ElementType item, Stack S){
//将元素item压入堆栈S
struct SNode *TmpCell;
TmpCell = (struct SNode *)malloc(sizeof(struct Snode));
TmpCell->Data = item;
TmpCell->Next = S->Next;
S->Next = TmpCell;
}
ElementType Pop(Stack S){
//删除并返回栈顶S的栈顶元素
struct SNode *FirstCell;
ElementType TopElem;
if (IsEmpty(S)){
printf(" 堆栈空 ");
return NULL;
}
else{
FirstCell = S->Next;
S->Next = FirstCell->Next;
TopElem = FristCell->Data;
free(FirstCell);
return TopEle
}
}
堆栈应用:表达式求值
- 运算数:入栈
- 运算符:从堆栈中弹出适当数量的运算数,计算并结果入栈;
- 最后,栈顶上的元素就是表达式的结果值
基本策略:将中缀表达式转换为后缀表达式,然后求值
- 运算数相对顺序不变
- 运算符号顺序发生改变
- 需要存储“等待中”的运算符号
- 要将当前运算符号与“等待中”的最后一个运算符号比较
中缀表达式如何转换为后缀表达式
- 运算数:直接输出
- 左括号:压入堆栈
- 有括号:将栈顶的运算符弹出并输出,直到遇到左括号(出栈,不输出);
- 运算符:
- 若优先级大于栈顶运算符时,则把它压栈;
- 若优先级小于等于栈顶运算符时,将栈顶运算符弹出并输出;再比较新的栈顶运算符,直到该运算符大于栈顶运算符优先级为止然后将该运算符压栈;
- 若个对象处理完毕,则把堆栈中存留的运算符一并输出。