数据结构·第3章【栈和队列】

news2024/11/25 20:35:50

顺序栈

栈(Stack)是限定仅在表的一端进行插入或删除操作的线性表。通常称插入删除的一端为栈顶(top),另一端称为栈底(bottom)。

typedef struct{
        DataType  data[StackSize];
        int  top;
}SeqStack;

基本操作

在这里插入图片描述

示意

在这里插入图片描述
在没有元素时,top=-1,栈的第一个元素的位序是0

基本操作实现

入栈

void Push(seqStack &S, DataType x)
{    if(StackFull(S))
       Error(“Stack overflow”);
      S.data[++S->top]=x;
}

出栈

DataType Pop(seqStack &S)
{    if(StackEmpty(S))
       Error(“Stack underflow”);
     return  S.data[S->top--];
}

链栈

typedef struct Stacknode{
     DataType data
     struct stacknode *next
}StackNode;

typedef struct{
        StackNode *top; //栈顶指针
}LinkStack;

基本操作的实现

入栈

void Push(LinkStack &S, DataType x){
     StackNode *p=(StackNode*)malloc(sizeof(StackNode));
     p->data=x;
     p->next=S->top;
     S->top=p;
}

出栈

DataType Pop(LinkStack &S){  
   DataType x;
   StackNode *p=S->top;
   if(StackEmpty(S))Error(“Stack underflow”);
   x=p->data;
   s->top=p->next;
   free(p);
   return x;
}

需要注意的是: p − > n e x t p->next p>next指向下面的元素以栈顶为上,栈底为下

顺序栈和链栈的比较

时间效率:

  • 所有操作都只需常数时间
  • 顺序栈和链式栈在时间效率上难分伯仲

空间效率:

  • 顺序栈须说明一个固定的长度
  • 链式栈的长度可变,但增加结构性开销

迷宫问题

顺序栈实现

// 定义迷宫中通道块的数据结构
typedef struct {
    int ord;            // 通道块在路经上的“序号”
    PosType seat;        // 通道块在迷宫中的“坐标位置”
    int di;                // 从此通道块走向下一通道块的“方向”
} SElemType;

// 寻找从起点 start 到终点 end 的路径
Status MazePath(MazeType maze, PosType start, PosType end) {
    SqStack S;
    InitStack(S);        // 初始化栈

    PosType curpos = start;    // 设定“当前位置”为“入口位置”
    int curstep = 1;           // 探索第一步
    
    do {
        if (Pass(curpos)) {
            // 当前位置可以通过,即是未曾走到过的通道块
            FootPrint(curpos);                    // 留下足迹
            SElemType e = {curstep, curpos, 1};    // 新建通道块
            Push(S, e);                           // 加入路径
            if (curpos == end) {
                return (TRUE);                    // 到达出口(终点)
            }
            curpos = NextPos(curpos, 1);           // 下一位置是当前位置的东邻
            curstep++;                            // 探索下一步
        } else {
            // 当前位置不能通过
            if (!StackEmpty(S)) {
                SElemType e;
                Pop(S, e);
                while (e.di==4 && !StackEmpty(S)) {
                    MarkPrint(e.seat); Pop(S, e);     // 留下不能通过的标记,并退回一步
                }
                if (e.di < 4) {
                    e.di++;
                    Push(S, e);                          // 换下一个方向探索
                    curpos = NextPos(e.seat, e.di);      // 设定当前位置是该新方向上的相邻块
                }
            }
        }
    } while (!StackEmpty(S));
    return (FALSE);    // 无法从起点到达终点
}

表达式求值

OperandType EvaluateExpression()  {
    //算术表达式求值的算符优先算法。设OPTR和OPND
    //分别为运算符栈和运算数栈,OP为运算符集合。
    InitStack (OPTR); Push(OPTR,’#’);
    InitStack (OPND); c=getchar();
    while (c!=‘#’||GetTop(OPTR)!=‘#’) {
       if (!In (c, OP)) { Push (OPND, c); c= getchar();}
                                                     //不是运算符则进栈
       else 
         switch (Precede( GetTop (OPTR), c)){
           case<:                               //栈顶元素优先权低
                   Push (OPTR, c); c= getchar();
                   break;
           case=:    //脱括号并接受下一字符
                        Pop (OPTR, c); c= getchar();
                        break;
           case>:          //退栈并将运算结果入栈
                        Pop (OPTR, theta);
                        Pop (OPND, b); Pop (OPND, a);
                        Push (OPND, Operate (a, 
                        theta, b)); 
                        break;
           }//switch
    }//while
    return GetTop(OPND);
}//EvaluateExpression

递归

  • 问题的定义是递归的。例如,数学中的阶乘函数、二阶Fibonacci函数等。
  • 有的数据结构,如二叉树、广义表等,由于结构本身固有的递归特性,则它们的操作可递归的描述。
  • 有些问题,虽然问题本身没有明显的递归结构,但用递归求解比迭代求解更简单。如八皇后问题、Hanoi塔问题等

没什么好说的这一块

队列

先进先出

链队列

 typedef struct QNode {// 结点类型
    QElemType      data;
    struct QNode  *next;
  } QNode, *QueuePtr;
typedef struct { // 链队列类型
    QueuePtr  front;  // 队头指针
    QueuePtr  rear;   // 队尾指针
} LinkQueue;

示意图

在这里插入图片描述
易知:队列的next指针是指向当前元素在队列中后面的元素

基本操作

在这里插入图片描述

入队列

Status EnQueue (LinkQueue &Q, QElemType e) {
    // 插入元素e为Q的新的队尾元素
    p = (QueuePtr) malloc (sizeof (QNode));
    if(!p) exit (OVERFLOW);  //存储分配失败
    p->data = e;   p->next = NULL;
    Q.rear->next = p;    Q.rear = p;
    return OK;
}

其中,自己在写算法时也要注意添加类似if(!p) exit (OVERFLOW); //存储分配失败这样的判断异常的语句

出队列

Status DeQueue (LinkQueue &Q, QElemType &e) {
  // 若队列不空,则删除Q的队头元素,
  //用 e 返回其值,并返回OK;否则返回ERROR
   if (Q.front == Q.rear)    return ERROR;
   p = Q.front->next;   e = p->data;
   Q.front->next = p->next;
   if (Q.rear == p)  Q.rear = Q.front;
   free (p);      return OK;
}

判断异常if (Q.front == Q.rear) return ERROR;
这一步的特判if (Q.rear == p) Q.rear = Q.front;

顺序队列

循环

#define MAXQSIZE  100  //最大队列长度
  typedef struct {
    QElemType *base; // 动态分配存储空间
    int front;   // 头指针,若队列不空,
                 //  指向队列头元素
    int  rear;  // 尾指针,若队列不空,
          //指向队列尾元素 的下一个位置
  } SqQueue;

这里的 r e a r rear rear是指向队尾元素的下一个位置
链队列的 r e a r rear rear指向队尾元素

基本操作

大体同链队列

入队列

Status EnQueue (SqQueue &Q, ElemType e) {   
     // 插入元素e为Q的新的队尾元素
    if ((Q.rear+1) % MAXQSIZE == Q.front) return ERROR;     //队列满
    Q.base[Q.rear] = e;
    Q.rear = (Q.rear+1) % MAXQSIZE;
    return OK;
}

好好理解一下这个,(Q.rear+1) % MAXQSIZE的意思(结合下面Q.rear = (Q.rear+1) % MAXQSIZE;

出队列

Status DeQueue (SqQueue &Q, ElemType &e) { 
    // 若队列不空,则删除Q的队头元素,
   // 用e返回其值,并返回OK;  否则返回ERROR
    if (Q.front == Q.rear)  return ERROR;
    e = Q.base[Q.front];
    Q.front = (Q.front+1) % MAXQSIZE;
    return OK;
}

同样注意:Q.front = (Q.front+1) % MAXQSIZE;

银行问题(离散事件模拟)

假设某银行有4个窗口营业,从早晨银行开门起不断有客户进入银行。每个窗口每一时刻只能接待一个客户,对于刚进入银行的客户,如果某个窗口的业务员正空闲,则可上前办理业务;反之,若4个窗口均有客户所占,他会排在人数最少的队伍后面。现要编制一个程序以模拟银行的这种业务活动并计算一天客户在银行逗留的平均时间。

思路

  • 大体思路是事件驱动

代码

typedef struct {
         int OccurTime;  //事件发生时刻
         int NType;   //事件类型,0表示到达事件,1至4表示四个窗口的离开事件
}Event, ElemType;     //事件类型,有序链表LinkList的数据元素类型
typedef LinkList EventList  //事件链表类型,定义为有序链表
typedef struct {
         int ArrivalTime;            //到达时刻
         int Duration;              //办理事务所需时间
}QElemType;                        //队列的数据元素类型

EventList     ev;                        //事件表
Event         en;                       //事件
LinkQueue     q[5];                    //4个客户队列   
QElemType    customer;           //客户记录
int   TotalTime, CustomerNum;   //累计客户逗留时间,客户数
int com (Event a, Event b);    //依事件a的发生时刻<或=或>事件b的发生时刻分别返回-1或0或1

void OpenForDay() {                     //初始化操作
       TotalTime=0;   CustomerNum=0;   //初始化累计时间和客户数为0
       InitList (ev);                 //初始化事件链表为空表
       en.OccurTime=0;  en.NType=0;  //设定第一个客户到达事件 
       OrderInsert (ev, en, cmp);   //插入事件表
       for (i=1; i<=4; ++i)   InitQueue (q[i]); //置空队列
}//OpenForDay 

void CustomerArrived()  {
    //处理客户到达事件,en.NType=0。
    ++CustomerNum;
    Random (durtime, intertime);  //生成随机数
    t=en.OccurTime+ intertime; //下一客户到达时刻
    if (t<CloseTime)        //银行尚未关门,插入事件表
      OrderInsert (ev, (t, 0), cmp);
    i=Minimum (q);                        //求长度最短队列
    EnQueue (q[i], (en.OccurTime, durtime));
    if(QueueLength (q[i])==1)
        OrderInsert (ev, (en.OccurTime + durtime, i), cmp);  //设定第i队列的一个离开事件并插入事件表
}// CustomerArrived

void CustomerDeparture()  {
    //处理客户离开事件,en.NType>0。
    i=en.NType;  DelQueue (q[i], customer);          //删除第i队列的排头客户
    TotalTime+=en.OccurTime- customer.Arrivaltime; //累计客户逗留时间
    if (!QueueEmpty(q[i])) //设定第i队列的一个离开事件并插入事件表
        GetHead (q[i], customer);
        OrderInsert (ev, (en.OccurTime + custom.Duration, i), (*cmp)());                  
}// CustomerDeparture

void Bank_ Simulation (int CloseTime) {
        OpenForDay();
        while (! ListEmpty(ev) ) {   //初始化
           DelFirst (GetHead (ev), p); 
           en=GetCurElem(p);
           if(en.NType==0) CustomerArrived();    //处理客户到达事件
           else CustomerDeparture() ; //处理客户离开事件
   }                //计算平均逗留时间
 printf(“The Average Time is %f\n”, (float)TotalTime/CustomerNum);
}// Bank_ Simulation 

习题

3.2

简述栈和线性表的差别

题解
线性表是具有相同特性的数据元素的一个有限序列。栈是限定仅在表尾进行插入或删除操作的线性表

3.18

在这里插入图片描述
这道题看题目,思路上没什么难度。但是需要注意一下,写算法的细节:

  • InitStack(s)
  • ElemType x
  • 最后if(s.size!=0) return False;(左右括号数量不等),我觉得我个人,很可能写完while和switch语句后,就没考虑到这个细节,值得重新写一遍

3.21

在这里插入图片描述
在这里插入图片描述

3.32

试利用循环队列编写求 k k k阶斐波那契序列中前 n + 1 n+1 n+1 ( f 0 , f 1 , … , f n ) (f_0, f_1 ,…, f_n) (f0f1fn) 的算法,要求满足: f n ≤ m a x , f n + 1 > m a x f_n≤max, f_{n+1}>max fnmax,fn+1>max,其中 m a x max max 为某个约定的常数。(注意:本题所用循环队列的容量仅为 k k k,则在算法执行结束时,留在循环队列中的元素应是所求 k k k阶斐波那契序列中的最后 k k k f n − k + 1 … … f n f_{n-k+1}……f_n fnk+1……fn

long Fib_CirQueue(long Q[], int k, int& rear, int& n, long max) {
    // 使用循环队列Q[k]计算k阶斐波那契数fn,要求fn是不超过max的最大的斐波那契数,
    // 函数返回计算结果,rear是队尾指针,指向实际队尾位置。
    long sum; // sum为计算过程中的中间变量,记录前k项斐波那契数的和
    int i; // 循环计数器

    // 给前0~k-1项赋初值
    for (i = 0; i < k - 1; i++) {
        Q[i] = 0;
    }
    Q[k - 1] = 1; // 第k项斐波那契数初始化为1
    rear = n = k - 1; // 队尾指针,指示实际队尾位置;n为当前fj计数,初始值为k-1

    sum = 0; // 初始化sum为0,开始计算第k项斐波那契数

    while (1) {
        // 累加前k项的斐波那契数的值
        for (i = 0; i < k; i++) {
            sum = sum + Q[(rear - i + k) % k];
        }
        if (sum > max) {
            break; // 若计算出来的斐波那契数超过了max,则退出循环
        }
        n++; // 计数器自增1,记录当前计算到了第几项斐波那契数
        rear = (rear + 1) % k; // 队列中仅存入到fn项,队尾指针向后移动
        Q[rear] = sum; // 将计算结果存入循环队列中,并取代已无用的项
    }
    return Q[rear]; // 返回计算结果
}

这道题有点复杂,主要是 k k k阶斐波那契数列的概念

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/584673.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

缩减虚拟机堆空间的方式,缓解32位cpu上虚拟内存地址空间限制导致的内存分配失败崩溃

缩减虚拟机堆空间的方式&#xff0c;缓解32位cpu上虚拟内存地址空间限制导致的内存分配失败崩溃 前言Matrix使用说明效果验证 前言 瑞芯微平台应用开发&#xff0c;目前RK3288芯片应用还是比较广泛&#xff08;成本低&#xff09;&#xff0c;它是一个32位cpu&#xff0c;并且…

Halcon中的一些3D算子

一、记录一些Halcon里的关于3D的算子 1.read_object_model_3d 从文件读取一个3d模型 如下图&#xff0c;读的一个ply文件出来是个3d点云模型 2.visualize_object_model_3d 交互式展示3d模型 即上个算子读出来后&#xff0c;通过这个算子可以把3d模型显示出来旋转、平移&am…

SpringFramework 中CollectionUtils 工具类的使用

CollectionUtils是Spring框架中的一个工具类&#xff0c;提供了一系列对集合的操作方法。 import org.springframework.util.CollectionUtils;import java.util.*;public class CollectionUtilDemo {public static void main(String[] args) {//判断一个集合或Map是否为空&…

OWASP ZAP alerts

前提 使用OWASP ZAP对网站进行安全扫描&#xff0c;扫描后发现一些警告。 使用警告名称在百度进行搜索就能看到在OWASP ZAP网站上对应警告的解释。 可以在如下地址输入alert查询 https://www.zaproxy.org/docs/alerts/ Missing Anti-clickjacking Header 见https://www.zap…

SQL Server 2008R2安装图文教程(附SQL Server下载安装包)

SQL Server 2008R2 安装教程 演示系统&#xff1a;Windows server 2008R2 安装包&#xff1a;下载传送门 下载并解压安装包&#xff0c;找到解压的安装包&#xff0c;双击【setup.exe】 如果是你的服务器没有安装.NET Framework 3.5&#xff0c;那会弹出个提示框&#xff0c;…

CODEC 基础知识

&#xfeff; ASDOUT : ADC data output  DSDIN : DAC data input  LRCK : Left/Right data alignment clock  SCLK : Bit clock &#xfeff; MCLK 256 LRCK 或者 MCLK384 LRCK different audio data formats:  I2S …

公司新来了个拿18K出来的测试,让我见识到了什么叫真正的测试扛把子...

今天上班开早会就是新人见面仪式&#xff0c;听说来了个很厉害的大佬&#xff0c;年纪还不大&#xff0c;是上家公司离职过来的&#xff0c;薪资已经达到中高等水平&#xff0c;很多人都好奇不已&#xff0c;能拿到这个薪资应该人不简单&#xff0c;果然&#xff0c;自我介绍的…

如何编写快速高效的SQL查询(四)——优化特定类型的查询与样例

这一节&#xff0c;我们将介绍如何优化特定类型的查询。 本节介绍的多数优化技巧都和特定的版本有关&#xff0c;所以对于未来MySQL的版本未必适用。毫无疑问&#xff0c;某一天优化器自己也会实现这里列出的部分或者全部优化技巧。 优化COUNT()查询 COUNT()聚合函数&#xf…

Hive安装部署

1、Hive安装地址 ①Hive官网地址 Apache Hive ②文档查看地址 GettingStarted - Apache Hive - Apache Software Foundation ③下载地址 Index of /dist/hive ④github地址 GitHub - apache/hive: Apache Hive 2、 安装Hive 1&#xff09;把apache-hive-3.1.3-bin.ta…

“AI换脸”诈骗背后,如何应对黑灰产使用手段?

目录 诈骗是如何发生的&#xff1f; AI换脸诈骗的操作防范 AI换脸的风险分析与技术防范 近日&#xff0c;警方通报了一起使用智能AI技术进行电信诈骗的案件。被骗者是福州市某科技公司法人代表郭先生&#xff0c;他通过微信视频接到自己好友的电话&#xff0c;对方佯装需要借…

帕累托改进和帕累托最优、卡尔多-希克斯改进

根据目标个数&#xff0c;分为单目标规划&#xff0c;以及多目标规划。多目标的规划是去找折中的解集合&#xff0c;既pareto最优解集合。对优化目标超过3个以上的&#xff0c;称之为超多目标优化问题。 帕累托改进描述的就是在没有人变得不好的前提下让有些人更好的过程。帕累…

GPT虚拟直播Demo系列(二)|无人直播间实现虚拟人回复粉丝

摘要 虚拟人和数字人是人工智能技术在现实生活中的具体应用&#xff0c;它们可以为人们的生活和工作带来便利和创新。在直播间场景里&#xff0c;虚拟人和数字人可用于直播主播、智能客服、营销推广等。接入GPT的虚拟人像是加了超强buff&#xff0c;具备更强大的自然语言处理能…

Postman 接口测试神器

Postman 接口测试神器 Postman 是一个接口测试和 http 请求的神器&#xff0c;非常好用。 官方 github 地址: Postman Inc. GitHub Postman 的优点&#xff1a; 支持各种的请求类型: get、post、put、patch、delete 等支持在线存储数据&#xff0c;通过账号就可以进行迁移…

HY-M5 三维机器视觉系统在工业自动化生产的应用

行业背景&#xff1a; 如今科学技术有了日新月异的变化&#xff0c;工业自动化也在不断地发展。然而&#xff0c;在高强度、高精准的工作环境下&#xff0c;人工操作已经不能适应企业的发展需求&#xff0c;于是机器人的出现便提供了高效快捷的解决方案。为了实现自动化生产并确…

AUTOSAR通信篇 - CAN网络通信(二:CanIf)

在上一篇&#xff0c;我们介绍了CAN模块&#xff0c;接下来我们介绍在CAN模块之上的模块Can Interface&#xff08;CanIf&#xff09;模块。在AUTOSAR软件架构中&#xff0c;CanIf也在BSW层&#xff0c;它处于CAN模块之上紧挨着CAN模块。CanIf是一个硬件独立层&#xff0c;具有…

【MySQL】如何实现单表查询?

在我们对数据进行操作时&#xff0c;查询无疑是至关重要的&#xff0c;查询操作灵活多变&#xff0c;我们可以根据开发的需求&#xff0c;设计高效的查询操作&#xff0c;把数据库中存储的数据展示给用户。 文章目录 前言1. 基础查询1.1 基础查询语法1.2 基础查询练习 2. 条件查…

算法与数据结构-复杂度分析(上)

文章目录 什么是大 O 复杂度表示法为什么要用大 O 复杂度表示法如何分析一段代码的时间复杂度1、只关注循环执行次数最多的一段代码2、加法法则&#xff1a;总复杂度等于量级最大的那段代码的复杂度3、乘法法则&#xff1a;嵌套代码的复杂度等于嵌套内外代码复杂度的乘积 几种常…

Unity老动画系统Animation

1、创建老动画系统 给要制作动画的GameObeject添加Animation组件 2、Animation参数 Animation&#xff1a;默认播放的动画 Animations&#xff1a;该动画组件可以控制的所有动画 Play AutoMatically&#xff1a;是否一开始就自动播放默认动画 Animate Physics&#xff1a;动画…

【JavaSE】Java基础语法(三十二):Stream流

文章目录 1. 体验Stream流2. Stream流的常见生成方式3. Stream流中间操作方法【应用】4. Stream流终结操作方法【应用】5. Stream流的收集操作 1. 体验Stream流 案例需求 按照下面的要求完成集合的创建和遍历 创建一个集合&#xff0c;存储多个字符串元素把集合中所有以"…

Python文件打包成exe文件

文章目录 背景安装pyinstaller开始打包总结 背景 今天因为在线将pdf转为word被收费了&#xff0c;有点不爽&#xff0c;所以自己动手撸一个pdf转word的小工具&#xff0c;想着打包成exe给朋友使用&#xff0c;万一哪天会用到呢&#xff1f; 安装pyinstaller 打开cmd命令窗口…