一、抽象数据类型栈的定义
-
定义:只能在表的一端( 栈顶 也就是表尾)进行插入和删除运算的线性表。表头称为栈底,不含元素的空表称为空栈
-
逻辑结构:与线性表相同,仍为一对一关系
-
存储结构:用顺序栈或链栈存储均可
-
运算规则:只能在栈顶运算,且访问结点时依照 后进先出(LIFO)或先进后出(FILO)的原则
-
实现方式:关键是编写入栈和出栈函数,具体实现依顺序栈或链栈的不同而不同
二、栈的表示和实现
顺序表和顺序栈的对比:
顺序栈的栈底指针(base)始终指向栈底位置,若base的值为Null表明栈结构不存在。栈顶指针(top)初值指向栈底,top=base可作为空栈的标记,每当插入新的栈顶元素是,指针top增1;删除栈顶元素时,top减1。
顺序栈的存储表示:
#define MAXSIZE 100//最大存储空间的分配
typedef struct
{
SElemType *base;
SElemType *top;
int stacksize; //当前已分配的存储空间,以元素为单位
}SqStack;
顺序栈的基本操作:
存储分配:
Status InitStack (SqStack &S){
//构造一个空栈
SqStack *S
S.base = new SElemType[MAXSIZE]; //存储分配
if(!S.base)exit (OVERFLOW);
S.top=S.base; //初始化栈顶指针,现在这是一个空栈
S.stacksize = MAXSIZE; //设置栈的大小
return OK;
}//InitStack
判断栈是否为空:
bool StackEmpty( SqStack S )
{
if(S.top == S.base)
return true;
else
return false;
}
求顺序栈的长度:
int StackLength( SqStack S ){
return S.top – S.base;
}
清空顺序栈:
Status ClearStack( SqStack S ){
if( S.base )
S.top = S.base;
return OK;
}
销毁顺序栈:
Status DestroyStack( SqStack &S ){
if( S.base ){
delete S.base ;
S.stacksize = 0;
S.base = S.top = NULL;
}
return OK;
}
顺序栈进栈:
Status Push( SqStack &S, SElemType e)
{
if( S.top - S.base== S.stacksize ) // 栈满
return ERROR;
*S.top=“D”;
S.top++;
return OK;
}
顺序栈出栈:
Status Pop( SqStack &S, SElemType &e) {
if( S.top == S.base ) // 栈空
return ERROR;
S.top--;
e=*S.top;
return OK;
}
选取栈顶元素:
Status GetTop( SqStack S, SElemType &e)
{
if( S.top == S.base ) // 栈空
return ERROR;
e = *( S.top – 1 );
return OK;
}
链栈:受限的单链表,只能在链表头部进行操作,故没有必要附加头结点。栈顶指针就是链表的头指针。
存储分布:
typedef struct StackNode {
SElemType data;
struct StackNode *next; //栈顶指针也就是头指针
} StackNode, *LinkStack;
LinkStack S;
-
栈的应用举例
1.数制转换
【算法步骤】
① 初始化一个空栈S。
② 当十进制数N非零时,循环执行以下操作:
* 把N与8求余得到的八进制数压入栈S;
* N更新为N与8的商。
③ 当栈S非空时,循环执行以下操作:
* 弹出栈顶元素e;
* 输出e。
代码实现:
【算法描述】
void conversion(int N)
{//对于任意一个非负十进制数,打印输出与其等值的八进制数
InitStack(S); //初始化空栈S
while(N) //当N非零时,循环
{
Push(S,N%8); //把N与8求余得到的八进制数压入栈S
N=N/8; //N更新为N与8的商
}
while(!StackEmpty(S))//当栈S非空时,循环
{
Pop(S,e); //弹出栈顶元素e
cout<<e; //输出e
}
}
2.行编辑程序:
设立一个输入缓冲区,用以接收用户输入的一行字符。当输入的字符既不是退格符(@)也不是退行符(#)时,将这个字符压入栈顶;如果是一个退格符则从栈顶删去一个元素;如果是一个退行符则将字符栈清为空栈。
代码实现:
void LineEdit(){
//利用字符栈S,从终端接收一行并传送至调用过程的数据区
InitStack(S); //构造一个空栈S
ch=getchar(); //从终端接收第一个字符
while(ch!=EOF){
while(ch!=EOF&&ch!='/n'){
switch(ch){
case'#':Pop(S,ch); break; //仅当栈非空时退栈,弹出ch
case'@':ClearStack(S); break; //设置S为空栈
default:Push(S,ch); break;//有效字符进栈
}
ch=getchar();//从终端接收下一个字符
}//将从栈底到栈顶的栈内字符传送至调用过程的数据区;
ClearStack(S); //重置S为空栈
if(ch!=EOF) ch=getchar();
}
DestroyStack(S);
}//LineEdit
-
栈与递归的实现
递归函数:直接调用自己或者通过一列的调用语句间接地调用自己的函数
double Fact ( double n ) {
if ( n == 0) return 1;
else return n * Fact (n-1);
}
在运行被调用函数之前,系统需先完成三件事:
(1)将实参,返回地址等传递给被调用函数
(2)为被调用函数的局部变量分配存储区
(3)将控制转移到被调用函数的入口
从被调用函数返回调用函数之前,系统需要完成的三件事:
(1)保存被调用函数的计算结果
(2)释放被调用函数的数据区
(3)依照被调用函数保存的返回地址将控制转移到调用函数
三阶Hanoi塔问题:
将X上的圆盘移至Z上并仍然按相同顺序叠排
代码实现:
#include<iostream.h>
int c=0;
void move(char x,int n,char z)
{
cout<<++c<<","<<n<<","<<x<<",“<<z<<endl;
}
void Hanoi(int n,char A,char B,char C)
{
if(n==1)
move(A,1,C);
else
{
Hanoi(n-1,A,C,B);
move(A,n,C);
Hanoi(n-1,B,A,C);
}
}
void main(){
Hanoi(3,'a','b','c');
}