一. 栈的基本概念
1. 栈(Stack)的定义
栈是只允许在一端进行插入或删除操作的线性表 后进先出。LIFO
2.栈的基本操作
- InitStack(&S):初始化栈。构造一个空栈S,分配内存空间。
- DestoryStack(&L):销毁栈。销毁并释放栈S所占用的内存空间。
- Push(&S,x):进栈,若栈S未满,则将x加入使之成为新栈顶。
- Pop(&S,&x):出栈,若栈S非空,则弹出栈顶元素,并用x返回。
- GetTop(S,&x):读栈顶元素。若栈S费控,则用x返回栈顶元素。
- StackEmpty(S):判断一个栈S是否为空。若S为空,则返回true,否则返回false。
3.栈的常考题型
n个不同元素进栈,出栈元素不同排列的个数为: 1 n + 1 C 2 n n \frac1{n+1}C^n_{2n} n+11C2nn
二. 栈的顺序存储实现
1. 顺序栈的定义
#define MaxSize 10 //栈中元素的最大个数
typedef struct{
Elemtype data[MaxSize]; //静态数组存放栈中元素
int top; //栈顶指针
}SqStack;
2.初始化操作
//初始化栈
void InitStack(SqStack &S){
S.top=-1;
}
//判断栈空
bool StackEmpty(SqStack S){
if(S.top==-1) //栈空
return true;
else
return false;
}
3.进出栈操作
//新元素进栈
bool Push(SqStack &S,ElemType x){
if(S.top==MaxSize-1) //栈满,报错
return false;
S.top=S.top+1; //指针先加1
S.data[S.top]=x; //新元素入栈
/*等价于
S.data[++S.top]=x;
*/
return true;
}
//出栈操作
bool Pop(SqStack &S,ElemType &x){
if(S.top==-1) //栈空,报错
return false;
x=S.data[S.top]; //栈顶元素先出栈
S.top=S.top-1; //指针再减1
/*等价于
S.data[S.top--]=x;
*/
return true;
}
[入栈]
[出栈]
4.读栈顶元素操作
//读栈顶元素
bool GetTop(SqStack S,ElemType &x){
if(S.top==-1)
return false;
x=S.data[S.top]; //x记录栈顶元素
return true;
}
审题时要注意top指针指向的是栈顶,还是栈顶的下一个位置。
指向栈顶下一个位置时,栈满的条件是:top==MaxSize;进栈出栈位置相反,即:
进栈:S.data[S.top++]=x;
出栈:x=S.data[–S.top];
5.共享栈
两个栈共享同一片空间。
///共享栈
typedef struct{
ElemType data[MaxSize]; //静态数组存放栈中元素
int top0; //0号栈栈顶指针
int top1; //1号栈栈顶指针
}ShStack;
//初始化共享栈
void InitStack(ShStack &S){
S.top0=-1;
S.top1=MaxSize;
}
栈满的条件:top0+1==top1
三. 栈的链式存储
1. 链栈的定义
typedef struct Linknode{
ElemType data; //数据域
struct Linknode *next; //指针域
} *LiStack; //栈类型定义
2.链栈的基本操作
创,增,删,查,判空,判满。
//创,增,删,查,判空,判满。
//带头结点链栈
//创(初始化)
void InitLiStack(LiStack &S){
LiStack node=(LiStack)malloc(sizeof(Linknode));
S=node;
S->next=NULL;
}
//判空
bool isEmpty(LiStack &S){
if(S->next==NULL)
return true;
return false;
}
//判满:链表没有判满。
//增(进栈)
void Push(LiStack &S,ElemType x){
S->data=x;
LiStack newNode=(Linknode*)malloc(sizeof(Linknode));
newNode->next=S;
S=newNode;
}
//删 (出栈)
ElemType Pop(LiStack &S){
ElemType x=S->next->data;//被删除元素带回
LiStack top=S->next;
S->next=top->next;
free(top);
return x;
}
//查(获取栈顶元素)
ElemType GetTop(LiStack &S){
return S->next->data;
}
测试链栈:
//链栈
printf("创建。。。\n");
LiStack S;
printf("创建成功。\n");
printf("初始化。。。\n");
InitLiStack(S);
printf("初始化成功。");
printf("链表是否为空:%d\n",isEmpty(S));
printf("进栈\n");
Push(S,6);
printf("链表是否为空:%d\n",isEmpty(S));
printf("查询栈顶元素为:%d\n",GetTop(S));
printf("出栈\n");
Pop(S);
printf("链表是否为空:%d\n",isEmpty(S));
四. 栈的应用
1.栈在括号匹配中的应用
算法思路:
//括号匹配
bool bracketCheck(char str[],int length){
SqStack S;
InitStack(S);
for(int i=0;i<length;i++){
if(str[i]=='('||str[i]=='['||str[i]=='{')
Push(S,str[i]);
else{//扫描到右括号
if(StackEmpty(S))// 栈为空
return false;//匹配失败
char topElem;
Pop(S,topElem);
if(str[i]==')'&&topElem=='(')
continue;
else if(str[i]==']'&&topElem=='[')
continue;
else if(str[i]=='}'&&topElem=='{')
continue;
else
return false;
}
}
return StackEmpty(S);//检索完全部括号后,栈空说明匹配成功
}
//测试括号匹配
void testbracketCheck()
{
char str[]={'{','[','(',')',']','}'};
for(int i=0;i<6;i++){
printf("%c",str[i]);
}
printf("\n");
if(bracketCheck(str,6))
printf("匹配成功!");
else
printf("匹配失败。");
}
2.栈在表达式求值中的应用
算术表达式由三个部分组成:操作数,运算符,界限符。
波兰表达式:前缀表达式; 逆波兰表达式:后缀表达式。
中缀表达式 | 后缀表达式 | 前缀表达式 |
---|---|---|
a + b | a b + | + a b |
a + b - c | a b + c - | - + a b c |
中缀表达式转后缀表达式(机算):
(1). 初始化一个栈,用于保存暂时还不能确定运算顺序的运算符。
(2). 从左到右处理各个元素,直到末尾。可能遇到三种情况:
①遇到操作数。直接加入后缀表达式。
②遇到界限符。遇到“(”直接入栈:遇到“)”则依次弹出栈内运算符并加入后缀表达式,直到弹出“(”为止。注意:“(”不加入后缀表达式。
③遇到运算符。依次弹出栈中优先级高于或等于当前运算符的所有运算符,并加入后缀表达式,若碰到“(”或栈空则停止。之后再把当前运算符入栈。
(3). 按上述方法处理完所有字符后,将栈中剩余运算符依次弹出,并加入后缀表达式。
用栈实现后缀表达式的计算(机算):
①. 从左往右扫描下一个元素,直到处理完所有元素;
②. 若扫描到操作数则压入栈,并回到①;否则执行③;
③. 若扫描到运算符,则弹出两个栈顶元素,执行相应运算,运算结果压回栈顶,回到①。注意:先出栈的是右操作数。
用栈实现中缀表达式的计算(机算):(中缀转后缀+后缀计算)
初始化两个栈,操作数栈和运算符栈
①. 若扫描到操作数,压入操作数栈
②. 若扫描到运算符或界限符,则按照“中缀转后缀”相同的逻辑压入运算符栈(期间也会弹出
运算符,每当弹出一个运算符时,就需要再弹出两个操作数栈的栈顶元素并执行相应运算,
运算结果再压回操作数栈)
用栈实现前缀表达式的计算(机算):
①. 从右往左扫描下一个元素,直到处理完所有元素;
②. 若扫描到操作数则压入栈,并回到①;否则执行③;
③. 若扫描到运算符,则弹出两个栈顶元素,执行相应运算,运算结果压回栈顶,回到①。注意:先出栈的是左操作数。
3.栈在递归中的应用
函数调用的特点:最后被调用的函数最先执行结束(FIFO)
函数调用时,需要用一个栈存储:
①调用返回地址
②函数
③局部变量
斐波那契数列
#include <stdio.h>
#include <stdlib.h>
///递归
int Fibon1(int n){
if(n==1||n==2)
return 1;
else
return Fibon1(n-1)+Fibon1(n-2);
}
int Fibon2(int n){
if(n==0)
return 0;
else if(n==1)
return 1;
else
return Fibon2(n-1)+Fibon2(n-2);
}
///非递归
int Fibon3(int n){
int f0=0,f1=1,f2=1;
for(int i=2;i<=n;i++){
f2=f0+f1;
f0=f1;
f1=f2;
}
if(n==0)
return 0;
else if(n==1)
return 1;
else
return f2;
}
int main(){
printf("斐波那契数列值:");
printf("\n");
printf("递归1: ");
for(int i=1;i<15;i++){
printf("%d ",Fibon1(i));
}
printf("\n");
printf("递归2: ");
for(int i=1;i<15;i++){
printf("%d ",Fibon2(i));
}
printf("\n");
printf("非递归:");
for(int i=1;i<15;i++){
printf("%d ",Fibon3(i));
}
return 0;
}