c语言之栈


栈的定义及其运算

1、栈的定义
     堆栈(Stack)可以看成是一种“特殊的”线性表,这种线性表上的插入和删除运算限定在表的某一端进行的。
  (1)通常称插入、删除的这一端为栈顶(Top),另一端称为栈底(Bottom)。
  (2)当表中没有元素时称为空栈
  (3)栈为后进先出(Last In First Out)的线性表,简称为LIFO表
     栈的修改是按后进先出的原则进行。每次删除(退栈)的总是当前栈中"最新"的元素,即最后插入(进栈)的元素,而最先插入的是被放在栈的底部,要到最后才能删除。
         




  图中元素是以a1,a2,…,an的顺序进栈,退栈的次序却是an,an-1,…,a1

  具体算法演示请点击查看算法演示

2、栈的基本运算
(1)InitStack(S)
     初始化操作,设定一个空栈S。
(2)EmptyStack(S)
     判空栈操作,若S为空栈,则返回TRUE,否则返回FALSE。
(3)FullStack(S)
     判栈满。若S为满栈,则返回TRUE,否则返回FALSE。
注意:
     
该运算只适用于栈的顺序存储结构。
(4)Push(S,e)
     进栈。若栈S不满,在S栈顶插入一个元素e,栈顶位置由top指针指出。
(5)Pop(S)
     退栈。若栈S非空,则将S的栈顶元素删去,并返回该元素。
(6)GetTop(S)
     取栈顶元素。若栈S非空,则返回栈顶元素,但不改变栈的状态。
(7)ClearStack(S)
     置栈空操作,已知S为栈,不论操作之前的栈是否为空栈,本操作的结果都是将S置为空栈。
(8)CurrentStack(S)
     求当前栈中元素的个数。
(9)DestroyStack(S)
     销毁S栈。


顺序栈


   与线性表类似栈也有两种存储结构,即顺序存储结构和链表存储结构。栈的顺序存储结构亦称为顺序栈,它是运算受限的顺序表。
1、 顺序栈的类型定义
  const int MAXSIZE=100; //假定预分配的栈空间最多为100个元素
  typedef int ElemType;//假定栈元素的数据类型为整型
  struct SqStack{ ElemType elem[MAXSIZE]; //一维数组
      int top;//指针top指向栈顶元素的当前位置
     } 
  注意:
   
  ①SqStack就是栈的顺序存储结构的类型标识符。
     ②顺序栈中元素用向量存放
     ③栈底位置是固定不变的,可设置在向量两端的任意一个端点
     ④栈顶位置是随着进栈和退栈操作而变化的,用一个整型量top(通常称top为栈顶指针)来指示当前栈顶位置
   例:
  假设MAXSIZE取值为6,图3.2展示了顺序栈S中数据元素和栈顶指针的关系。为了与前文所述top=0为空栈相一致,图中未画出S.elem[0]。逻辑上可利用有效空间为S.elem[1],...,S.elem[5]。


2、 顺序栈的基本操作 
  
前提条件:
     设S是SeqStack类型的指针变量。若栈底位置在向量的低端,即S.elem[0]是栈底元素。
(1) 进栈操作
     进栈时,需要将top加1
  注意:
    ①top==MAXSIZE-1表示栈满
    ②"上溢"现象--当栈满时,再做进栈运算产生空间溢出的现象。
      上溢是一种出错状态,应设法避免。
【算法3.1】
void SqStack::push( ElemType e) 
{ if(top= =MAXSIZE-1)
{cout<<"栈满溢出"<<endl;
exit(1);
}
else {top++;
elem[top]=e;
}
}

(2) 退栈操作
     退栈时,需将top减1
  注意:
     
①top<0表示空栈
     ②"下溢"现象——当栈空时,做退栈运算产生的溢出现象。
      下溢是正常现象,常用作程序控制转移的条件。
【算法3.2】
ElemType SqStack::pop()
{ElemType x;
if(top= =0)
{ cout<< " 栈为空,不能出栈操作"<<endl; exit(1);}
else { x=elem[top];
top--;
return x;
}
}
顺序栈在进栈和退栈操作时的具体算法演示请点击查看算法演示

3、两个栈共享同一存储空间
     
  顺序存储结构条件下的多栈操作也是数据结构课程所讨论的内容。在计算机系统软件中,诸如各种高级语言的编译软件都离不开栈的操作,且往往是同时使用和管理多个栈。若让多个栈共用一个向量,其管理和运算都很复杂,这里仅介绍两个栈共用一个向量的问题。两个栈共用一个向量又有不同的分配方法。如图3.3所示。

  图3.3(a)的方法是将向量平均分配给两个栈(设向量有n个元素),它们的栈底分别在下标为0和下标为(n-1)/2+1处,栈顶指针在进栈时作加1操作。如果其中一个栈已满,若还要进此栈,则此栈产生溢出,即使另一个栈仍有空间,也不能利用,这是它的局限性。如果让第二个栈底可以浮动,则实现的算法太麻烦。
  图3.3(b)所示的方法是两个栈底安排在向量的两端,一个在下标为0 处,另一个在下标为n-1处。两个栈顶指针可以向中间浮动,左栈进栈时,栈顶指针加1,右栈进栈时,栈顶指针减1。显然,这种方法向量空间利用率高。

链栈

  栈的链式存储结构称为链栈。

1、链栈的类型定义
  栈可以用单链表做为存储结构,链表中数据元素结点描述如下:
typedef int ElemType;
struct Lsnode { ElemType data;
struct Lsnode *next;
};


     
  图3.4展示了单链表栈的数据元素与栈顶指针top的关系。
  图3.4(a)是含有3个数据元素A,B,C的栈,A是栈底元素,指针型变量top指向栈顶元素C;图3.4(b)是在(a)的基础之上出栈一个元素后的状态;图3.4(c)是在(b)的基础上又进栈一个元素X后的状态。需指明的是,一个链表栈由栈顶指针top唯一确定。当top为NULL时,则是一个空栈

【算法3.3】 
LsStack:: LsStack () //构造函数,建立一个空栈
{ top=NULL;
}
void LsStack::Display() //输出显示单链表栈内容
{ Lsnode *p; p=top;
while(p!=NULL)
{ cout<<” ”<<p->data;
p=p->next;
}
cout<<"结束!outend!" ;
}
上述算法可以输出单链表栈。首先,判别是否栈空。如果不是空栈,即将线性表的数据元素一一输出。在这个算法中,我们又复习到单链表的相关内容。可见,栈是一种特殊的线性结构,它与前一章中的线性表数据结构有密切的联系。


2、链栈的基本运算

(1) 入栈
【算法3.4】 void LsStack::Push(ElemType x)
{ Lsnode *p;
p=new Lsnode; // 申请一个结点空间
p->data=x; p->next=top; top=p;
} //进栈Push
首先,建立一新结点。然后,把这个结点插到栈顶,并且令栈顶指针top 指向它。

(2) 出栈
【算法3.5】
ElemType LsStack::Pop()
{ Lsnode *p; ElemType x;
if(top!=NULL) { p=top; top=top->next;
x=p->data; free(p);
return(x);
}
else {cout<<"Stack null! \n";exit(1); }
}/*出栈Pop*/
  由上述算法可看出,栈在链表存储结构条件下进栈一个元素时一般不考虑栈满上溢出问题,而出栈时必须考虑栈空问题。


栈的应用
(1) 表达式的计算
  任何一个表达式都是由操作数(operand)、运算符(operator)和界限符(delimiter)组成的。我们把运算符和界限符统称为算符,它们构成的集合命名为OP。根据上述三条运算规则,在运算的每一步中,任意两个相继出现的算符θ1和θ2之间的优先关系至多是下面三种关系之一;
θ1<θ2 θ1的优先权低于θ2
θ1=θ2 θ1的优先权等于θ2
θ1>θ2 θ1的优先权高于θ2

例:
利用算法EvaluteExpression对算术表达式3*(7-2)求值,操作过程如下所示:


(2) 子程序嵌套调用
  在各种程序设计语言中都有子程序(或称函数、过程)调用功能。一个子程序还可以调用另一子程序。图3.5(a)展示的是由主程序开始的三层嵌套调用关系。

  主函数main调函数func1时需记下返回地址R,func1调用func2需记下返回地址S,func2调用func3时需记下返回地址T。func3执行结束时返回func2的地址T,依次返回到func1的地址S,最终返回到main的地址R。在编译软件内就设立一个栈专用于存放返回地址, 在嵌套调时返回地址一一入栈, 调用结束时返回地址一一出栈,如图3.5(b)所示。这是一个典型的先进后出结构。



  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值