数据结构-队列

news2024/11/25 17:31:23

数据结构之队列

    • 队列的概念
      • 顺序队列
      • 循环队列
    • 顺序循环队列的ADT定义
      • 1、简单结构体定义
      • 2、初始化
      • 3、队列的清空
      • 4、计算队列的长度
      • 5、判断队列是否为空
      • 6、插入新的元素
      • 7、元素的删除
      • 8、遍历输出队列内的所有元素
    • 链队列的ADT定义
      • 1、链队列简单结构体定义
      • 2、初始化链队列
      • 3、判断链队列是否为空
      • 4、清空链队列
      • 5、销毁链队列
      • 6、获取链队列的长度
      • 7、获取链队列的头元素
      • 8、在链队列尾插入新元素
      • 9、删除链队列的头元素
      • 10、遍历链队列中的元素
    • 顺序队列和链式队列的比较

队列的概念

队列(queue)和栈类似,队列中的数据也呈线性排列,但是队列中添加和删除数据的操作分别是在两端进行。就和“ 队列” 这个名字一样,把它想象成排成一队的人更容易理解。在队列中,处理总是从第一个位置开始往后进行,而新来的人只能排在最后的位置。
队列是只允许在一端进行插入操作,在另一端进行删除操作,是一种先进先出的线性表, First In First Out,简称FIFO。允许插入的一 端称为队尾,允许删除的一端称为队头。
在这里插入图片描述
线性表有顺序存储和链式存储两种形式,队列作为一种特殊的线性表,也存在着这两种存储方式。

顺序队列

顺序存储形式的队列,是利用一组地址连续的存储单元,每个存储单元依次存放队列中的元素。为了避免当只有一个元素时,队头和队尾重合使得处理变得麻烦,所以引入两个指针(头指针和尾指针):头指针front指针指向队头元素,尾指针rear指针指向队尾元素的下一个位置。初始化时的头尾指针,初始值均为0。
入队时尾指针rear加1,出队时头指针front加1,头尾指针相等时队列即为空。

当尾指针已经指向了队列的最后一个位置的下一位置时,若再有元素入队,就会发生“溢出”。
为了解决溢出问题,可以采用循环队列。

循环队列

使用循坏队列,将新元素再插入到第一个位置上,入队和出队仍按先进先出的原则进行,操作效率高,空间利用率高,同时解决了顺序队列的假溢出问题。
在这里插入图片描述
但是,同时仅凭 front = rear 不能判定循环队列是空还是满
判断队空或者队满,有三种方式:

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

顺序循环队列的ADT定义

1、简单结构体定义

typedef int Status;
typedef int QElemType; // QElemType类型根据实际情况而定,这里假设为int 

// 循环队列的顺序存储结构
typedef struct
{
    QElemType data[MAXSIZE];
    int front;      // 头指针
    int rear;       // 尾指针,若队列不空,指向队列尾元素的下一个位置 
}SqQueue;

2、初始化

Status InitQueue(SqQueue *Q)
{
    Q->front = 0;
    Q->rear = 0;
    return  TRUE;
}

3、队列的清空

Status ClearQueue(SqQueue *Q)
{
    Q->front = Q->rear = 0;
    return TRUE;
}

4、计算队列的长度

int QueueLength(SqQueue Q)
{
    return  (Q.rear - Q.front + MAXSIZE) % MAXSIZE;
}

5、判断队列是否为空

若队列不空,则用e返回Q的队头元素,并返回TRUE,否则返回FALSE

Status GetHead(SqQueue Q, QElemType *e)
{
    if (Q.front == Q.rear) /* 队列空 */
        return FALSE;
    *e = Q.data[Q.front];
    return TRUE;
}

6、插入新的元素

若队列未满,则插入元素e为Q新的队尾元素

Status EnQueue(SqQueue *Q, QElemType e)
{
    if ((Q->rear + 1) % MAXSIZE == Q->front)    // 队列满的判断 
        return FALSE;
    Q->data[Q->rear] = e;           // 将元素e赋值给队尾
    Q->rear = (Q->rear + 1) % MAXSIZE;// rear指针向后移一位置,若到最后则转到数组头部 
    return  TRUE;
}

7、元素的删除

若队列不空,则删除Q中队头元素,用e返回其值

Status DeQueue(SqQueue *Q, QElemType *e)
{
    if (Q->front == Q->rear)  //*队列空的判断
        return FALSE;
    *e = Q->data[Q->front];  // 将队头元素赋值给e
    Q->front = (Q->front + 1) % MAXSIZE; //front指针向后移一位置,到了最后则转到数组头部
    return  TRUE;
}

8、遍历输出队列内的所有元素

队头到队尾依次对队列Q中每个元素输出

Status QueueTraverse(SqQueue Q)
{
    int i;
    i = Q.front;
    while ((i + Q.front) != Q.rear)
    {
        visit(Q.data[i]);
        i = (i + 1) % MAXSIZE;
    }
    printf("\n");
    return TRUE;
}

链队列的ADT定义

队列的链式存储结构,就是一个单向链表。链式队列和单向链表比就多了两个指针,头指针和尾指针。

优点:
相比普通的队列,元素出队时无需移动大量元素,只需移动头指针。
可动态分配空间,不需要预先分配大量存储空间。
适合处理用户排队等待的情况。
缺点:
需要为表中的逻辑关系增加额外的存储空间。

读取时的时间复杂度为O(1)。
插入、删除时的时间复杂度为O(1)。

1、链队列简单结构体定义

typedef int Status; // 函数返回结果类型
typedef int ElemType; // 元素类型

// 队列节点
typedef struct QNode {
    ElemType data; // 元素值
    struct QNode *next; // 指向下一个节点的指针
} QNode, *QueuePtr;

// 链队列结构
typedef struct {
    QueuePtr front, rear; // 队头指针、队尾指针
} LinkQueue;

2、初始化链队列

Status InitQueue(LinkQueue *Q) {
    // 为队头和队尾指针分配内存
    Q->front = Q->rear = (QueuePtr) malloc(sizeof(QNode));

    // 内存分配失败,结束程序
    if (!Q->front || !Q->rear) {
        return FALSE;
    }
    Q->front->next = NULL; // 队头节点指向NULL
    return TRUE;
}

3、判断链队列是否为空

Status QueueEmpty(LinkQueue Q) {
    // 头指针和尾指针位置相等,队列为空
    if (Q.front == Q.rear) {
        return TRUE;
    } else {
        return FALSE;
    }
}

4、清空链队列

Status ClearQueue(LinkQueue *Q) {
    QueuePtr p, q; // p用来遍历队列节点,q用来指向被删除的节点

    Q->rear = Q->front; // 队尾指针指向队头指针
    p = Q->front->next; // p指向队头指针的下一个节点
    Q->front->next = NULL; // 队头指针的下一个节点指向NULL(表示删除之后的所有元素)
    // 当队列中还有元素,释放头节点之后的所有节点
    while (p) {
        q = p; // q节点指向被删除节点
        p = p->next; // p指向队列的下一个节点
        delete q; // 释放q节点
    }
    return TRUE;
}

5、销毁链队列

Status DestroyQueue(LinkQueue *Q) {
    // 当队列中还有元素
    while (Q->front) {
        Q->rear = Q->front->next;// 队尾指针指向队头指针的下一个元素
        delete Q->front; // 释放队头指针所在节点
        Q->front = Q->rear; // 队头指针指向队尾指针(即原来的下一个元素)
    }
    return TRUE;
}

6、获取链队列的长度

int QueueLength(LinkQueue Q) {
    int i = 0; // 用于统计队列长度的计数器
    QueuePtr p; // 用于遍历队列的元素
    p = Q.front; // p指向队头节点

    // 当p没有移动到队尾指针位置
    while (p != Q.rear) {
        i++; // 计数器加1
        p = p->next; // p移动到队列的下一个节点
    }
    return i; // 返回队列长度
}

7、获取链队列的头元素

Status GetHead(LinkQueue Q, ElemType *e) {
    QueuePtr p;

    // 队列为空,获取失败
    if (Q.front == Q.rear) {
        return FALSE;
    }

    p = Q.front->next; // p指向队列的第一个元素
    *e = p->data; // 将队列头元素的值赋值给e元素
    return TRUE;
}

8、在链队列尾插入新元素

Status EnQueue(LinkQueue *Q, ElemType e) {
    // 给新节点分配空间
    QueuePtr s = (QueuePtr) malloc(sizeof(QNode));

    // 分配空间失败,结束程序
    if (!s) {
        return FALSE;
    }

    s->data = e; // 将值赋值给新节点
    s->next = NULL; // 新节点指向NULL
    Q->rear->next = s; // 队尾指针的下一个元素指向新节点
    Q->rear = s; // 队尾指针指向新节点(新节点成为队尾指针的指向的节点)
    return TRUE;
}

9、删除链队列的头元素

Status DeQueue(LinkQueue *Q, ElemType *e) {
    QueuePtr p; // 用于指向被删除节点

    // 队列为空,出队失败
    if (Q->front == Q->rear) {
        return FALSE;
    }

    p = Q->front->next; // p指向队列的第一个元素
    *e = p->data; // 将队列头节点的值赋值给元素e
    Q->front->next = p->next; // 头指针的下一个节点指向下下个节点(跳过头节点)

    // 如果被删除节点是队尾指针指向的节点(删除后队列为空)
    if (Q->rear == p) {
        Q->rear = Q->front; // 队尾指针指向队头指针
    }
    delete p; // 释放队头节点
    return TRUE;
}

10、遍历链队列中的元素

Status QueueTravel(LinkQueue Q) {
    QueuePtr p; // 用于遍历队列中的节点

    p = Q.front->next; // p指向头节点

    printf("[ ");
    // 当队列中还有元素
    while (p) {
        printf("%d ", p->data); // 打印当前节点的值
        p = p->next; // p移动到队列下一个位置
    }
    printf("]\n");
    return TRUE;
}

顺序队列和链式队列的比较

顺序队列是以数组的形式实现的,首指针在出队的时候向后移动,尾指针在入队的时候向后移动,需要考虑队列为空、队列为满的两种情况。

链式队列是以链表的形式实现的,首指针不移动始终指向头节点,尾指针在入队的时候移动指向插入的元素,只考虑队列为空的情况
(只要存储空间够,就能申请内存空间来存放节点,所以不用考虑满,因为链表长度在程序运行过程中可以不断增加)

参考资料:数据结构与算法基础-王卓老师

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

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

相关文章

chatgpt赋能python:Python怎么断行-让代码更易读

Python怎么断行 - 让代码更易读 大多数Python程序员都知道,代码可读性非常重要。好的代码应该易于阅读和理解,而不是让人困惑和痛苦。 然而,我们经常会发现一些Python代码在一行中拥挤着多个表达式、长变量名混杂其中,让人感到相…

数组:为什么数组都从0开始编号?

提到数组,我想你肯定不陌生,甚至还会自信地说,它很简单啊。 是的,在每一种编程语言中,基本都会有数组这种数据类型。不过,它不仅仅是一种编程语言中的数据类型,还是一种最基础的数据结构。尽管…

word中使用通配符批量将参考文献设置为上角标

目录 一、word中的通配符匹配规则 二、匹配单个参考文献 三、匹配多个参考文献 四、操作方式 (1)打开word中的替换功能 (2)输入要查找的内容 (3)选择替换格式 (4)点击替换 一…

Linux调试工具GDB(1)

文章目录 前言一、GDB概念二、GDB具体使用方法三、GDB断点类型总结 前言 本篇文章我们来介绍一下Linux中的调试利器GDB工具,在Linux的调试中GDB可以帮助我们来解决非常多的问题。 一、GDB概念 GDB是一个功能强大的调试工具,可以用于分析程序崩溃&…

temporal简介

文章目录 前言一、temporal是什么?二、使用步骤1.执行以下命令以启动预构建映像以及所有依赖项。2.运行示例 总结 前言 这两天在国外的网站发现了一个新的golang的微服务框架,感觉挺不错,分亨出来,大家一起看看。 一、temporal是…

python:并发编程(四)

前言 本文将和大家一起探讨python的多进程并发编程,使用内置基本库multiprocessing来实现并发,先通过官方来简单使用这个模块。先打好基础,能够有个基本的用法与认知,后续文章,我们再进行详细使用。 本文为python并发…

【数据库原理与应用 - 第四章】关系数据库规范化理论

目录 一、关系模式规范化理论 1、关系模式规范化概念 2、关系模式应满足的基本要求 3、关系规范化的意义 (1)一个好的数据库应遵循的标准 (2)规范化的意义 二、函数依赖 1、函数依赖的概念 (1)平凡…

Vue--》Vue3打造可扩展的项目管理系统后台的完整指南(五)

今天开始使用 vue3 ts 搭建一个项目管理的后台,因为文章会将项目的每一个地方代码的书写都会讲解到,所以本项目会分成好几篇文章进行讲解,我会在最后一篇文章中会将项目代码开源到我的GithHub上,大家可以自行去进行下载运行&…

学生成绩管理系统(逻辑清楚-简单实用)

1、需求分析 1.1、需求分析概述 需求分析是我们在软件开发中的重要环节,是软件开发的第一步也是最基础的环节,这将决定我们所实现的目标以及系统的各个组成部分、各部分的任务职能、以及使用到的数据结构、各个部门之间的组成关系和数据流程&#xff0…

chatgpt赋能python:Python列表操作:如何使用Python将数据放入列表中

Python列表操作:如何使用Python将数据放入列表中 在Python中,列表是一种重要的数据结构,允许我们将多个项目存储在单个变量中。在本文中,我们将介绍如何将数据放入Python列表中。我们将讨论Python中的列表数据类型以及如何向列表…

chatgpt赋能python:Python中如何放大图片

Python中如何放大图片 简介 图片是网站优化中不可或缺的一部分,然而,当图片在网站中被缩小或拉伸时,会导致其模糊或失真。在这种情况下,可以使用Python中的一些库来放大图片,同时保持图像的清晰度和质量。在本篇文章…

JavaScript 进阶 - 第3天

文章目录 JavaScript 进阶 - 第3天1 编程思想1.1 面向过程1.2 面向对象(oop) 2 构造函数3 原型对象3.1 原型3.2 constructor 属性3.3 对象原型3.4 原型继承3.5 原型链(面试高频) JavaScript 进阶 - 第3天 了解构造函数原型对象的语…

C++友元函数friend使用的学习总结

C友元函数friend使用的学习总结 1. 友元函数简介1.1 使用友元函数的目的1.2 友元函数的三种实现方法 2.全局函数做友元3.类做友元4.成员函数做友元注意! 1. 友元函数简介 1.1 使用友元函数的目的 允许一个函数或者类访问另一个类中的私有成员,使得两个类可以共享同…

HTML学习(一)

Web标准 主要包括&#xff1a;结构&#xff0c;外观&#xff0c;行为三个方面 HTML标签&#xff0c;把标签学会&#xff0c;这门语言就基本学会了 双标签的关系&#xff0c;并列关系和包含关系 每个标签起作用的位置 vscode自动生成的代码的解释 <!DOCTYPE>文档类型声…

【RabbitMQ教程】第八章 —— RabbitMQ - 幂等性、优先级、惰性

&#x1f4a7; 【 R a b b i t M Q 教程】第八章—— R a b b i t M Q − 幂等性、优先级、惰性 \color{#FF1493}{【RabbitMQ教程】第八章 —— RabbitMQ - 幂等性、优先级、惰性} 【RabbitMQ教程】第八章——RabbitMQ−幂等性、优先级、惰性&#x1f4a7; &#x1f…

chatgpt赋能python:Python调试技巧:如何使用断点运行程序

Python调试技巧&#xff1a;如何使用断点运行程序 在Python编程中&#xff0c;程序出现错误或需要调试时&#xff0c;我们需要一些工具来帮助我们定位问题和修复代码。其中之一就是使用断点来运行程序。接下来我们将讨论如何在Python中使用断点进行程序调试的相关技巧。 什么…

C++11核心特性---右值引用(代码+讲解)

右值引用C11 1.左值和右值2.左值引用3.右值引用4.右值引用的性能优化空间5.移动语义6.值类别7.将左值转化为右值8.万能引用和引用折叠9.完美转发10.针对局部变量和右值引用的隐士类型转换11.总结 今天看到谢丙堃老师的《现代C语言核心特性解析》一书中关于右值引用的介绍&#…

21.网络编程|Java学习笔记

文章目录 网络的相关概念ipv4地址分类网路通信协议TCP和UDP InetAdress类SocketTCP网络通信编程应用案例服务端客户端 netstat指令TCP网络通讯不为人知的秘密 UDP网络通信编程【了解】网络传输文件代码示例 网络的相关概念 网络通信 网络 ip地址 1. 概念:用于唯一标识网络…

【数据库原理与应用 - 第七章】数据库安全性和完整性控制

目录 一、数据库安全性控制 二、数据库完整性控制 三、课后习题 数据库的保护功能&#xff1a;数据库安全性、完整性、数据库恢复、并发控制 一、数据库安全性控制 数据库的安全性&#xff1a;保护数据库&#xff0c;以防止非授权用户非法存取造成的数据泄密、更改、破坏DBM…

Python开发中的常见问题和解决方法:如何解决常见的性能和bug问题

第一章&#xff1a;引言 在Python开发中&#xff0c;我们经常会遇到一些常见的问题&#xff0c;包括性能瓶颈和程序错误&#xff08;bug&#xff09;。这些问题可能会影响我们的应用程序的运行效率和稳定性。因此&#xff0c;了解这些问题的根源并学习解决方法是非常重要的。本…