1.顺序栈——用一块连续空间顺序存放栈元素
栈的数据结构定义:
#define STACK_SIZE 64; //栈容量设置
typedef struct Stack
{
datatype stack[STACK_SIZE];//栈空间
int top;//栈顶指针
} SeqStack;
约定:
栈空:Top=-1
栈满:Top=STACK_SIZE-1
1)初始化:置空栈
void InitStack(SeqStack *s)
{
s->top=-1;
}
2)判断栈空
/*=======================================
函数功能:判断顺序栈是否为空
函数输入:顺序栈地址
函数输出:1——栈空;0——栈非空
=========================================*/
int StackEmpty_SqStack(SeqStack *s)
{
return( s->top == -1 );
}
3)进栈(注意区别传值调用和传址调用)
栈状态:栈未满或栈满
伪代码描述:若栈满则 return FALSE;
修改栈顶指针top++;
在栈顶指针指向位置放入x;
return TRUE.
/*=======================================
函数功能:顺序栈进栈操作
函数输入:顺序栈地址,进栈元素值
函数输出:0——栈上溢,操作失败;1——操作正常
=========================================*/
int Push_SqStack(SeqStack *s, datatype x)
{
if ( s->top == STACK_SIZE-1) return FALSE; //栈上溢
else
{
s->top++;
s->stack[s->top]=x;
}
return TRUE;
}
4)出栈
测试情形:栈非空,栈顶指针>0 else =0; 栈空,栈顶指针=-1。
伪代码描述:若栈空,则 return FALSE;
记录栈顶元素值x;
修改栈顶指针:top–;
return TRUE。
/*=====================================
函数功能:顺序栈出栈操作
函数输入:顺序栈地址,(出栈元素地址)
函数输出:0——栈下溢,操作失败;1——操作正常
=========================================*/
int Pop_SqStack(SeqStack *s, datatype *x)
{
if ( s->top == -1) return FALSE;
else
{
*x= s-> stack[s-> top]; //取栈顶元素的值
s-> top--; //修改栈顶指针
}
return TRUE;
}
顺序栈存在栈满以后就不能再进栈的问题,这是因为用了定长的数组存储栈的元素。解决的方法,可以使用链式存储结构,让栈空间可以动态扩充。
2.链栈的基本操作
关于链表和链栈:都是采用离散的方式存储;存储元素分别为线性表元素和栈元素;而链栈是运算受限的单链表,其插入和删除操作仅限制在表头的位置上进行。
因为只能在链表头部进行操作,故链栈不需要附加头结点
链栈数据结构的描述:
typedef int datatype;
typedef struct node //结点
{
datatype data;
struct node *next; //链指针
} LinkStack;
LinkStack *top; //栈顶指针
1 )进栈;
步骤:申请一个新结点p,并赋值为x;
将p结点链在栈顶;
修改栈顶指针;
返回栈顶指针。
ListStack *PushLStack(ListStack *top, datatype x)
{
LinkStack *p;
p=malloc(sizeof(LinkStack));
p->data=x;
p->next=top;
top=p;
return top; //需注意进栈前,栈底指针赋 NULL
}
return top:将新建的链栈结点链入,top指针改变让下个结点链入新top。
需注意,链入的第一个结点其链入栈顶结点为NULL,即栈底的next值改为0。
2)出栈;
伪代码:若栈非空;
记录栈顶结点地址p,元素值datap;
修改栈顶指针top;
释放结点p。
/*==================================================
函数功能:链栈的出栈操作
函数输入:栈顶指针、(出栈元素)
函数输出:栈顶指针
===================================================*/
LinkStack * PopLStack(LinkStack *top, datatype *datap)
{ LinkStack *p;
if ( top != NULL)
{ *datap=top->data;
p=top;
top=top->next;
free(p);
}
return top; //栈空时,返回NULL
}
共享栈——多栈共享邻接空间
常常一个程序中要用到多个栈,为了不发生上溢错误,必须给每个栈预先分配一个足够大的存储空间;另一方面,若每个栈都预分配一个足够大的存储空间,势必造成系统空间紧张;若让多个栈共用一个足够大的连续存储空间,则可利用栈的动态特性使它们的存储空间互补。
例两个栈共享一个一维空间S(StackSize),两个栈的栈底设置在数组的两端,当栈顶相遇,栈满。
#define STACK_SIZE 64 //两栈共享的存储空间大小
typedef struct
{
datatype stack[STACK_SIZE];
int top1, top2; //两栈的栈顶指针
}DSegStack;
栈(STACK)
凡应用问题求解的过程具有"后进先出"的天然特性的话,则求解的算法中也必然需要利用"栈"。
1.十进制数N到R进制数d的转换;
算法:
N=(n div d)*d+n mod d (div为整除运算,mod为求余运算)
例:(1348)10=(2504)8
程序实现
//对于输入的任意一个非负十进制整数n,打印输出与其等值的八进制数
void conversion(SeqStack *S, int n) {
int e;
while (n) {
Push_SqStack(S,n%8);// "余数"入栈
n = n/8; //非零"商"继续运算
}
while (!StackEmpty_SqStack(S)) { //栈非空,显示结果
Pop_SqStack(S,&e);
printf("%d",e);
}
}
2.栈实现函数的递归
数据结构与算法分析新视角:p 125
问题:实现等差级数1+2+3+…+value的求和
递归的实现方法
(1)递归边界条件: iterate=value 当value=1
(2)递归继续条件 iterate=value+iterate(value-1) 当value>1
int iterate( int value )
{
if (value == 1) return 1;
return sum = value + iterate(value -1);
}
3.表达式求值
1)后缀表达式(Postfix Notation)
———运算符在操作数之后
运算符紧跟在两个操作数之后的表达式叫后缀表达式
如:
中缀表达式(AB/C)
对应的后缀表达式为 (ABC/)
后缀表达式计算:
表达式:1 + ( 5 - 6 / 2 ) * 3
后缀表达式:1 5 6 2 / - 3 * + #
注:为运算方便在后缀表达式最后加一个结束标志‘#’
算法设计:
void value(char suffix[], int *c)
{
char *p,ch;
InitStack(S);//初始化栈S
p=suffix;
While(*p!='#')//表达式未结束,“#”结束标识
{
if(!isoperator(ch))//判断ch非运算符
{
push(S,ch); //压栈
}
else
{
pop(S,a); //弹栈取操作数a
pop(S,b); //弹栈取操作数b
push(S,operate(a,ch,b));//a与b的计算结果压栈
}
p++;
}
pop(S,*c);//计算结果赋给 c
}
2)中缀转后缀规则
转换过程中:要用到一个存放运算符的栈S,一个保存后缀表达式的数组suffix[];
从左到右扫描中缀表达式时,将遇到五类元素:操作数、左括号、运算符、右括号和结束符#。
例:将中缀表达式“1+2-3#”转换为后缀表达式
例:1+((2+3)×4)-5#
3)中缀转前缀规则