数据结构(用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++) {            //链表前一半元素进栈

    s[i] = p->data;

    p = p->next;

    }

    i--;                                  //恢复最后的i值

    if (n % 2 == 1)                       //若n是奇数,后移过中心结点

         p = p->next;

    while (p != NULL && s[i] == p->data) { //检测是否中心对称

         i--;                               //i充当找顶指针

         p = p->next;

    }

    if (i == -1)                           //桟为空找

         return 1;                          //链表中心对称

    else

         return 0;                          //链表不中心对称

}

注意:算法先将“链表的前一半”元素(字符)进栈。当n为偶数时,前一半和后一半的个数相同;当n为奇数时,链表中心结点字符不必比较,移动链表指针到下一字符开始比较。比较过程中遇到不相等时,立即退出while循环,不再进行比较。

      1. 第三题

设有两个栈s1,s2都釆用顺序栈方式,并且共享一个存储区[0, …, maxsize-1],为了尽量利用空间,减少溢出的可能,可釆用栈顶相向、迎面增长的存储方式。试设计s1,s2 有关入栈和出栈的操作算法。

两个栈共享向量空间,将两个栈的栈底设在向量两端,初始时,s1栈顶指针为-1,s2 栈顶指针为maxsize。两个栈顶指针相邻时为栈满。两个栈顶相向、迎面增长,栈顶指针指向栈顶元素。

#define maxsize 100  //两个栈共享顺序存储空间所能达到的最多元素数

#define elemtp int   //假设元素类型为整型

typedef struct {

    elemtp stack[maxsize];  //栈空间

    int top[2];             //top为两个栈顶指针

}stk;

stk s;                      //s是如上定义的结构类型变量,为全局变量

本题的关键在于,两个桟入栈和退栈时的栈顶指针的计算。s1栈是通常意义下的栈;而 s2栈入栈操作时,其栈顶指针左移(减1),退栈时,栈顶指针右移(加1)。
此外,对于所有栈的操作,都要注意“入栈判满、出栈判空”的检查。

  1. 入栈操作

int push(int i, elemtp x) {

    //入栈操作。i为栈号,i=0表示左边的s1栈,i=1表示右边的s2栈,x是入栈元素

    //入栈成功返回1,否则返回0

    if (i<0 || i>1) {

         printf("栈号输入不对");

         exit(0);

    }

 

    if (s.top[1] - s.top[0] == 1) {

         printf("栈已满\n");

         return 0;

    }

 

    switch (i) {

    case 0: s.stack[++s.top[0]] = x; return 1; break;

    case 1: s.stack[--s.top[1]] = x; return 1;

    }

}

  1. 退栈操作

elemtp pop(int i) {

    //退栈算法。i代表栈号,i=0时为s1栈,i=l时为s2栈

    //退栈成功返回退栈元素,否则返回-1

    if (i<0 || i>1) {

         printf("栈号输入错误\n");

         exit(0);

    }

    switch (i) {

    case 0:

         if (s.top[0] == -1) {

             printf("栈空\n");

             return -1;

         }

         else

             return s.stack[s.top[0]--];

    case 1:

         if (s.top[1] == maxsize) {

             printf("栈空\n");

             return -1;

         }

         else

             return s.stack[s.top[1]++];

    }//switch

}

  1. 队列
    1. 队列的基本概念
      1. 队列的定义

队列(Queue): 队列简称队,也是一种操作受限的线性表,只允许在表的一端进行插入,而在表的另一端进行删除。向队列中插入元素称为入队或进队;删除元素称为出队或离队。这和我们日常生活中的排队是一致的,最早排队的也是最早离队的。其操作的特性是先进先出(First In First Out, FIFO),故又称为先进先出的线性表。

队头(Front):允许删除的一端,又称为队首。

队尾(Rear): 允许插入的一端。

空队列:不含任何元素的空表。

      1. 队列常见的基本操作

InitQueue(&Q): 初始化队列,构造一个空队列Q。

QueueEmpty(Q): 判队列空,若队列Q为空返回true,否则返回false。

EnQueue(&Q,x): 入队,若队列Q未满,将x加入,使之成为新的队尾。

DeQueue(&Q,&x): 出队,若队列Q非空,删除队头元素,并用x返回。

GetHead(Q,&x): 读队头元素,若队列Q非空,则将对头元素赋值给x。

需要注意的是,队列是操作受限的线性表,所以,不是任何对线性表的操作都可以作为队列的操作。比如,不可以随便读取队列中间的某个数据。

    1. 队列的顺序存储结构
      1. 队列的顺序存储

队列的顺序实现是指分配一块连续的存储单元存放队列中的元素,并附设两个指针front和rear分别指示队头元素和队尾元素的位置。设队头指针指向队头元素,队尾指针指向队尾元素的下一个位置(也可以让rear指向队尾元素,front指向队头元素的前一个位置)。

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

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

typedef struct {

    ElemType data[MaxSize];            //存放队列元素

    int front, rear;                   //队头指针和队尾指针

}SqQueue;

初始状态(队空条件):Q.front == Q.rear == 0。

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

出队操作:队不空时,先取队头元素值,再将队头指针加1。

但不能用Q.rear==MaxSize作为队列满的条件。

      1. 循环队列

将顺序队列臆造为一个环状的空间,即把存储队列元素的表从逻辑上看成一个环,称为循环队列。当队首指针Q.front=MaxSize-1后,再前进一个位置就自动到0,这可以利用除法取余运算(%)来实现。

初始时: Q.front=Q.rear=0

队首指针进1: Q.front=(Q.front+1)%MaxSize

队尾指针进1: Q.rear=(Q.rear+1)%MaxSize

队列长度:(Q.rear+MaxSize-Q.front)%MaxSize

出队入队时:指针都按顺时针方向进1。

显然,对空的条件是Q.front==Q.rear。如果入队元素的速度快于出队元素的速度,队尾指针很快就赶上了队首指针,此时可以看出队满时也有Q.front==Q.rear。

为了区分队空还是队满的情况,有三种处理方式:

*(1)牺牲一个单元来区分队空和队满,入队时少用一个队列单元,这是一种较为普遍的做法。 约定以“队头指针在队尾指针的下一个位置作为队满的标志”。

队满条件为:(Q.rear+1)%MaxSize==Q.front。

队空条件仍为:Q.front==Q.rear。

队列中元素的个数:(Q.rear-Q.front+MaxSize)%MaxSize

(2)类型中增设表示元素个数的数据成员。这样,则队空的条件为Q.Size==0;队满的条件为Q.size==MaxSize。这两种情况都有Q.front==Q.rear。

(3)类型中增设tag数据成员,以区分是队满还是队空。tag等于0的情况下,若因删除导致Q.front==Q.rear则为空队;tag等于1的情况下,若因插入导致Q.front==Q.rear则为队满。

      1. 循环队列的操作
  1. 初始化

void InitQueue(SqQueue &Q) {

    Q.rear = Q.front = 0;               //初始化队首、队尾指针

}

  1. 判队空

bool IsEmpty(SqQueue Q) {

    if (Q.rear == Q.front)   return true//队空条件

    else return false;

}

  1. 入队

bool EnQueue(SqQueue &Q, ElemType x) {

    if ((Q.rear + 1) % MaxSize == Q.front) return false;      //队满

    Q.data[Q.rear] = x;

    Q.rear = (Q.rear + 1) % MaxSize;                          //队尾指针加1取模

    return true;

}

  1. 出队

bool DeQueue(SqQueue &Q, ElemType &x) {

    if (Q.rear == Q.front)      return false;                  //队空,报错

    x = Q.data[Q.front];

    Q.front = (Q.front + 1) % MaxSize;                         //队头指针加1取模

    return true;

}

    1. 队列的链式存储结构
      1. 队列的链式存储

队列的链式表示称为链队列,它实际上是一个同时带有队头指针和队尾指针的单链表。 头指针指向队头结点,尾指针指向队尾结点,即单链表的最后一个结点(注意与顺序表不同)。

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

typedef struct {                       //链式队列结点

    ElemType data;

    struct LinkNode *next;

}LinkNode;

typedef struct {                        //链式队列

    LinkNode *front, *rear;             //队列的队头和队尾指针

}LinkQueue;

当Q.front==NULL且Q.rear==NULL时,链式队列为空。

出队时,首先判断队是否为空,若不空,则取出队头元素,将其从链表中摘除,并让Q.front指向下一个结点(若该结点为最后一个结点,则置Q.front和Q.rear都为NULL)。 入队时,建立一个新结点,将新结点插入到链表的尾部,并改让Q.rear指向这个新插入的结点(若原队列为空队,则令Q.front也指向该结点)。

不难看出,不设头结点的链式队列在操作上往往比较麻烦,因此,通常将链式队列设计成一个带头结点的单链表,这样插入和删除操作就统一了。

用单链表表示的链式队列特别适合于数据元素变化比较大的情形,而且不存在队列满且产生溢出的问题。另外,加入程序中要使用多个队列,与多个栈的情形一样,最好使用链式队列,这样就不会出现存储分配不合理和“溢出”的问题。

      1. 链式队列的基本操作
  1. 初始化

void InitQueue(LinkQueue &Q) {

    Q.front = Q.rear = (LinkNode*)malloc(sizeof(LinkNode));     //建立头结点

    Q.front->next = NULL;                                       //初始为空

}

  1. 判空队

bool IsEmpty(LinkQueue Q) {

    if (Q.front == Q.rear) return true;

    else return false;

}

  1. 入队

void EnQueue(LinkQueue &Q, ElemType x) {

    LinkNode *s = (LinkNode*)malloc(sizeof(LinkNode));

    s->data = x; s->next = NULL//创建新结点,插入到链尾

    Q.rear->next = s;

    Q.rear = s;

}

  1. 出队

bool DeQueue(LinkQueue &Q, ElemType &x) {

    if (Q.front == Q.rear) return false; //空队

    LinkNode *p = Q.front->next;

    x = p->data;

    Q.front->next = p->next;

    if (Q.rear == p)

         Q.rear = Q.front;  //若原队列中只有一个结点,删除后变空

    free(p);

    return true;

}

    1. 习题
      1. 第一题

如果希望循环队列中的元素都能得到利用,则需设置一个标识域tag,并以tag的值为0或1来区分队头指针front和队尾指针rear相同时的队列状态是“空”还是“满”。试编写与此结构相应的入队和出队算法。

在循环队列的类型结构中,增设一个tag的整型变量,进队时置tag为1,出队时置tag为0(因为只有入队操作可能导致队满,也只有出队操作可能导致队空)。队列Q初始时,置tag=0,front=rear=0。这样的队列的4要素如下:

队空条件:Q.front==Q.rear且Q.tag==0。

队满条件:Q.front==Q.rear且Q.tag==1。

进队操作:Q.data[Q.rear]=x; Q.rear=(Q.rear+1)%MaxSize; Q.tag=1。

出队操作:x=Q.data[Q.front]; Q.front=(Q.front+1)%MaxSize; Q.tag=0。

  1. 设”tag”法的循环队列入队算法:

int EnQueue1(SqQueue &Q, ElemType x) {

    if (Q.front == Q.rear&&Q.tag == 1)

         return 0;                       //两个条件都满足时则队满

    Q.data[Q.rear] = x;

    Q.rear = (Q.rear + 1) % MaxSize;

    Q.tag = 1;                          //可能队满

    return 1;

}

  1. 设”tag”法的循环队列出队算法:

int DeQueue1(SqQueue &Q, ElemType &x) {

    if (Q.front == Q.rear&&Q.tag == 0)

         return 0;                       //两个条件都满足时则队满

    x = Q.data[Q.front];

    Q.front = (Q.front + 1) % MaxSize;

    Q.tag = 0;                          //可能队空

    return 1;

}

      1. 第二题

Q是一个队列,S是一个空栈,实现将队列中的元素逆置的算法。

【算法思想】将队列中的元素逐个地出队列,入栈;全部入栈后再逐个出栈,入队列。

代码:

void Inverser(Stack &S, Queue &Q) {

    ElemType x;

    //本算法实现将队列中的元素逆置

    while (!QueueEmpty(Q)) {

         DeQueue(Q,x);                                             //队列中全部元素依次出队

         Push(S, x);                                               //元素依次入栈

    }

    while (!StackEmpty(S)) {

         Pop(S, x);                                                //栈中全部元素依次出栈

         EnQueue(Q, x);                                            //再入队

    }

}

*完整代码:

#include <stdio.h>   

#include "stdlib.h" 

#include <iostream> 

using namespace std;

typedef int ElemType;

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

typedef struct {

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

    int top;                           //栈顶指针

}Stack;

typedef struct {

    ElemType data[MaxSize];            //存放队列元素

    int front, rear;                   //队头指针和队尾指针

}Queue;

void InitStack(Stack &S) {

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

}

void InitQueue(Queue &Q) {

    Q.rear = Q.front = 0;               //初始化队首、队尾指针

}

bool QueueEmpty(Queue Q) {

    if (Q.rear == Q.front)   return true//队空条件

    else return false;

}

bool DeQueue(Queue &Q, ElemType &x) {

    if (Q.rear == Q.front)      return false;                  //队空,报错

    x = Q.data[Q.front];

    Q.front = (Q.front + 1) % MaxSize;                         //队头指针加1取模

    return true;

}

bool Push(Stack &S, ElemType x) {

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

         return false;

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

    return true;

}

bool StackEmpty(Stack &S) {

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

         return true;

    else                              //不空

         return false;

}

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

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

         return false;

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

    return true;

}

bool EnQueue(Queue &Q, ElemType x) {

    if ((Q.rear + 1) % MaxSize == Q.front) return false;      //队满

    Q.data[Q.rear] = x;

    Q.rear = (Q.rear + 1) % MaxSize;                          //队尾指针加1取模

    return true;

}

void Inverser(Stack &S, Queue &Q) {

    ElemType x;

    //本算法实现将队列中的元素逆置

    while (!QueueEmpty(Q)) {

         DeQueue(Q,x);                                             //队列中全部元素依次出队

         Push(S, x);                                               //元素依次入栈

    }

    while (!StackEmpty(S)) {

         Pop(S, x);                                                //栈中全部元素依次出栈

         EnQueue(Q, x);                                            //再入队

    }

}

void main() {

    Stack S;

    InitStack(S);

    Queue Q;

    InitQueue(Q);

    EnQueue(Q, 100);

    EnQueue(Q, 101);

    Inverser(S, Q);

    ElemType x;

    DeQueue(Q, x);

    printf("The first element is %d\n", x);

    DeQueue(Q, x);

    printf("The second element is %d\n", x);

    system("pause");

}

      1. 第三题

利用两个栈S1,S2来模拟一个队列,已知栈的4个运算定义如下:

Push(S,x);                                     //元素x入栈S

Pop(S,x);                                      //S出栈并将出栈的值赋给x

StackEmpty(S);                                    //判断栈是否为空

StackOverflow(S);                                  //判断栈是否满

那么如何利用栈的运算来实现该队列的3个运算(形参由读者根据要求自己设计):

Enqueue;                                        //将元素x入队

Dequeue;                                        //出队,并将出队元素存储在x中

QueueEmpty;                                     //判断队列是否为空

【算法思想】利用两个栈S1和S2来模拟一个队列,当需要向队列中插入一个元素时,用S1来存放已输入的元素,即S1执行入栈操作。当需要出队时,则对S2执行出栈操作。由于从栈中取出元素的顺序是原顺序的逆序,所以,必须先将S1中的所有元素全部出栈并入栈到S2中,再在S2中执行出栈操作,即可实现出队操作,而在执行此操作前必须判断S2是否为空,否则会导致顺序混乱。当栈S1和S2都为空时队列为空。

总结如下:

  1. 对S2的出栈操作用做出队,若S2为空,则先将S1中的所有元素送入S2。
  2. 对S1的入栈操作用做入队,若S1满,必须先保证S2为空,才能将S1中的元素全部插入S2中。

代码:

入队算法:

int EnQueue(Stack &S1, Stack &S2, ElemType x) {

    if (!StackOverflow(S1)) {

         Push(S1, x);

         return 1;

    }

    if (StackOverflow(S1) && !StackEmpty(S2)) {

         printf("队列满");

         return 0;

    }

    if (StackOverflow(S1) && StackEmpty(S2)) {

         while (!StackEmpty(S1)) {

             Pop(S1, x);

             Push(S2, x);

         }

    }

    Push(S1, x);

    return 1;

}

出队算法:

void DeQueue(Stack &S1, Stack &S2, ElemType &x) {

    if (!StackEmpty(S2)) {

         Pop(S2, x);

    }

    else if (StackEmpty(S1)) {

         printf("队列为空");

    }

    else {

         while (!StackEmpty(S1)) {

             Pop(S1, x);

             Push(S2, x);

         }

         Pop(S2, x);

    }

}

判断队列为空的算法:

int QueueEmpty(Stack S1, Stack S2) {

    if (StackEmpty(S1) && StackEmpty(S2))

         return 1;

    else

         return 0;

}

  1. 栈和队列的应用

栈: 括号匹配、表达式求值、递归

队列: 层次遍历

    1. 习题
      1. 第一题

假设一个算数表达式中包含圆括号、方括号和花括号3种类型的括号,编写一个算法来判别表达式中的括号是否匹配,以字符”\0”作为算术表达式的结束符。

【算法思想】扫描每个字符,遇到花、中、圆的左括号进栈,遇到花、中、圆的右括号时检查栈顶元素是否为相应的左括号,若是,退栈,否则配对错误。 最后栈如果不为空也为错误。

代码:

bool BracketsCheck(char *str) {

    Stack S;

    ElemType e;

    InitStack(S);                              //初始化栈

    int i = 0;

    while (str[i] != '\0') {

         switch (str[i]) {

             //左括号入栈

         case '(': Push(S, '('); break;

         case '[': Push(S, '['); break;

         case '{': Push(S, '{'); break;

             //遇到右括号,检测栈顶

         case ')': Pop(S, e);

             if (e != '(') return false;

             break;

         case ']': Pop(S, e);

             if (e != '[') return false;

             break;

         case '}': Pop(S, e);

             if (e != '{') return false;

             break;

         default:

             break;

         }//switch

         i++;

    }//while

    if (!IsEmpty(S)) {

         printf("括号不匹配\n");

         return false;

    }

    else {

         printf("括号匹配\n");

         return true;

    }

}

      1. 第二题

利用一个栈实现以下递归函数的非递归计算:

  1.           n=0

 

Pn(x)=        2x                      n=1

 

 

             2xPn-1(x)-2(n-1)Pn-2(x)     n>1 

【算法思想】设置一个栈用于保存n和对应的Pn(x),栈中相邻元素的Pn(x)有题中关系。然后边出栈边计算Pn(x),栈空后该值就计算出来了。

代码:

double P(int n, double x) {

    struct stack {

         int no;                                //保存n

         double val;                            //保存pn(x)值

    }st[MaxSize];

    int top = -1, i;                           //top为栈st的下标值变量

    double fv1 = 1, fv2 = 2 * x;               //n=0,n=1时的初值

    for (i = n; i >= 2; i--) {

         top++;

         st[top].no = i;

    }                                           //入栈

    while (top >= 0) {

         st[top].val = 2 * x*fv2 - 2 * (st[top].no - 1)*fv1;

         fv1 = fv2;

         fv2 = st[top].val;

         top--;                                   //出栈

    }

    return fv2;

}

猜你喜欢

转载自blog.csdn.net/Ezra1991/article/details/83106848