数据结构(用C语言描述):栈和队列基本概念、实现

栈和队列

    1. 栈的基本概念
      1. 栈的定义

栈(Stack):只允许在一端进行插入或删除操作的线性表。

栈顶(Top): 线性表允许进行插入和删除的那一端

栈底(Bottom): 固定的,不允许进行插入和删除操作的另一端。

空栈:不含任何元素的空表。

栈的一个明显的操作特性:后进先出(Last In First Out, LIFO),故又称为后进先出的线性表。

      1. 栈的基本操作

InitStack(&S):初始化一个空栈S。

StackEmpty(S):判断一个栈是否为空,若栈S为空返回true,否则返回false。

Push(&S,x):进栈,若栈S未满,将x加入使之成为新栈顶。

Pop(&S,&x)出栈,若栈S非空,弹出栈顶元素,并用x返回。

GetTop(S,&x):读栈顶元素,若栈S非空,用x返回栈顶元素。

ClearStack(&S):销毁栈,并释放栈S所占的存储空间。

在解答算法题时,若题干没有做出限制,可以直接使用这些基本的操作函数。

 

    1. 栈的顺序存储结构
      1. 顺序栈的实现

栈的顺序存储称为顺序栈,它是利用一组地址连续的存储单元存放自栈底到栈顶的数据元素,同时附设一个指针(top)指示当前栈顶的位置。

栈的顺序存储类型可描述为:

#define MaxSize 50                     //定义栈中元素的最大个数

typedef struct {

    ElemType data[MaxSize];            //存放栈中元素

    int top;                           //栈顶指针

}SqStack;

栈顶指针:S.top,初始时设置S.top=-1;栈顶元素:S.data[S.top]。

进栈操作:栈不满时,栈顶指针先加1,再送值到栈顶元素。

出栈操作:栈非空时,先取栈顶元素值,再将栈顶指针减1。

栈空条件:S.top==-1;栈满条件:S.top==MaxSize-1;栈长:S.top+1。

      1. 顺序栈的基本运算
  1. 初始化

void InitStack(SqStack &S) {

    S.top = -1;                       //初始化栈顶指针

}

  1. 判空栈

bool StackEmpty(SqStack &S) {

    if (S.top == -1)                  //栈空

         return true;

    else                              //不空

         return false;                

}

  1. 进栈

bool Push(SqStack &S, ElemType x) {

    if (S.top == MaxSize - 1)              //栈满,报错

         return false;

    S.data[++S.top] = x;                   //指针先加1,再入栈

    return true;

}

  1. 出栈

bool Pop(SqStack &S, ElemType &x) {

    if (S.top == -1)                          //栈空,报错

         return false;

    x = S.data[S.top--];                      //先出栈,指针再减1

    return true;

}

  1. 读栈顶元素

bool GetTop(SqStack &S, ElemType &x) {

    if (S.top == -1)                             //栈空,报错

         return false;

    x = S.data[S.top];                           //x记录栈顶元素

    return true;

}

注意:这里栈顶指针指向的就是栈顶元素,所以进栈时的操作是S.data[++S.top] = x;出栈时的操作时x = S.data[S.top--]。如果栈顶指针初始化为S.top=0,即栈顶指针指向栈顶元素的下一个位置,则入栈操作变为S.data[S.top++] = x;出栈操作变为x = S.data[--S.top]。相应的栈空、栈满条件也会发生变化。

      1. 共享栈

利用栈底位置相对不变的特性,可以让两个顺序栈共享一个一维数据空间,将两个栈的栈底分别设置在共享空间的两端,两个栈顶向共享空间的中间延伸。

两个栈的栈顶指针都指向栈顶元素,top0=-1时0号栈为空,top1=MaxSize时1号栈为空;仅当两个栈顶指针相邻(top1-top0=1)时,判断为栈满。当0号栈进栈时top0先加1再赋值,1号栈进栈时top1先减1再赋值;出栈时则刚好相反。

共享栈是为了更有效地利用存储空间,两个栈地空间相互调节,只有在整个存储空间被占满时才发生上溢。其存取数据的时间复杂度均为O(1),所以对存取效率没有什么影响。

 

    1. 栈的链式存储结构

采用链式存储的栈称为链栈,链栈的优点是便于多个栈共享存储空间和提高其效率,且不存在栈满上溢的情况。通常采用单链表实现,并规定所有操作都是在单链表的表头进行。这里规定链栈没有头结点,Lhead指向栈顶元素。

栈的链式存储类型可描述为:

typedef struct Linknode {

    ElemType data;                    //数据域

    struct Linknode *next;            //指针域

}*LiStack;                            //栈类型定义

采用链式存储,便于结点的插入与删除。

    1. 习题
      1. 第一题

假设以I和O分别表示入栈和出栈操作。栈的初态和终态均为空,入栈和出栈的操作序列可表示为仅由I和O组成的序列,可以操作的序列称为合法序列,否则称为非法序列。

①下面所示的序列中哪些是合法的? 

A. IOIIOIOO     B. IOOIOIIO      C. IIIOIOIO     D. IIIOOIOO 

②通过对①的分析,写出一个算法,判定所给的操作序列是否合法。若合法,返回true,否则返回false(假定被判定的操作序列已存入一维数组中)。

  1. A和D是合法序列,B和C 是非法序列。
  2. 设被判定的操作序列已存入一维数组A中。

【算法思想】依次逐一扫描入栈出栈序列(即由”I”和”O”组成的字符串),每扫描至任一位置均需检查出栈次数(即”O”的个数)是否小于入栈次数(“I”的个数),若大于则为非法序列。扫描结束后,再判断入栈和出栈次数是否相等,若不相等则不合题意,为非法序列。

int Judge(char A[])

//判断字符数组A中的输入输出序列是否是合法序列。如是,返回true,否则返回false。

{

    int i = 0;                         //i为下标。

    int j = 0; int k = 0;              //j和k分别为I和字母O的的个数。

    while (A[i] != '\0')               //未到字符数组尾。

    {

         switch (A[i])

         {

         case 'I': j++; break;          //入栈次数增1

         case 'O': k++;

             if (k > j) { printf("序列非法\n"); exit(0); }

         }

         i++;                           //不论A[i]是'I'或'O',指针i均后移。

    }                                  //while

    if (j != k) {

         printf("序列非法\n");

         return false;

    }

    else {

         printf("序列合法\n");

         return true;

    }

}

      1. 第二题

设单链表的表头指针为h,结点结构由data和next两个域构成,其中data域为字符型。试设计算法判断该链表的前n个字符是否中心对称。例如xyx,xyyx都是中心对称。

【算法思想】使用栈来判断链表中的数据是否中心对称。将链表的前一半元素依次进栈。在处理链表的后一半元素时,当访问到链表的一个元素后,就从栈中弹出一个元素,两个元素比较,若相等,则将链表中下一个元素与栈中再弹出的元素比较,直至链表到尾。这时若是空栈,则得出链表中心对称的结论;否则,当链表中的一个元素与栈中弹出元素不等时,结论为链表非中心对称,结束算法的执行。

int dc(LinkList L, int n) {

    //h是带头结点的n个元素单链表,本算法判断链表是否是中心对称

    char s[n/2]; int i;                   //s字符栈

    LNode *p = L->next;                   //p是链表的工作指针,指向待处理的当前元素

    for(i = 0; i<n / 2; i++) {           

你可能感兴趣的:(数据结构,数据结构,栈和队列,考研习题)