第三章 栈和队列【数据结构与算法】【精致版】

简介: 第三章 栈和队列【数据结构与算法】【精致版】

前言

2023-11-2 22:12:24

以下内容源自《【数据结构与算法】【精致版】》

仅供学习交流使用

第 3 章 栈和队列

第2章介绍了线性表的概念,从数据结构上看,栈和队列也是线性表,只不过是两种特殊的 线性表。栈只允许在表的一端进行插入或删除操作,而队列只允许在表的一端进行插入操作、在另一端进行删除操作。因而,栈和队列也可以被称为操作受限的线性表。从数据类型的角度来看,栈与队列是与线性表不同的抽象数据结构。

3.1 应用实例

应用实例一 迷宫求解问题

这是实验心理学中的一个经典问题,心理学家用一个无顶盖的大盒子做成“迷宫”从然后把一只老鼠从入口处赶进迷宫。迷宫中设置了很多隔断,对老鼠的前进方向构成多处障碍,在迷宫的唯一出口处放置了一块奶酪,吸引老鼠寻找正确的通路以到达出口。

可以用一个mxn的方阵表示迷宫,0和1分别表示迷宫中的通路和障碍。设计

一个程序,对任意设定的迷宫,求出一条从入口到出口的通路,或得到没有通路的结论。

计算机解迷宫时,通常是采用“穷举求解”的方法,即从入口出发,沿某方向向前探索,若能 走通则继续往前走;否则沿原路返回,换一个方向再继续探索,直至所有可能的通路都探索到为止。为了保证在任何位置上都能沿原路退回,显然要用具有后进先出特性的栈来保存从入口到当前位置的路径。

应用实例二“马”踏棋盘问题

将棋子“马”随机地放在国际象棋8x8棋盘Board[8][8]的某个方格中,“马”按走棋规则进 行移动,要求每个方格只进入一次,走遍棋盘上所有的64个方格。编写(非)递归程序,求出“马” 的行走路线,并按求出的行走路线将数字1,2,…,64依次填入一个8x8的方阵并输出。

求解时可采用栈的数据结构,即将“马”的行走顺序压人栈中。每次在多个可走的位置中选 择其中一个进行试探,其余未曾试探过的可走位置必须用适当的结构妥善管理,以备试探失败时 的“回溯”(悔棋)使用。

以上实例的分析与实现将在3.4节详细介绍。

3.2栈

3.2.1 栈的概念及运算

栈(stack)是一种只允许在一端进行插人和删除操作的线性表,它是一
种操作受限的线性表

在表中允许进行插入和删除的一端称为“栈顶”(top),另一端称为“栈底”(bottom)。

栈的插入操作通常称为“入栈”或“进栈”(push),

而栈的删除操作则称为“出栈”或“退栈”(pop)。

当栈中无数据元素时,称为“空栈”。

栈具有后进先出的特性,因此又称为“后进先出”(Last In First Out,LIFO)表

栈的示意图

栈的基本运算有以下几种。
① InitStack(S) 初始化:初始化一个新的栈。
② Empy(S )栈的非空判断:若栈S不空,则返回TRUE;否则,返回FALSE。
③ Push(S.x) 入楼:在栈S的顶部插入元素x,若栈满,则返回FALSE;否则,返回TRUE
④ Pop(S) 出栈:若栈S不空,则返回栈顶元素,并从栈顶中删除该元素;否则,返回空元素  
⑤ GetTop(S) 取栈顶元素:若栈S不空,则返回栈顶元素;否则返回空元素NULL。
⑥ SetEmpty(S) 置栈空操作:置栈S为空栈。

栈是一种特殊的线性表,因此栈可采用顺序存储结构存储,也可以使用链式存储结构存储。

3.2.2栈的顺序存储结构

1. 顺序栈
利用顺序存储的方式实现的栈称为“顺序栈”。类似于顺序表的定义,栈中的数据元素用一个
预设的足够长度的一维数组来实现:ElemType elem[MAXSIZE]。栈底位置可以设置在数组任
一个端点,而栈顶是随着插人和删除而变化的。用int top来作为栈顶的指针,指明当前栈顶的    位置,同时将elem和top封装在一个结构中。顺序栈的类型描述如下。
#define MAXSIZE<栈最大元素数>
typedef struct{ 
  ElemType elem[MAXSIZE];
  int top;
}SeqStack;

定义一个指向顺序栈的指针:

SeqStack *s;

常将0下标端设为栈底,这样空栈时校顶指针s->top=-1;入栈时,栈顶指针加1,即s->top++;出栈时,栈顶指针减1,即s->top–。

1-顺序栈.h
#include<stdio.h> 
#include<stdlib.h> 
//在使用的使用命名 
//typedef int ElemType;
//定义顺序栈 
#define MAXSIZE 10
typedef int  ElemType; 
typedef struct{ 
  ElemType elem[MAXSIZE];
  int top;
}SeqStack;
//(1)置空栈
//首先建立栈空间,然后初始化栈顶指针。
SeqStack * InitStack(){
  SeqStack *s;
  s=(SeqStack * ) malloc(sizeof( SeqStack));  
  s->top=-1;
  return s;
}
//(2)判空栈
int Empty(SeqStack *s){
  if(s->top==-1) return 1;  //代表空 
  else return 0;
}
//(3)入栈
int Push(SeqStack *s, ElemType x){
  if(s->top==MAXSIZE-1) return 0;//栈满不能入栈,否则将造成“上溢” 
  else {s->top++;
  s->elem[s->top]=x;
  return 1;
  } 
}
//(4)出栈
int Pop( SeqStack *s, ElemType *x){
  if(Empty(s)) return 0;  //栈空不能出栈
  else { 
    *x=s->elem[s->top];//栈顶元素存入*x,返回
    s->top--;
    return 1;
  }
}
//(5)取栈顶元素
ElemType GetTop(SeqStack *s){
  if(Empty(s)) return 0;//栈空
  else return (s->elem[s->top]);
}

1-顺序栈测试.c

typedef int ElemType; 
#include "1-顺序栈.h" 
int main(){
  SeqStack *s=InitStack();
  printf("%d",Empty(s));//1 
  int x1=1;
  Push(s,x1); 
  int x2=2;
  Push(s,x2);
  printf("%d",Empty(s));//0
  int x3;
  Pop(s,&x3);
  printf("%d",x3);//2
  int t=GetTop(s);
  printf("%d",t);//1
}
2. 多栈共享邻接空间

栈的共享中最常见的是两栈共享

假设两个栈共享一维数组elem[MAXSIZE],则可以利用栈的“栈底位置不变,栈顶位置动态变化”特性,两个栈底分别为-1和MAXSIZE,而它们的栈顶都往中间方向延伸。因此,只要整个数组elem[MAXSIZE]未被满,无论哪个栈的人栈都不会发生上溢。

两栈共享的数据结构可定义如下。

typedef struct{
  ElemType elem[ MAXSIZE];
  int lefttop;//左栈栈顶位置指示器
  int righttop;//右栈栈顶位置指示器
} Dupsqstack;

两个栈共享邻接空间的示意图如图3-4所示。左栈入栈时,栈顶指针加1,右栈人栈时,栈顶指针减1。由于两个栈顶均可向中间伸展,互补余缺,因此使得每个栈的最大空间均大于MAXSIZE/2。

为了识别左右栈,必须另外设定标志:

char status;
status ='L';  //左栈
status ='R';//右栈

在进行栈操作时,需指定栈号status=L’为左栈,status='R’为右栈;判断栈满的条件如下:

s->lefttop+1=s->righttop;
2-共享栈.c

勘误:
P63共享栈的初始化,应该是二级指针或者返回一级指针
P64入栈的右栈进栈有误,应为righttop

#include<stdio.h>
#include<stdlib.h> 
#define FALSE 0
#define TRUE 1
//定义顺序栈 
#define MAXSIZE 10
typedef int  ElemType; 
typedef struct{
  ElemType elem[MAXSIZE];
  int lefttop;//左栈栈顶位置指示器
  int righttop;//右栈栈顶位置指示器
} Dupsqstack;
//(1)初始化操作
//错误;初始化不了 
//s的初始化,并不能传递到实参
//需要使用二级指针 
//int InitDupStack(Dupsqstack *s){
//  //创建两个共享邻接空间的空栈由指针s指出
//  if ((s=(Dupsqstack * )malloc(sizeof(Dupsqstack)))==NULL)  
//    return FALSE;
//  s->lefttop=-1;
//  s->righttop=MAXSIZE;
//  return TRUE;
//}
//修改 
int InitDupStack(Dupsqstack **s){
  //创建两个共享邻接空间的空栈由指针s指出
  if ((*s=(Dupsqstack * )malloc(sizeof(Dupsqstack)))==NULL)  
    return FALSE;
  (*s)->lefttop=-1;
  (*s)->righttop=MAXSIZE;
  return TRUE;
}
//(2)入栈操作
int PushDupSrack(Dupsqstack *s,char status, ElemType x){
  //把数据元素x。压入左栈或右栈
  if(s->lefttop+1==s->righttop)return FALSE;//栈满
  if(status=='L') s->elem[++s->lefttop]=x;//左栈进栈
  if(status=='R') s->elem[--s->righttop]=x;//右栈进栈
  else return FALSE;//参数错误
  return TRUE;
}
//(3)出栈操作
ElemType PopDupStack(Dupsqstack * s,char status){
  //从左楼(satus='L')或右栈(status='R')退出栈顶元素
  if(status=='L'){    
    if(s->lefttop<0) return 999;//左栈为空
    else return (s->elem[s->lefttop--]);//左栈出栈
  }else if(status=='R'){  
    if(s->righttop>MAXSIZE-1) return 999;//右栈为空
    else return (s->elem[s->righttop++]);//右栈出栈
  }else return 999;//参数错误
}
// 去栈顶元素
ElemType GetDupsqTop(Dupsqstack * s,char status){
  if(status=='L'){    
    if(s->lefttop<0) {return 999;}//左栈为空
    else return (s->elem[s->lefttop]);//取左栈顶元素 
  }else if(status=='R'){  
    if(s->righttop>MAXSIZE-1) return 999;//右栈为空
    else return (s->elem[s->righttop]);//取右栈顶元素 
  }
}
int main(){
  Dupsqstack *d;
  InitDupStack(&d);
  int l0=1;
  int r0=2;
  PushDupSrack(d,'L',l0);
  PushDupSrack(d,'R',r0);
  int l=GetDupsqTop(d,'L');
  int r=GetDupsqTop(d,'R');
  printf("%d\n",l);//1
  printf("%d\n",r);//2
}

3.2.3栈的链式存储结构

栈也可以采用链式存储结构表示,这种结构简称为“链栈”。

在一个链栈中,栈底就是结表的最后一个结点,而栈顶总是链表的第一个结点采用带头结点的单链表实现栈。因为栈的插入和删除操作仅限制在表头位置进行,所以链表的表头指针top就作为栈顶指针,top始终指向当前栈顶元素前面的头结点,即top->next为栈顶元素,当top->next==NULL,则代表栈空

1)链栈定义
//链栈的C语言定义如下。
typedef int DataType;
typedef struct Stacknode{
  DataType data;
  struct Stacknode * next;
}slStacktype;
2)链栈操作
3-链栈.h
#include<stdio.h>
#include<stdlib.h> 
#define FALSE 0
#define TRUE 1
//链栈的C语言定义如下。
typedef int DataType;
typedef struct Stacknode{
  DataType data;
  struct Stacknode * next;
}slStacktype;
// 初始化
slStacktype* Init(){
  slStacktype *p;
  if((p=(slStacktype * )malloc(sizeof( slStacktype)))==NULL) 
    return NULL;
  return p;
} 
//(1)入栈操作
//将元素x压入链栈top中
int PushLstack(slStacktype * top, DataType x){  
  slStacktype *p;
  //申请一个结点
  if((p=(slStacktype * )malloc(sizeof( slStacktype)))==NULL) 
    return FALSE;
  p->data=x;
  p->next=top->next;
  top->next=p;
  return TRUE;
}
//(2)出栈操作
//从链栈top中删除栈顶元素
DataType PopLstack(slStacktype * top){
  slStacktype * p;
  DataType x;
  if(top->next==NULL){//空栈  
    printf("此栈为空!");  
    return;
  }
  p=top->next;
  top->next=p->next;  
  x=p->data;
  free(p);
  return x;
}
//取栈顶元素
DataType GetLsPop(slStacktype * top){
  if(top->next==NULL){//空栈  
    printf("此栈为空!");  
    return;
  }
  DataType x=top->next->data;
  return x;
}

3-链栈.c

#include"3-链栈.h"
int main(){
  slStacktype *sl=Init();
  int x=1;
  PushLstack(sl,x);
  int y=GetLsPop(sl);
  printf("%d",y);//1 
  int z=PopLstack(sl);
  printf("%d",z);//1
}
(3)多个链栈的操作

可将多个单链栈的栈顶指针放在一个一维数组中统一管理。

设一维数组top[M]:

slStacktype* top[M]

其中top[0] ,top[1],…,top[i],…,top[M-1]指向M个不同的链栈,分别是M个链栈的 栈顶指针,这样只需确定链栈号i,然后以top[i]为栈顶指针进行栈操作,即可实现各种操价。多个链栈示意图如图3-6所示。

多个链栈的基本操作
4-多个链栈.c
#include<stdio.h>
#include<stdlib.h> 
#define FALSE 0
#define TRUE 1
#define M 10 
//链栈的C语言定义如下。
typedef int DataType;
typedef struct Stacknode{
  DataType data;
  struct Stacknode * next;
}slStacktype;
// 初始化
void Init(slStacktype * top[M],int i){
  if((top[i]=(slStacktype * )malloc(sizeof( slStacktype)))==NULL) 
    printf("初始化出错");
    return;
} 
//①入栈操作
//将元素x压人链栈top[i]中
int PushDupLs(slStacktype * top[M],int i, DataType x){
  Init(top,i);
  slStacktype * p;
  //申请一个结点
  if((p=(slStacktype *)malloc(sizeof(slStacktype)))==NULL){
    return FALSE;   
  }
  p->data=x;
  p->next=top[i]->next;
  top[i]->next=p;
  return TRUE;
}
//②出栈操作
//从链栈top[i]中删除栈顶元素
DataType PopDupLs(slStacktype * top[M],int i){
  slStacktype *p;
  DataType x;
  if(top[i]->next==NULL){//空栈
    printf("此栈为空!");
    return;
  }
  p=top[i]->next;
  top[i]->next=p->next;
  x=p->data;
  free(p);
  return x;
}
//取栈顶元素
DataType GetTopDupLs(slStacktype * top[M],int i){
  slStacktype *p;
  DataType x;
  if(top[i]->next==NULL){//空栈
    printf("此栈为空!");
    return;
  }
  p=top[i]->next; 
  x=p->data;
  free(p);
  return x;
}
int main(){
  slStacktype * top[M];
  int x1=1;
  PushDupLs(top,1,x1);
  int x11=GetTopDupLs(top,1);
  printf("%d",x11);//1
  int x2=2;
  PushDupLs(top,2,x2);
  int x22=GetTopDupLs(top,2);
  printf("%d",x22);//2
}

在上面的两个算法中,当指定栈号i(0<=i<=M-1)时,则只对第i个链栈操作,不会影响其他链栈。

3.2.4栈的应用

0. 括号匹配

判断都是括号所构成的字符串是否括号都是一一对应的

5-括号匹配.c
typedef char ElemType; 
#include "1-顺序栈.h"
int Match(char x,char y){
  if(x=='('){
    if(y==')'){
      return 1;
    }
  }
  if(x=='['){
    if(y==']'){
      return 1;
    }
  }
  if(x=='{'){
    if(y=='}'){
      return 1;
    }
  }
  return 0;
}
void BracketMatch(char *str){ 
  SeqStack* _S=InitStack();
  SeqStack S=*_S;
  int i;
  char ch;
  for(i=0;str[i]!='\0';i++){
    switch(str[i]){
      case'(':
      case'[':
      case'{': 
        Push(&S,str[i]);
        break;  
      case ')':
      case ']':
      case '}': 
        if(Empty(&S)){
          printf("右括号多余!");  
          return;
        }else{
          ch=GetTop(&S);
          if(Match(ch,str[i])){
            Pop(&S,&ch);
          }else{
            printf("对应的左右括号不同类!");  
            return;
          }
        } 
    }
  }
  if(Empty(&S))
    printf("括号匹配!");
  else
    printf("左括号多余!");
}
int main(){
  char str[10];
  printf("输入括号字符串\n"); 
  gets(str);
  printf("%s",str);
  BracketMatch(str);
}

运行结果如下

1. 算术表达式求值(上机)

表达式求值是程序设计语言编译中的一个最基本问题,它的实现方法是栈的一个典型的应用实例,

在计算机中,任何一个表达式都是由操作数(operand)、运算符(operalor)和界限符(delimiter)组成的。其中操作数可以是常数,也可以是变量或常量的标识符;运算符可以是算术运算符、关系运算符和逻辑运算值;界限符为左右括号和标识表达式结束的结束符。在本节中,仅讨论简单算术表达式的求值问题。假设在这种表达式中只含加、减、乘、除四则运算,所有的运算对象均为整型常数,表达式的结束符为“#”,即仅含符号+、一、*、/、(、)和#。

算术四则运算的规则如下。

①先乘除、后加减。

②同级运算时先左后右。

③先括号内,后括号外。

3.3 队列

3.3.1队列的概念及其运算

队列(queue)是另一种限定性线性表,它只允许插人在表的一端进行,而删除在表的另一端进行,允许插人的一端叫队尾(rear),而允许删除的一端叫队头(front)。

队列的插人操作通常为“入队”或“进队”,而队列的删除操作则称为“出队”或“退队”。当队列中无数据元素时,所“空队列”。

根据队列的定义可知,队头元素总是最先进队列,也总是最先出队列;队尾元素总最后进队列,因而也是最后出队列。这种表是按照先进先出(First In First Out,FIFO)的原则组织数据的,因此,队列也被称为“先进先出”表。

在队列上进行的基本操作如下。

①队列初始化:InitQutoe(ql
相始条件:队列q不存在,
操作结果:构造了一个空队列:
②人队操作:InQuene(q.x)
初始条件:队列q存在。
操作结果:对已存在的队列q,插人一个元素x到队尾。操作成功,返回值为TRUE,否则返回值为FALSE
③出队操作:OutQueue(q.x)
初始条件:队列g存在且非空。
操作结果:删除队首元素,并返回其值。操作成功,返回值为TRUE,否则返回值为FALSE
④读队头元素:FrontQueue(q,x)
初始条件:队列q存在且非空。
操作结果:读队头元素,并返回其值,队列不变。操作成功,返回值为TRUE,否则返回值为FALSE.
⑤判队空操作:EmptyQueue(q)
初始条件:队列q存在
操作结果:若q为空队列则返回为TRUE,否则返回为FALSE.

队列的顺序存储结构可以 简称为“顺序队列”,另外再设立两个指示器:一个为指向队头元素位置的指示器front,另一个为指向队尾元素位置的指示器rear

C语言中,数组的下标是从0开始的,因此为了算法设计的方便,在此约定:初始化队列时,空队列的front=rear=-1,当插人新的数据元素时,尾指示器rear加1,而当队头元素出队列时,头 指示器front加1。另外还约定,在非空队列中,头指示器front总是指向队列中实际队头元素的 前面一个位置,而尾指示器rear总是指向队尾元素(这样的设置是为了某些运算的方便,并不是唯一的方法)。

顺序队列的类型定义如下

define MAXSIZE<最大元素数>//队列的最大容量
vypedef struct{
  ElemType elem[MAXSIZE];//队列元素的存储空间
  int rear,front;//队尾、队头指针
}SeQueue;

定义一个指向队列的指针变量:SeQueue *sq;

申请一个顺序队列的存储空间:sq=(SeQueue*)malloc(sizeof(SeQueue));

存在“假溢出”的问题

3.3.2循环队列

解决假溢出的方法之一是将队列的数据区elem[0~MAXSIZE-1]看成头、尾相接的循环结构,即规定最后一个单元的后维为第个单元,这样整个数据区就像一个环,我们形象地称之为循环队列

入队时队尾指针加1操作修改为

sq->rear=(sq->rear+1)%MAXSIZE

出队时队尾指针加1操作修改为

sq->rear=(sq->front+1)%MAXSIZE

又产生一个新问题队空和队满的条件相同都为front==rear

解决的方法之一是少用一个元素空间,把如图所示视为队满。

队满的条件是(rear+1)%MAXSIZE==front

就能和队空区别开。

另外一种方法是附设一个存储队列中元素个数的变量,如num,当num == 0时队空,当num==MAXSIZE时队满

还有一种在习题(8)中

下面的循环队列及操作依据少用一个元素空间来实现的。

循环队列的类型定义
#define MAXSIZE 10
//下面的循环以列及操作依据少用个元素空间来实现
//循环队列的类型定义及基本运算如下。
typedef int ElemType;
typedef struct{ 
  ElemType elem [MAXSIZE];//队列的存储区
  //队头队尾指针
  int front, rear;  
}CSeQueue;//循环队列
循环队列的基本运算
6-循环队列.c
#include<stdio.h>
#include<stdlib.h> 
#define FALSE 0
#define TRUE 1
#define MAXSIZE 10
//下面的循环以列及操作依据少用个元素空间来实现
//循环队列的类型定义及基本运算如下。
typedef int ElemType;
typedef struct{ 
  ElemType elem [MAXSIZE];//队列的存储区
  //队头队尾指针
  int front, rear;  
}CSeQueue;//循环队列
//(1)置空队
CSeQueue * IniseQueue(){
  CSeQueue * q=(CSeQueue *)malloc(sizeof(CSeQueue));
  q->front=q->rear=MAXSIZE-1;
  return q;
}
//(2)入队
int InSeQueue( CSeQueue * q,ElemType x){
  if((q->rear+1)%MAXSIZE==q->front){//队满不能人队
    printf("队满");
    return FALSE;
  }else{
    q->rear=(q->rear+1)%MAXSIZE;
    q->elem[q->rear]=x;
    return TRUE;//人队完成
  }
}
//(3)出队
int OutSeQueue( CSeQueue *q , ElemType *x){
  if(q->front==q->rear){
    printf("队空");
    return FALSE;
  }else{
    q->front=(q->front+1)%MAXSIZE;
    *x=q->elem[q->front];//读出队头元素
    return TRUE;//出队完成
  }
}
//(4) 判断队空
int EmptySeQueue(CSeQueue *q){
  if(q->front==q->rear) return TRUE;
  else return FALSE;
}
int main(){
  CSeQueue *cs=IniseQueue();
  int x=1;
  InSeQueue(cs,x);
  printf("%d",EmptySeQueue(cs));//0
  int x0;
  OutSeQueue(cs,&x0);
  printf("%d",x0);//1
  printf("%d",EmptySeQueue(cs));//1
}

3.3.3 链队列

链式存储的队列称为链队列

可以采用带头结点的单链表表示队列。

头指针始终指向头结点,尾指针指向当前最后一个元素结点

//链队列的数据类型描述如下。
typedef struct node{ 
  DataType data;
  struct node * next;
}QNode;
//链队列结点的类型
typedef struct{  
  QNode * front;
  QNode * rear;
} LQueue;//将头尾指针封装在一起的链队列
链队列的基本运算
7-链队列.c
#include<stdio.h>
#include<stdlib.h> 
#define FALSE 0
#define TRUE 1
#define MAXSIZE 10
typedef int  DataType; 
//链队列的数据类型描述如下。
typedef struct node{ 
  DataType data;
  struct node * next;
}QNode;
//链队列结点的类型
typedef struct{  
  QNode * front;
  QNode * rear;
} LQueue;//将头尾指针封装在一起的链队列
//(1)创建一个带头结点的空队
LQueue * Init_LQueue(){
  LQueue *q; QNode*p;
  q=(LQueue*)malloc( sizeof(LQueue));//申请头尾指针结点  
  p=(QNode*)malloc( sizeof(QNode));//申请链队列头结点  
  p->next=NULL;
  q->front=q->rear=p;
  return q;
}
//(2)入队
void InLQueue(LQueue *q , DataType x){ 
  QNode *p;
  p=(QNode*)malloc(sizeof(QNode));//申请新结点  
  p->data=x;
  p->next=NULL; 
  q->rear->next=p;
  q->rear=p;
}
//(3)判队空
int Empty_LQueue(LQueue *q){
  if(q->front==q->rear) return 1;//代表空 
  else return 0;
}
//(4)出队
int Out_LQueue(LQueue *q, DataType *x){
  QNode *p;
  if(Empty_LQueue(q)){
    printf("队空");
    return FALSE;
  }
  else{ 
    p=q->front->next;
    q->front->next=p->next;
    *x=p->data;//队头元素放x中
    free(p);
    if(q->front->next==NULL)//只有一个元素时,出队后队空,修改队尾指针
      q->rear=q->front;
    return TRUE;
  }
}
int main(){
  LQueue *lq=Init_LQueue();
  printf("%d",Empty_LQueue(lq));//1
  int x=1;
  InLQueue(lq,x);
  printf("%d",Empty_LQueue(lq));//0
  int x0;
  Out_LQueue(lq,&x0);
  printf("%d",x0);//1 
  printf("%d",Empty_LQueue(lq));//0
}

3.4 实例分析与实现

应用实例一 迷宫求解

应用实例二 马踏棋盘

应用实例三 计算器

3.5 算法总结 ——递归与分治 算法

实验

应用实例一 迷宫求解

应用实例二 马踏棋盘

应用实例三 计算器

习题3

1.单项选择题

(1)(2010考研真题)若元素a,b,e,d,e,f依次进栈,允许进栈、退栈操作交替进行,但不允许连续三次进行退栈操作,则不可能得到的出栈序列是____。 D

A. d,c,e,b,f,a

B. c,b,d,a,e,f

C. b.c,a,e,f,d

D. a,f,e,d,c,b

(2)若某堆栈的输人序列为1,2,3,n-l,n,输出序列的第1个元素为n,则第i个输出元素为____。 A

A. n-i+1

B. n-1

C. i

D.哪个元素都有可能

(3)以数组Q[0…m-1]存放循环队列中的元素,变量rear和qulen分别指示循环队列中队尾元素的实际位置和当前队列中元素的个数,队列第一个元素的实际位置是___。 D

A. rear-qulen

B. rear-qulen+m

C. m-qulen

D. (1+(rear-qulentm))mod m

(4)设输人元素为1,2,3,A,B,输人次序为123AB,元素经过栈后到达输出序列,当所有元素均到达输出序列后,序列____是不可能的。 C

A. BA321

B. A3B21

C. B32A1

D. AB321

(5)(2012年考研真题)已知操作符包括“+”“一”“”“/”“(”和“)”。将中缀表达式a+b-a((c+d)/e-)+g转换为等价的后缀表达式ab+acd+e/f-*-g+时,用栈来存放暂时还不能确定运算次序的操作符。若栈初始时为空,则转换过程中同时保存在栈中的操作符的最大个数是___。 A

A. 5

B. 7

C. 8

D.11

2.综合题

(2) 回文是指正读反读均相同的字符序列,如“abba”和“abdba"均是回文,但“good"不是回文。试写一个算法判定给定的字符串是否为回文。

2.c

#include<string.h>
typedef char ElemType; 
#include "1-顺序栈.h" 
int Match(char x,char y){
  if(x==y){   
    return 1;
  }
  return 0;
}
void BracketMatch(char *str){ 
  SeqStack* _S=InitStack();
  SeqStack S=*_S;
  int i;
  char ch;
    int len=strlen(str); 
    int mid=len/2;
  for(i=0;str[i]!='\0';i++){
    if(i==mid&&len%2==1){
      continue;
    }
    if(i<mid){      
      Push(&S,str[i]); 
    }else{
      if(Empty(&S)){
        printf("右字符多余!");  
        return;
      }else{
        ch=GetTop(&S);
        if(Match(ch,str[i])){
          Pop(&S,&ch);
        }else{
          printf("对应的左右字符不同!");  
          return;
        }
      } 
    }
  }
  if(Empty(&S))
    printf("回文!");
  else
    printf("左字符多余!");
}
int main(){
  char str[10];
  printf("输入字符串\n");
  gets(str);
  printf("%s",str);
  BracketMatch(str);
}

运行结果如下

(6) 假设以带头结点的循环链表表示队列,并且只设一个指针指向队尾元素结点,试编写相应的置空队、判队空、入队和出队算法。

6.c

#include<stdio.h>
#include<stdlib.h> 
#define FALSE 0
#define TRUE 1
#define MAXSIZE 10
typedef int  DataType; 
//链队列的数据类型描述如下。
typedef struct node{ 
  DataType data;
  struct node * next;
}QNode;
//链队列结点的类型
typedef struct{  
  QNode * rear;
} XQueue;//将尾指针封装在一起的链队列
//(1)创建一个带头结点的空队
XQueue * Init_XQueue(){
  XQueue *q; QNode*p;
  q=(XQueue*)malloc(sizeof(XQueue));//申请尾指针结点  
  p=(QNode*)malloc(sizeof(QNode));//申请循环队列头结点  
  p->next=p;
  q->rear=p;
  return q;
}
//(2)入队
void InXQueue(XQueue *q , DataType x){ 
  QNode *p;
  p=(QNode*)malloc(sizeof(QNode));//申请新结点  
  p->data=x;
  p->next=q->rear->next; 
  q->rear->next=p;
  q->rear=p;
}
//(3)判队空
int Empty_XQueue(XQueue *q){
  if(q->rear->next==q->rear) return 1;//代表空 
  else return 0;
}
//(4)出队
int Out_XQueue(XQueue *q, DataType *x){
  QNode *p;
  if(Empty_XQueue(q)){
    printf("队空");
    return FALSE;
  }
  else{ 
    p=q->rear->next->next;//q->rear->next相当于q->front 
    q->rear->next->next=p->next;
    *x=p->data;//队头元素放x中
    free(p);
    if(q->rear->next->next==NULL)//只有一个元素时,出队后队空,修改队尾指针
      q->rear=q->rear->next;
    return TRUE;
  }
}
int main(){
  XQueue *lq=Init_XQueue();
  printf("%d",Empty_XQueue(lq));//1
  int x1=1;
  InXQueue(lq,x1);
  printf("%d",Empty_XQueue(lq));//0
  int x2=2;
  InXQueue(lq,x2);
  int y1;
  Out_XQueue(lq,&y1);
  printf("%d",x1);//1 
  int y2;
  Out_XQueue(lq,&y2);
  printf("%d",y2);//2
  printf("%d",Empty_XQueue(lq));//0
}

(8) 在循环队列中,可以设置一个标志域tag,以区分当尾指针和头指针相等时,队列状态是“空”还是“满”(tag的值为0表示“空”,tag的值为1表示“满”),编写此结构相应的队列初始化、入队、出队算法。

8.c

#include<stdio.h>
#include<stdlib.h> 
#define FALSE 0
#define TRUE 1
#define MAXSIZE 2
//下面的循环以列及操作依据少用个元素空间来实现
//循环队列的类型定义及基本运算如下。
typedef int ElemType;
typedef struct{ 
  ElemType elem [MAXSIZE];//队列的存储区
  //队头队尾指针
  int front, rear;
  int flag; 
}CSeQueue;//循环队列
//先声明一下,就不会有Warning 
int EmptySeQueue(CSeQueue *q);
int ManSeQueue(CSeQueue *q);
//(1)置空队
CSeQueue * IniseQueue(){
  CSeQueue * q=(CSeQueue *)malloc(sizeof(CSeQueue));
  q->front=q->rear=MAXSIZE-1;
  q->flag=0;
  return q;
}
//(2)入队
int InSeQueue( CSeQueue * q,ElemType x){
  if(ManSeQueue(q)){//队满不能人队
    printf("队满");
    return FALSE;
  }else{
    q->rear=(q->rear+1)%MAXSIZE;
    q->elem[q->rear]=x;
    q->flag=1;
    return TRUE;//人队完成
  }
}
//(3)出队
int OutSeQueue( CSeQueue *q , ElemType *x){
  if(EmptySeQueue(q)){
    printf("队空");
    return FALSE;
  }else{
    q->front=(q->front+1)%MAXSIZE;
    *x=q->elem[q->front];//读出队头元素
    q->flag=0;
    return TRUE;//出队完成
  }
}
//(4) 判断队空
int EmptySeQueue(CSeQueue *q){
  if(q->front==q->rear&&q->flag==0) return TRUE;
  else return FALSE;
} 
//(5)判断队满
int ManSeQueue(CSeQueue *q){
  if(q->front==q->rear&&q->flag==1) return TRUE;
  else return FALSE;
}
int main(){
  CSeQueue *cs=IniseQueue();
  printf("%d",EmptySeQueue(cs));//1
  int x1=1;
  InSeQueue(cs,x1);
  int x2=2;
  InSeQueue(cs,x2);
  printf("%d",ManSeQueue(cs));//1
  int y1;
  OutSeQueue(cs,&y1);
  printf("%d",y1);//1
  printf("%d",EmptySeQueue(cs));//0
  int y2;
  OutSeQueue(cs,&y2);
  printf("%d",y2);//2
  printf("%d",EmptySeQueue(cs));//1
}

最后

2023-11-2 23:24:48

我们都有光明的未来

不必感谢我,也不必记得我

祝大家考研上岸

祝大家工作顺利

祝大家得偿所愿

祝大家如愿以偿

点赞收藏关注哦

相关文章
|
3天前
|
算法 安全 测试技术
golang 栈数据结构的实现和应用
本文详细介绍了“栈”这一数据结构的特点,并用Golang实现栈。栈是一种FILO(First In Last Out,即先进后出或后进先出)的数据结构。文章展示了如何用slice和链表来实现栈,并通过golang benchmark测试了二者的性能差异。此外,还提供了几个使用栈结构解决的实际算法问题示例,如有效的括号匹配等。
golang 栈数据结构的实现和应用
|
16天前
|
存储 Java
【数据结构】优先级队列(堆)从实现到应用详解
本文介绍了优先级队列的概念及其底层数据结构——堆。优先级队列根据元素的优先级而非插入顺序进行出队操作。JDK1.8中的`PriorityQueue`使用堆实现,堆分为大根堆和小根堆。大根堆中每个节点的值都不小于其子节点的值,小根堆则相反。文章详细讲解了如何通过数组模拟实现堆,并提供了创建、插入、删除以及获取堆顶元素的具体步骤。此外,还介绍了堆排序及解决Top K问题的应用,并展示了Java中`PriorityQueue`的基本用法和注意事项。
22 5
【数据结构】优先级队列(堆)从实现到应用详解
|
7天前
|
存储 算法 前端开发
深入理解操作系统:进程调度与优先级队列算法
【9月更文挑战第25天】在操作系统的复杂世界中,进程调度是维持系统稳定运行的核心机制之一。本文将深入探讨进程调度的基本概念,分析不同的进程调度算法,并着重介绍优先级队列算法的原理和实现。通过简洁明了的语言,我们将一起探索如何优化进程调度,提高操作系统的效率和响应速度。无论你是计算机科学的初学者还是希望深化理解的专业人士,这篇文章都将为你提供有价值的见解。
01_设计一个有getMin功能的栈
01_设计一个有getMin功能的栈
|
3天前
|
前端开发
07_用队列实现栈
07_用队列实现栈
06_用栈来求解汉诺塔问题
06_用栈来求解汉诺塔问题
05_用一个栈实现另一个栈的排序
05_用一个栈实现另一个栈的排序
03_如何仅用递归函数和栈操作逆序一个栈
03_如何仅用递归函数和栈操作逆序一个栈
|
3天前
|
测试技术
02_由两个栈组成的队列
02_由两个栈组成的队列
|
7天前
|
存储
下一篇
无影云桌面