栈的基础

来源:互联网 发布:夜的第七章 知乎 编辑:程序博客网 时间:2024/06/05 18:06

1. 栈的基础 
    栈,也成为堆栈,是一种重要的线性结构。栈具有线性表的特点:每一个元素只有一个前驱元素和后继元素(除了第一个元素和最后一个元素外),但是在操作上与线性表不同,栈是一种操作受限的线性表,只允许在栈的一端进行插入和删除操作。栈可以用顺序存储结构和链式存储结构存储,采用顺序存储结构的栈称为顺序栈,采用链式存储结构的栈称为链式栈。栈的应用十分广泛,在表达式求值、括号匹配时常用到栈的思想。 
2. 栈的操作 
    允许插入和删除操作的一端称为栈顶,另一端称为栈底。栈顶是动态变化的,它由一个称为栈顶指针(top)的变量指示,当表中没有元素时,称为空栈。栈的插入操作称为入栈或进栈,删除操作称为出栈或退栈。 
                                                                 

                                                                            图 堆栈示意图
    例如:一个进栈的序列由A、B、C组成,它的出栈序列有ABC、ACB、BAC、BCA和CBA五种可能,只有CAB是不可能的输出序列。因为ABC进栈后,C出栈接着就是B要出栈,不可能A在B之前出栈,所以CAB是不可能的序列。

    栈的基本操作包括以下七种:

    (1)InitStack(&S):初始化操作,建立一个空栈S。

    (2)StackEmpty(S):若栈S为空,返回1,否则返回0。

    (3)GetTop(S,&e):返回栈S的栈顶元给e。

    (4)PushStack(&S,e):在栈S中插入元素e,使其成为新的栈顶元素。

    (5)PopStack(&S,&e):删除栈S的栈顶元素。

    (6)StackLength(S):返回栈S的元素个数。

    (7)ClearStack(S):清空栈S。
3. 共享栈

    栈的应用十分广泛,经常会出现一个程序需要同时使用多个栈的情况。使用顺序栈会由于栈空间的大小难以准确估计,从而造成有的栈溢出,有的栈空间还有空闲,为了解决这个问题,可以让多个栈共享一个足够大的连续存储空间,通过利用栈的动态特性使多个栈存储空间能够互相补充,存储空间得到有效利用,这就是栈的共享。

    在栈的共享问题中,最常用的是两个栈的共享,实现方法是两个栈共享一个一维数组空间S(StackSize),两个栈的栈底设置在数组的两端,当有元素进栈时,栈顶位置从栈的两端迎面增长,当两个栈的栈顶相遇时,栈满。共享栈的存储表示如下图所示:

                              

                                                                            图 两栈共享空间

    如上图,设两个栈共享一个一维数组空间Stack[M],将两个栈的栈底分别在该数组空间两端,即Stack[O]和Stack[M-1]。这样,当元素进栈时,两个栈都是从两端向中间伸展。通过两个栈顶指针(top1和top2)的动态变化,使其存储空间相互补充。

    两栈共享空间的数据结构定义如下:

C代码  收藏代码
  1. #define M 100         //两栈共享的存储空间大小  
  2. typedef struct  
  3. {  
  4.   ElemType Stack[M];  //两栈共享的一维数组空间  
  5.   int top[2];         //两栈的栈顶指针  
  6. } DSegStack;  

4. 栈的链式表示与实现

    在顺序栈中,由于顺序存储结构需要事先静态分配,而存储规模往往又难以确定,如果栈空间分配过小,可能会照成溢出,如果栈空间分配过大,又造成存储空间浪费。因此,为了克服顺序存储的缺点,采用链式存储结构表示栈。

    采用链式存储方式的栈称为链栈或链式栈。链栈由一个个节点构成,节点包含数据域和指针域两部分。在链栈中,利用每一个节点的数据域存储栈中的每一个元素,利用指针域表示元素之间的关系。

                                     

                                                                      图 链栈示意图 

 链栈的数据结构定义如下:

C代码  收藏代码
  1. typedef struct node  
  2. {  
  3.     DataType data;  
  4.     struct node *next;  
  5. }LStackNode, *LinkStack;   //LinkStack为栈顶指针,始终指向链栈的头结点  

  5. 栈与递归

    递归是指在函数的定义中,在定义自己的同时又出现了对自身的调用。如果一个函数在函数体中直接调用自己,则称为直接递归函数。如果经过一系列的中间调用,间接调用自己的函数称为间接递归调用。递归问题可以分解成规模小且性质相同的问题加以解决。

    用递归编制的算法具有结构清晰、易读,容易实现并且递归算法的正确性很容易得到证明。但是,递归算法的执行效率比较低,因为递归需要反复入栈,时间和空间开销大。递归算法也完全可以转换为非递归实现,这就是递归的消除。消除递归的方法有两种,一是对于简单的递归可以直接用迭代,通过循环结构就可以消除;另一种方法是利用栈的方式实现。

    利用栈模拟递归过程可以通过以下步骤实现:

    (1)设置一个工作栈,用于保存递归工作记录,包括实在参数,返回地址等。

    (2)将调用函数传递过来的参数和返回地址入栈。

    (3)利用循环模拟递归分解过程,逐层将递归过程的参数和返回地址入栈。当满足递归结束条件时,依次逐层退栈,并将结果返回给上一层,直到栈空为止。

0 0