数据结构:队列及其应用

news/2024/9/28 19:36:35 标签: 数据结构

队列(Queue)是一种特殊的线性表,它的主要特点是先进先出(First In First Out,FIFO)。队列只允许在一端(队尾)进行插入操作,而在另一端(队头)进行删除操作。以下是对队列的详细介绍:

一、基本概念

  • 队列的定义:队列是一种只允许在表的一端(队尾)进行插入操作,在另一端(队头)进行删除操作的线性表
  • 队头(Front):队列中允许删除的一端,又称为队首。
  • 队尾(Rear):队列中允许插入的一端。
  • 空队列:不包含任何元素的队列。

二、队列的主要操作

队列的主要操作包括入队(Enqueue)、出队(Dequeue)、查看队头元素(Peek/Front)和判断队列是否为空(IsEmpty)等。

  • 入队(Enqueue):在队列的队尾插入一个新元素。
  • 出队(Dequeue):从队列的队头删除一个元素,并返回该元素的值。
  • 查看队头元素(Peek/Front):返回队列队头元素的值,但不删除该元素。
  • 判断队列是否为空(IsEmpty):如果队列中没有任何元素,则返回true;否则返回false。

三、队列的分类(存储结构)

队列根据实现方式的不同,可以分为多种类型,如顺序队列、循环队列、链式队列等。

  • 顺序队列
  • 使用数组实现的队列,队头和队尾指针分别指向队列的首尾元素。顺序队列在插入和删除操作时可能会出现“假溢出”现象,即队列未满但因指针限制无法继续插入元素的情况。

初始化:

//initialize
#define MaxSize 50
typedef struct
{
    int rear,front;//rear指向队尾元素的下一个值,front指向队头元素
    Elemtype data[MaxSize];
}SqQueue;
  • 循环队列
  • 为了解决顺序队列的“假溢出”问题,引入了循环队列。在循环队列中,当队尾指针到达数组末尾时,会自动回到数组的开始位置,形成一个环状结构。循环队列需要牺牲一个存储单元来区分队列空和队列满的状态。

初始:front=rear=0;

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

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

队长:(rear-front+Max Size)%MaxSize 

队空判断条件:

循环队列为空的判断条件相对简单,即队头指针(front)和队尾指针(rear)相等。这是因为当队列为空时,没有元素被插入,所以队头和队尾都指向数组的起始位置(或某个约定的初始位置)。

队空判断条件front == rear

队满判断条件:

循环队列为满的判断条件则较为复杂。由于队尾指针在达到数组末尾后会回到数组开头,因此当队尾指针再次指向队头指针时,队列可能已满,也可能为空。为了区分这两种情况,通常采用以下几种方法之一来判断队列是否满:

  1. 牺牲一个元素空间
    这是最常见的方法。在循环队列中,约定当(rear + 1) % MaxSize == front时,认为队列已满。这里,MaxSize是队列所使用数组的大小,%是取模运算符。这种方法通过牺牲数组中的一个元素空间来区分队列满和队列空的状态。当(rear + 1) % MaxSize == front时,虽然从逻辑上看队尾指针和队头指针相邻,但实际上队列中还有一个空位置没有被使用,因此认为队列已满。

    队满判断条件(牺牲一个元素空间)(rear + 1) % MaxSize == front

         队空判断条件front == rear

   队长:(rear-front+MaxSize)%MaxSize

  1. 增设计数器或标志位:(size)
    另一种方法是增设一个计数器(记录队列中元素的数量)或标志位(记录队列的当前状态,如是否进行过插入或删除操作)。然而,这种方法需要额外的存储空间,并且增加了操作的复杂性。队空:size=0;  队满:size=maxSize;

  2. 改变front和rear的定义域
    还有一种较为特殊的方法是通过改变front和rear的定义域来区分队列满和队列空的状态。例如,可以约定front的初始值为数组的最大索引加1(或某个大于数组最大索引的值),而rear的初始值为0。这样,当front等于rear时,队列为空;而当rear再次等于front时(在循环过程中),队列满。但这种方法在实际应用中较为少见,因为它改变了front和rear的常规用法。

综上所述,循环队列队空和队满的判断条件主要取决于所采用的具体实现方法。其中,“牺牲一个元素空间”的方法因其简单性和高效性而被广泛采用。

图解:

代码: 
//initialize
void InitQueue(SqQueue &Q){
    Q.front=Q.rear=0;
}
//判断是否为空
bool isEempty(SqQueue Q){
    if(Q.rear===Q.front)
    return true;
    else
    return false;

}
//入队
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;
    return true;
        
}
//出队
bool DeQueue(SqQueue &Q,Elemtype &x){
    if(Q.front==Q.rear)
    return false;
    x=Q.data[Q.front];
    Q.front=(Q.front+1)%MaxSize;
    return true;
}
  • 链式队列
  • 使用链表实现的队列,每个节点包含数据域和指向下一个节点的指针。链式队列在插入和删除操作时不需要移动元素,具有较好的灵活性。
代码:
//
typedef struct{
    Elemtype  data;
    struct LinkNode *next;
}LinkNode;
typedef struct
{
    LinkNode *rear,*front;
}LinkQueue;

//initialize
void InitQueue(LinkQueue &Q){
    Q.front=Q.rear=(LinkNode*)malloc(sizeof(LinkNode));//建立头结点
    Q.front->next=NULL;
}

bool IsEmpty(LinkQueue Q){
    if(Q.front==Q.rear)
    return true;
    else
    return false;
}

void EnQueue(LinkQueue &Q,ElemType e){
    LinkNode *s=(LinkNode *)malloc(sizeof(LinkNode));
    s->data=e;
    s->next=NULL;
    Q.rear->next=s;
    Q.rear=s;
}

void DeQueue(LinkQueue &Q,ElemType &e){
    if(Q.front==Q.rear)
    return false;
    LinkNode *s=Q.front->next;
    e=s->data;
    Q.front->next=s->next;
    if(Q.rear==s)//队列中只有一个结点
    Q.front=Q.rear;//删除后变空
    free(s);
    return true;
}
双端队列:

1)能由输入受限的双端队列得到,但不能由输出受限的双端队列得到的是4,1,3,2。

2)能由输出受限的双端队列得到,但不能由输入受限的双端队列得到的是4,2,1,3。

3)既不能由输入受限的双端队列得到,又不能由输出受限的双端队列得到的是4,2,3,1。

四、队列的应用场景

队列在实际应用中有着广泛的应用,如:

  • 任务调度:在多任务系统中,可以使用队列来存储待执行的任务,系统按照队列中的顺序依次执行任务。
  • 消息传递:在分布式系统中,队列可以用作消息传递的媒介,发送方将消息发送到队列中,接收方从队列中取出消息并进行处理。
  • 缓存淘汰:在缓存系统中,可以使用队列的先进先出特性来淘汰最早进入缓存的数据项。
  • 并发控制:在多线程或多进程环境中,队列可以用于控制对共享资源的访问顺序,防止数据竞争和死锁等问题。
队列在计算机系统中的应用

队列在计算机系统中的应用非常广泛,以下仅从两个方面来阐述:第一个方面是解决主机与外部设备之间速度不匹配的问题,第二个方面是解决由多用户引起的资源竞争问题。

缓冲区的逻辑结构(2009):


对于第一个方面,仅以主机和打印机之间速度不匹配的问题为例做简要说明。主机输出数据给打印机打印,输出数据的速度比打印数据的速度要快得多,因为速度不匹配,若直接把输出的数据送给打印机打印,则显然是不行的。解决的方法是设置一个打印数据缓冲区,主机把要打印输出的数据依次写入这个缓冲区,写满后就暂停输出,转去做其他的事情。打印机就从缓冲区中按照先进先出的原则依次取出数据并打印,打印完后再向主机发出请求。主机接到请求后再向缓冲区写入打印数据。这样做既保证了打印数据的正确,又使主机提高了效率。由此可见,打印数据缓冲区中所存储的数据就是一个队列。


多队列出队/入队操作的应用(2016):


对于第二个方面, CPU (即中央处理器,它包括运算器和控制器)资源的竞争就是一个典型的例子。在一个带有多终端的计算机系统上,有多个用户需要 CPU 各自运行自己的程序,它们分别通过各自的终端向操作系统提出占用 CPU 的请求。操作系统通常按照每个请求在时间上的先后顺序,把它们排成一个队列,每次把 CPU 分配给队首请求的用户使用。当相应的程序运行结束或用完规定的时间间隔后,令其出队,再把 CPU 分配给新的队首请求的用户使用。这样既能满足每个用户的请求,又使CPU能够正常运行。

五、总结

队列是一种重要的数据结构,它遵循先进先出的原则进行元素的操作。队列的实现方式多样,包括顺序队列、循环队列和链式队列等。队列在实际应用中有着广泛的应用场景,如任务调度、消息传递、缓存淘汰和并发控制等。通过合理使用队列,可以提高系统的性能和稳定性。


http://www.niftyadmin.cn/n/5681722.html

相关文章

ant design vue做表单验证及form表单外验证、父子嵌套多个表单校验

1、form表单验证(若有时遇到输入框有值但是还是触发验证规则了&#xff0c;请检查form表单绑定正确吗、校验规则正确吗、表格数据字段名正确吗) <a-form:model"formState":label-col"{ span: 8 }":wrapper-col"{ span: 16 }":rules"rul…

flink设置保存点和恢复保存点

增加了hdfs package com.qyt;import org.apache.flink.api.java.functions.KeySelector; import org.apache.flink.api.java.tuple.Tuple2;import org.apache.flink.runtime.state.storage.FileSystemCheckpointStorage;import org.apache.flink.streaming.api.datastream.Dat…

《 Spring Boot实战:优雅构建责任链模式投诉处理业务》

Spring Boot实战&#xff1a;优雅构建责任链模式投诉处理业务 文章目录 Spring Boot实战&#xff1a;优雅构建责任链模式投诉处理业务场景描述&#xff1a;代码实现Step 1: 定义投诉请求类Step 2: 定义抽象处理者类Step 3: 实现具体的处理者类Step 4: 创建投诉处理链Step 5: 创…

基于Hive和Hadoop的哔哩哔哩网站分析系统

本项目是一个基于大数据技术的哔哩哔哩平台分析系统&#xff0c;旨在为用户提供全面的哔哩哔哩视频数据和深入的用户行为分析。系统采用 Hadoop 平台进行大规模数据存储和处理&#xff0c;利用 MapReduce 进行数据分析和处理&#xff0c;通过 Sqoop 实现数据的导入导出&#xf…

如何评估婚恋交友小程序的投资回报率

在这个数字化的时代&#xff0c;越来越多的人选择通过手机应用程序寻找自己的另一半。随着婚恋交友小程序的兴起&#xff0c;编辑h17711347205如何评估这类产品的投资回报率&#xff08;ROI&#xff09;成为了投资者和运营者关注的重点。本文将探讨如何有效地评估婚恋交友小程序…

如何构建鲁棒高性能 Prompt 的方法?

你好&#xff0c;我是三桥君 在当今时代&#xff0c;利用大型语言模型如ChatGPT进行文本生成和交互已成为一种趋势。然而&#xff0c;要充分发挥这些模型的能力&#xff0c;尤其是在生产环境中&#xff0c;我们需要精心设计和优化我们的提示词&#xff08;prompt&#xff09;。…

【C语言】动态内存管理:malloc、calloc、realloc、free

本篇介绍一下C语言中的malloc/calloc/realloc。 使用这些函数需要包含头文件<stdlib.h>。malloc/calloc/realloc申请的空间都是 堆区的。 1.malloc和free 1.1 malloc C语言提供了一个动态内存开辟的函数malloc&#xff0c;函数原型如下。 void* malloc(size_t size);…

汽车总线之---- LIN总线

Introduction LIN总线的简介&#xff0c;对于传统的这种点对点的连接方式&#xff0c;我们可以看到ECU相关的传感器和执行器是直接连接到ECU的&#xff0c;当传感器和执行器的数量较少时&#xff0c;这样的连接方式是能满足要求的&#xff0c;但是随着汽车电控功能数量的不断增…