顺序循环队列与链队列

简介: 今天学习了队列,一种是顺序循环队列,一种是链队列,我个人认为链队列相对好用一点,毕竟链队列不用考虑“假溢出”的问题,下面是我整理的关于队列的一些基本操作

我们先来看个问题:
【问题描述】
实现循环队列的基本操作。(循环队列最大长度不超过20)
【输入形式】
输入若干个整数(以空格分隔),其中0表示做出队操作,不为0的整数为入队元素。
【输出形式】
依次输出队列的全部元素,若队列为空,则输出“empty”
【样例输入1】
1 2 3 4 5 6
【样例输出1】
1 2 3 4 5 6
【样例输入2】
1 2 3 0 0 4 0 5
【样例输出2】
4 5
【样例输入3】
1 0 2 0 3 0
【样例输出3】
empty
【样例输入4】
1 0 2 0 0 3 0 0 0
【样例输出4】
empty
下面我们将分别通过顺序循环队列和链队列来实现以上操作

一、顺序循环队列

顺序循环队列需要两个指针,一个用来入队,一个用来出队
顺序循环队列是个在数组中移动的队列
顺序循环队列主要要考虑“假溢出”的问题,为了防止“假溢出”,我们通过(front+1)%MAX来实现

1.1 初始化一个顺序循环队列

#include<stdio.h>
#include<malloc.h>
#define MAX 10
typedef struct Queue{
    int *base;       //可以理解为一个数组
    int front;       //指向队头的指针   可以理解为数组的下标
    int rear;        //指向队尾的指针   可以理解为数组的下标
    int len;         //队列的长度,方便打印队列
}Queue,*PQueue;
int Init_Queue(PQueue Q){
    Q->base=(int *)malloc(MAX*sizeof(int));   //初始化分配一个空间
    Q->front=Q->rear=0;                //初始化队头队尾在一起
    Q->len=0;                          //初始化队列长度为0
    return 1;
}

1.2 判断队列是否为空

int Empty_Queue(PQueue Q){
    if(Q->front==Q->rear)     //当队头和队尾指针在一起的时候,说明是个空队
    return 1;
    else
    return 0;
}

1.3 入队

入队前一定要先判断队列是否已经满了,满了就不能再入队了

int Push_Queue(PQueue Q,int e){
    if((Q->rear+1)%MAX==Q->front) return 0;    //首先要判断队列是否满了
    Q->base[Q->rear]=e;           //满了无法入队,没满可以入队
    Q->rear=(Q->rear+1)%MAX;      //这个方法可以让队列成为循环队列
    Q->len++;                     //每次入队队列长度增加1
    return 1;
}

1.4 出队

出队前一定要判断队列是否已经空了,如果队列空了,那么就没办法出队了

int Pop_Queue(PQueue Q,int *e){
    if(Q->front==Q->rear) return 0;   //出队前要先判断是否队空
    *e=Q->base[Q->front];             //队列不空可以出队
    Q->front=(Q->front+1)%MAX;        //这个方法可以循环出队
    Q->len--;                         //出队后队长减1
    return 1;
}

1.5 打印队列

int Print_Queue(PQueue Q){
    int i;
    for(i=0;i<Q->len;i++){
        printf("%d ",Q->base[Q->front+i]);    //将队列打印出来
    }
    printf("\n");
    return 1;
}

1.6 代码实现

1.6.1 完整代码

#include<stdio.h>
#include<malloc.h>
#define MAX 10
typedef struct Queue{
    int *base;
    int front;
    int rear;
    int len;
}Queue,*PQueue;
int Init_Queue(PQueue Q){
    Q->base=(int *)malloc(MAX*sizeof(int));
    Q->front=Q->rear=0;
    Q->len=0;
    return 1;
}
int Empty_Queue(PQueue Q){
    if(Q->front==Q->rear)
    return 1;
    else
    return 0;
}
int Push_Queue(PQueue Q,int e){
    if((Q->rear+1)%MAX==Q->front) return 0;
    Q->base[Q->rear]=e;
    Q->rear=(Q->rear+1)%MAX;
    Q->len++;
    return 1;
}
int Pop_Queue(PQueue Q,int *e){
    if(Q->front==Q->rear) return 0;
    *e=Q->base[Q->front];
    Q->front=(Q->front+1)%MAX;
    Q->len--;
    return 1;
}
int Get_Queue(PQueue Q,int *e){
    if(Q->front==Q->rear) return 0;
    *e=Q->base[Q->front];
    return 1;
}
int Print_Queue(PQueue Q){
    int i;
    for(i=0;i<Q->len;i++){
        printf("%d ",Q->base[Q->front+i]);
    }
    printf("\n");
    return 1;
}
int main(){
    Queue Q;
    int e;
    Init_Queue(&Q);
    while(scanf("%d",&e)==1){
        if(e==0){
            Pop_Queue(&Q,&e);
        }else{
            Push_Queue(&Q,e);
        }
    }
    if(Empty_Queue(&Q)==1){
        printf("empty\n");
    }else{
        Print_Queue(&Q);
    }
    return 0;
}

1.6.2 运行结果

1.png

二、链队列(带头结点单链表)

我们用的是带头结点的单链表,方便出队
链队列需要两个指针,分别用来入队和出队
链队列不用考虑“假溢出”问题,操作起来更加方便

2.1 初始化一个链队列

#include<stdio.h>
#include<malloc.h>
typedef struct Node{       //每个结点
    int data;
    struct Node *next;
}Node,*PNode; 
typedef struct Queue{      //保存两个指针
    PNode front,rear;
}Queue,*PQueue;
int Init_Queue(PQueue Q){
    PNode P=(PNode)malloc(sizeof(Node));
    P->next=NULL;
    Q->front=Q->rear=P;    //初始队尾指针在队头指针位置
    return 1;
}

2.2 判断队列是否为空

int Empty_Queue(PQueue Q){
    if(Q->front==Q->rear)    //当队尾指针在队头指针处时,队列为空
    return 1;
    else
    return 0;
}

2.3 入队

链队列入队不用考虑“假溢出”问题,操作起来方便

int Push_Queue(PQueue Q,int e){
    PNode Pnew=(PNode)malloc(sizeof(Node)); //新建一个结点,用于入队
    Pnew->next=NULL;     
    Pnew->data=e;   
    Q->rear->next=Pnew;      //入队操作只用尾指针rear
    Q->rear=Pnew;
    return 1;
}

2.4 出队

int Pop_Queue(PQueue Q,int *e){
    if(Q->front==Q->rear) return 0;     //出队先判断是否队空
    *e=Q->front->next->data;
    if(Q->front->next==Q->rear){        //当只剩一个有效结点的时候,为了避免尾指针丢失
        Q->front->next=NULL;
        Q->rear=Q->front;               //我们删除结点后要让尾指针指向头结点
    }else{
        Q->front->next=Q->front->next->next;   
    }
    return 1;
}

2.5 打印队列

int Print_Queue(PQueue Q){
    PNode P=Q->front->next;
    while(P){
        printf("%d ",P->data);    //和打印单链表类似
        P=P->next;
    }
    printf("\n");
}

2.6 代码实现

2.6.1 完整代码

#include<stdio.h>
#include<malloc.h>
typedef struct Node{
    int data;
    struct Node *next;
}Node,*PNode;
typedef struct Queue{
    PNode front,rear;
}Queue,*PQueue;
int Init_Queue(PQueue Q){
    PNode P=(PNode)malloc(sizeof(Node));
    P->next=NULL;
    Q->front=Q->rear=P;
    return 1;
}
int Empty_Queue(PQueue Q){
    if(Q->front==Q->rear)
    return 1;
    else
    return 0;
}
int Push_Queue(PQueue Q,int e){
    PNode Pnew=(PNode)malloc(sizeof(Node));
    Pnew->next=NULL;
    Pnew->data=e;
    Q->rear->next=Pnew;
    Q->rear=Pnew;
    return 1;
}
int Pop_Queue(PQueue Q,int *e){
    if(Q->front==Q->rear) return 0;
    *e=Q->front->next->data;
    if(Q->front->next==Q->rear){
        Q->front->next=NULL;
        Q->rear=Q->front;
    }else{
        Q->front->next=Q->front->next->next;
    }
    return 1;
}
int Get_Queue(PQueue Q,int *e){
    if(Q->front==Q->rear) return 0;
    *e=Q->front->next->data;
    return 1;
}
int Print_Queue(PQueue Q){
    PNode P=Q->front->next;
    while(P){
        printf("%d ",P->data);
        P=P->next;
    }
    printf("\n");
}
int main(){
    Queue Q;
    Init_Queue(&Q);
    int e;
    while(scanf("%d",&e)==1){
        if(e==0){
            Pop_Queue(&Q,&e);
        }else{
            Push_Queue(&Q,e);
        }
    }
    if(Empty_Queue(&Q)==1){
        printf("empty\n");
    }else{
        Print_Queue(&Q);
    }
    return 0;
}

2.6.2 运行结果

2.png

三、总结

1.顺序循环队列入队和出队都要考虑“假溢出”的问题

2.链队列出队时要考虑是否是最后一个有效结点,避免尾结点的丢失

3.相比之下,链队列不用考虑“假溢出问题”,在实际应用中操作起来跟方便

目录
相关文章
|
3月前
|
前端开发 JavaScript 应用服务中间件
在Docker部署的前端应用中使用动态环境变量
以上步骤展示了如何在 Docker 配置过程中处理并注入环墨遁形成可执行操作流程,并确保最终用户能够无缝地与之交互而无须关心背后复杂性。
179 13
|
2月前
|
弹性计算 搜索推荐 异构计算
阿里云服务器多少钱一年?亲自整理ECS、轻量和GPU服务器租赁价格表
2025年阿里云服务器优惠汇总:轻量应用服务器2核2G 38元/年起,ECS 2核2G 99元/年,2核4G 199元/年,4核16G 89元/月,8核32G 160元/月,香港轻量25元/月起,新老用户同享,续费同价。
574 4
|
5月前
|
机器学习/深度学习 人工智能 数据挖掘
基于YOLOv8的狗狗品种(多达60种常见犬类)品种鉴别识别项目|完整源码数据集+PyQt5界面+完整训练流程+开箱即用!
随着宠物经济的不断发展,狗狗已经成为众多家庭的重要成员。不同品种犬类在性格、饲养方式、健康管理上有显著差异,快速准确地识别狗狗品种有着重要应用价值。传统方式依赖人工识别,效率低且易出错。 本项目借助YOLOv8强大的目标检测能力,结合高质量数据集训练,实现60种犬类的高精度自动分类识别,并提供可交互图形界面,极大降低使用门槛。
基于YOLOv8的狗狗品种(多达60种常见犬类)品种鉴别识别项目|完整源码数据集+PyQt5界面+完整训练流程+开箱即用!
|
17天前
|
机器学习/深度学习 人工智能 运维
构建AI智能体:二十一、精准检索“翻译官”:qwen-turbo在RAG Query改写中的最佳实践
因为用户的自然提问方式与知识库的客观组织方式天生存在不可调和的差异。如果不进行改写,直接将原始查询用于检索,就如同让一个不懂检索的人自己去漫无目的地查字典,结果往往是找不到、找错了或找到的没法用。Query 改写是保障 RAG 系统可靠性、准确性和可用性的“第一道防线”和“核心基础设施”。它通过一系列技术手段,将用户的意图“翻译”成检索器能高效理解的语言,从而确保后续步骤能在一个高质量的基础上进行。
198 11
|
6月前
|
缓存 前端开发 定位技术
通义灵码2.5智能体模式实战———集成高德MCP 10分钟生成周边服务地图应用
通义灵码2.5智能体模式结合高德MCP服务,实现快速构建周边服务地图应用。通过自然语言需求输入,智能体自动分解任务并生成完整代码,涵盖前端界面、API集成与数据处理,10分钟内即可完成传统开发需数小时的工作,大幅提升开发效率。
351 0
|
8月前
|
数据采集 存储 监控
Python 原生爬虫教程:网络爬虫的基本概念和认知
网络爬虫是一种自动抓取互联网信息的程序,广泛应用于搜索引擎、数据采集、新闻聚合和价格监控等领域。其工作流程包括 URL 调度、HTTP 请求、页面下载、解析、数据存储及新 URL 发现。Python 因其丰富的库(如 requests、BeautifulSoup、Scrapy)和简洁语法成为爬虫开发的首选语言。然而,在使用爬虫时需注意法律与道德问题,例如遵守 robots.txt 规则、控制请求频率以及合法使用数据,以确保爬虫技术健康有序发展。
1090 31
|
7月前
|
机器学习/深度学习 人工智能 搜索推荐
什么叫生成式人工智能?职业技能的范式转移与能力重构
生成式人工智能(Generative AI)是AI领域的重要分支,其核心在于通过学习数据分布生成新内容,如文本、图像、音乐等。与传统判别式模型不同,生成式AI基于深度学习技术(如Transformer架构),展现出“创造力”,但其本质仍是概率计算的结果。它正在重塑内容创作、编程、设计等多个职业领域,推动职业技能的范式转移。 掌握生成式AI需要理解其技术原理、能力边界及伦理挑战。职业技能培训应聚焦提示设计、结果评估和混合创作三大能力,帮助从业者在人机协作中发挥主导作用。未来,生成式AI将向多模态、个性化发展,而人类的独特价值在于为技术注入人文关怀与道德框架。
|
数据采集 监控 数据挖掘
拼多多商品评价API的获取与应用
在数字化商业时代,拼多多商品评价API为开发者和企业提供深入理解消费者反馈、优化产品策略及提升用户体验的重要途径。本文详述了该API的获取方法及其在电商平台运营优化、品牌商市场调研与产品改进、数据分析与市场洞察等领域的广泛应用,强调了遵守使用规范、数据质量处理及性能优化的重要性。
876 0
|
9月前
|
数据采集 缓存 负载均衡
动态HTTP代理与静态HTTP代理的区别及HTTP代理的常见用途与类型
HTTP代理在网络通信中扮演重要角色,优化数据传输并提供隐私保护和访问控制。本文对比动态与静态HTTP代理,探讨其特点、优劣势及适用场景。静态代理地址固定,适合稳定环境;动态代理灵活切换服务器,增强隐私保护。此外,介绍HTTP代理的常见用途(如缓存加速、匿名浏览、绕过限制等)及类型(透明、普匿、匿名、高匿、正向、反向代理),帮助用户根据需求选择合适的代理方式。最后提醒用户遵守法律法规,确保安全使用。
318 1