栈和队列(二) 队列操作详解及栈与队列的相互实现

news2024/11/26 13:39:55

文章目录

  • 四、队列
    • 1、什么是队列
    • 2、队列的基本操作
      • Queue.h
      • Queue.c
        • 初始化队列
        • 队尾入队列
        • 队头出队列
        • 获取队列头部元素
        • 获取队列队尾元素
        • 获取队列中有效元素个数
        • 检测队列是否为空,如果为空返回非零结果,如果非空返回0
        • 销毁队列
  • 五、设计循环队列
  • 六、栈与队列的相互实现
    • 1、用栈实现队列
    • 2、用队列实现栈

栈操作实现:栈和队列(一) 栈操作详解

在这里插入图片描述

四、队列

1、什么是队列

队列就像是高速公路上的一个隧道一样,所有的车辆只允许从入口驶入,从出口驶出,先进先出,不允许逆行。

队列(queue)是一种线性数据结构,队列的元素只能先入先出(First In First Out,简称FIFO)。
入队列:进行插入操作的一端称为队尾
出队列:进行删除操作的一端称为队头

在这里插入图片描述

2、队列的基本操作

利用单链表来实现队列的基本操作

在这里插入图片描述

代码结构设计:

  • Queue.h: 存放队列结构及需要用到的头文件,函数声明等
  • Queue.c: 各种操作函数的具体实现

Queue.h

#pragma once

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>

//方便修改数据类型
typedef int QDataType;

// 链式结构:表示队列 
typedef struct QListNode
{
	struct QListNode* next;
	QDataType data;
}QNode;

// 队列的结构 
typedef struct Queue
{
	QNode* front;//队头
	QNode* rear;//队尾
}Queue;

// 初始化队列 
void QueueInit(Queue* q);
// 队尾入队列 
void QueuePush(Queue* q, QDataType data);
// 队头出队列 
void QueuePop(Queue* q);
// 获取队列头部元素 
QDataType QueueFront(Queue* q);
// 获取队列队尾元素 
QDataType QueueBack(Queue* q);
// 获取队列中有效元素个数 
int QueueSize(Queue* q);
// 检测队列是否为空,如果为空返回非零结果,如果非空返回0 
int QueueEmpty(Queue* q);
// 销毁队列 
void QueueDestroy(Queue* q);

Queue.c

#include "Queue.h"

初始化队列

void QueueInit(Queue* q)
{
	assert(q);

	q->front = NULL;
	q->rear = NULL;
}

队尾入队列

void QueuePush(Queue* q, QDataType data)
{
	assert(q);
	//创建一个节点放数据
	QNode* newNode=(QNode*)malloc(sizeof(QNode));
	if (newNode == NULL)
	{
		perror("malloc fail");
		exit(-1);
	}
	newNode->data = data;
	newNode->next = NULL;

	//判断是否是第一个入队元素
	if (q->rear == NULL)
	{
		q->front = q->rear = newNode;
	}
	else
	{
		q->rear->next= newNode;
		q->rear = newNode;
	}
}

在这里插入图片描述

队头出队列

void QueuePop(Queue* q)
{
	assert(q);
	assert(!QueueEmpty(q));

	//判断是否只有一个元素
	if (q->front->next == NULL)
	{
		free(q->front);
		q->front = q->rear = NULL;
	}
	else
	{
		QNode* del = q->front;
		q->front = q->front->next;
		free(del);
	}
}

在这里插入图片描述

获取队列头部元素

front是队头节点,它的数据便是队头元素

QDataType QueueFront(Queue* q)
{
	assert(q);
	assert(!QueueEmpty(q));

	return q->front->data;
}

获取队列队尾元素

rear是队尾节点,它的数据便是队尾元素

QDataType QueueBack(Queue* q)
{
	assert(q);
	assert(!QueueEmpty(q));

	return q->rear->data;
}

获取队列中有效元素个数

遍历一遍链表就能得到有效元素个数,也可以直接给队列的结构里加上一个size

int QueueSize(Queue* q)
{
	assert(q);

	QNode* cur = q->front;
	int size = 0;
	while (cur)
	{
		cur = cur->next;
		size++;
	}
	return size;
}

检测队列是否为空,如果为空返回非零结果,如果非空返回0

int QueueEmpty(Queue* q)
{
	assert(q);

	//队头节点为空说明队列为空
	return q->front == NULL;
}

销毁队列

void QueueDestroy(Queue* q)
{
	assert(q);

	QNode* cur = q->front;
	while (cur)
	{
		QNode* curNext = cur->next;
		free(cur);
		cur = curNext;
	}

	q->front = q->rear = NULL;
}

五、设计循环队列

循环队列是一种线性数据结构,其操作表现基于 FIFO(先进先出)原则并且队尾被连接在队首之后以形成一个循环。它也被称为“环形缓冲器”。
循环队列的一个好处是我们可以利用这个队列之前用过的空间。在一个普通队列里,一旦一个队列满了,我们就不能插入下一个元素,即使在队列前面仍有空间。但是使用循环队列,我们能使用这些空间去存储新的值。

设计循环队列实现以下操作:

  • MyCircularQueue(k): 构造器,设置队列长度为 k 。
  • Front: 从队首获取元素。如果队列为空,返回 -1 。
  • Rear: 获取队尾元素。如果队列为空,返回 -1 。
  • enQueue(value): 向循环队列插入一个元素。如果成功插入则返回真。
  • deQueue(): 从循环队列中删除一个元素。如果成功删除则返回真。
  • isEmpty(): 检查循环队列是否为空。
  • isFull(): 检查循环队列是否已满。

创建队列时多开辟一个空间来区分空和满
如下是一个队列长度k=4的循环队列:
在这里插入图片描述
用数组实现

//循环队列结构
typedef struct {
    int* a;
    int front;
    int rear;
    int k;
} MyCircularQueue;
//初始化创建
MyCircularQueue* myCircularQueueCreate(int k) {
    MyCircularQueue* obj=(MyCircularQueue*)malloc(sizeof(MyCircularQueue));
    //多开一个空间方便区分空和满
    obj->a=(int*)malloc(sizeof(int)*(k+1));
    obj->front=0;
    obj->rear=0;
    obj->k=k;

    return obj;
}
//判断队列是否为空
bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
    //当队头和队尾相等时队列为空
    return obj->front==obj->rear;
}
//判断队列是否已满
bool myCircularQueueIsFull(MyCircularQueue* obj) {
    return (obj->rear+1)%(obj->k+1)==obj->front;
}

在这里插入图片描述

//入队
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
//判断满了没
    if(myCircularQueueIsFull(obj))
    {
        return false;
    }
    
    obj->a[obj->rear]=value;
    obj->rear++;

    //特殊情况
    (obj->rear)%=(obj->k+1);

    return true;
}

在这里插入图片描述

//出队
bool myCircularQueueDeQueue(MyCircularQueue* obj) {
    //判断队列是不是空的
    if(myCircularQueueIsEmpty(obj))
    {
        return false;
    }

    obj->front++;
    特殊情况
    (obj->front)%=(obj->k+1);
    return true;
}

在这里插入图片描述

//获取队头元素
int myCircularQueueFront(MyCircularQueue* obj) {
    if(myCircularQueueIsEmpty(obj))
    {
        return -1;
    }
    return obj->a[obj->front];
}
//获取队尾元素
//rear是队尾元素下一个元素的下标,所以队尾元素的下标为rear-1
//但当rear等于0的时候队尾元素下标为k,需要特殊处理
int myCircularQueueRear(MyCircularQueue* obj) {
    if(myCircularQueueIsEmpty(obj))
    {
        return -1;
    }
    // if(obj->rear==0)
    // {
    //     return obj->a[obj->k];
    // }else
    // {
    //     return obj->a[obj->rear-1];
    // }
    return obj->a[((obj->rear)+(obj->k))%(obj->k+1)];
}

在这里插入图片描述

//销毁队列
void myCircularQueueFree(MyCircularQueue* obj) {
    free(obj->a);
    free(obj);
}

六、栈与队列的相互实现

1、用栈实现队列

实现 MyQueue 类:

  • void push(int x) 将元素 x 推到队列的末尾
  • int pop() 从队列的开头移除并返回元素
  • int peek() 返回队列开头的元素
  • boolean empty() 如果队列为空,返回 true ;否则,返回 false

思路:

用两个栈实现先入先出队列,当有元素入队时,就是在pushst入栈

在这里插入图片描述
当出队时,将pushst中元素依次出栈放进popst中,然后对popst进行出栈操作

在这里插入图片描述

代码实现:

用的是前面自己实现的栈来实现的

typedef struct {
    ST pushst;
    ST popst;
} MyQueue;


MyQueue* myQueueCreate() {
    MyQueue* obj=(MyQueue*)malloc(sizeof(MyQueue));
    STInit(&obj->pushst);
    STInit(&obj->popst);
    return obj;
}

void myQueuePush(MyQueue* obj, int x) {
    STPush(&obj->pushst,x);
}

int myQueuePeek(MyQueue* obj) {
    if(STEmpty(&obj->popst))
    {
        while(!STEmpty(&obj->pushst))
        {
            STPush(&obj->popst,STTop(&obj->pushst));
            STPop(&obj->pushst);
        }
    }
    return STTop(&obj->popst);
}


int myQueuePop(MyQueue* obj) {
    int ret=myQueuePeek(obj);
    STPop(&obj->popst);
    return ret;
}

bool myQueueEmpty(MyQueue* obj) {
    return STEmpty(&obj->pushst)&&STEmpty(&obj->popst);
}

void myQueueFree(MyQueue* obj) {
    STDestroy(&obj->pushst);
    STDestroy(&obj->popst);
    free(obj);
}

2、用队列实现栈

实现 MyStack 类:

  • void push(int x) 将元素 x 压入栈顶。
  • int pop() 移除并返回栈顶元素。
  • int top() 返回栈顶元素。
  • boolean empty() 如果栈是空的,返回 true ;否则,返回 false 。

思路:

用两个队列q1和q2来实现一个后入先出的栈

入栈:放进不为空的那个队列
在这里插入图片描述

出栈:不为空队列的前n-1个出队列插入空队列,删除剩下的一个即可

在这里插入图片描述
代码实现:

用的是前面自己实现的队列来实现的

typedef struct {
    Queue q1;
    Queue q2;
} MyStack;


MyStack* myStackCreate() {
    MyStack* p=(MyStack*)malloc(sizeof(MyStack));
    QueueInit(&p->q1);
    QueueInit(&p->q2);

    return p;
}

void myStackPush(MyStack* obj, int x) {
    if(!QueueEmpty(&obj->q1))
    {
        QueuePush(&obj->q1,x);
    }
    else
    {
        QueuePush(&obj->q2,x);
    }
}

int myStackPop(MyStack* obj) {
    Queue* empty=&obj->q1;
    Queue* noEmpty=&obj->q2;
    if(!QueueEmpty(&obj->q1))
    {
        empty=&obj->q2;
        noEmpty=&obj->q1;
    }

    while(QueueSize(noEmpty)>1)
    {
        QueuePush(empty,QueueFront(noEmpty));
        QueuePop(noEmpty);
    }

    int top=QueueFront(noEmpty);
    QueuePop(noEmpty);

    return top;
}

int myStackTop(MyStack* obj) {
    if(!QueueEmpty(&obj->q1))
    {
        return QueueBack(&obj->q1);
    }
    else
    {
        return QueueBack(&obj->q2);
    }
}

bool myStackEmpty(MyStack* obj) {
    return QueueEmpty(&obj->q1)&&QueueEmpty(&obj->q2);
}

void myStackFree(MyStack* obj) {
    QueueDestroy(&obj->q1);
    QueueDestroy(&obj->q2);
    free(obj);
}

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

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

相关文章

virt-manager上安装ubuntu22.04虚拟机

文章目录 前言一、镜像下载二、 virt-manager新建机器2.1 选择安装来源类型2.2 选择ISO文件2.3 设置CPU数量和内存容量2.4 设置硬盘容量2.5 设置虚拟机类型&#xff0c;勾选配置按钮2.6 修改硬盘驱动类型2.7 修改网卡驱动类型2.8 设置显示器类型2.9 开始安装 三、操作系统安装3…

Von Maur, Inc EDI 需求分析

Von Maur, Inc 是一家历史悠久的卖场&#xff0c;成立于19世纪&#xff0c;总部位于美国。作为一家知名的零售商&#xff0c;Von Maur 主要经营高端时装、家居用品和美妆产品。其使命是为顾客提供优质的产品和无与伦比的购物体验。多年来&#xff0c;Von Maur 凭借其卓越的服务…

计算机视觉应用方向

计算机视觉可以大致有以下几个方向&#xff08;更详细的可以参考papers with code&#xff09;&#xff1a; 图像分类目标检测图像分割图像生成风格迁移超分辨率 1. 图像分类 图像分类是是视觉识别中的一项基本任务&#xff0c;目的是分辨整个图像并将其分类。 1.1 常用数据…

php使用get和post传递数据出现414 Request-URI Too Large的解决方案

递数据出现414 Request-URI Too Large的解决方案 一、Request-URI Too Large的原因二、GET与POST三、项目分析1.读取源数据2.将读取的到数据&#xff0c;进行传递3.ajax获取传递的数据并传递到后台4.传递数据5.解决方案 一、Request-URI Too Large的原因 “Request-URI Too La…

如果你不只是个点工,那你应该知道 前后端分离与不分离的区别

Web 应用的开发主要有两种模式&#xff1a; 前后端不分离 前后端分离 理解它们的区别有助于我们进行对应产品的测试工作。 前后端不分离 在早期&#xff0c;Web 应用开发主要采用前后端不分离的方式&#xff0c;它是以后端直接渲染模板完成响应为主的一种开发模式。以前后端…

理想汽车:中国电动汽车领域最有投资价值的公司?

来源&#xff1a;猛兽财经 作者&#xff1a;猛兽财经 总结&#xff1a; &#xff08;1&#xff09;尽管面临着价格和中国电动汽车市场需求放缓的压力&#xff0c;但理想汽车&#xff08;LI&#xff09;在6月份还是交付了32,575辆电动汽车&#xff0c;并且超过了竞争对手蔚来&a…

element-plus:el-date-picker日期只选择年月不要日

<el-date-picker v-model"value" type"month" format"YYYY-MM" value-format"YYYY-MM" />使用format属性将时间显示格式修改为YYYY–MM 年月格式 使用value-format将绑定值的格式修改为YYYY–MM年月格式

DNS解析过程实践分析【nslookup演示】

基本原理 首先一句话概括&#xff0c;DNS就是做域名到IP的映射&#xff0c;rdns表示IP到域名的映射。 怎么映射&#xff0c;其中传递的报文&#xff0c;以及如何进行进行扫描&#xff0c;记录类型都大有讲究。涉及到网络空间测绘当中的DNS扫描&#xff0c;dos也有DNS的相关应…

企升编辑器word编写插件

面向用户群体招投标人员&#xff0c;用统一的模板来编写标书&#xff0c;并最终合并标书。项目经理&#xff0c;编写项目开发计划书&#xff0c;项目验收文档等。开发人员&#xff0c;编写项目需求规格说明书、设计说明书、技术总结等文档。其他文档编写工作量较多的岗位人员。…

小红书 KOL 种草执行策略揭秘:打造爆款产品,提升品牌影响力

随着互联网的普及和社交媒体的发展&#xff0c;小红书成为了众多年轻人购物决策的重要参考平台。小红书 KOL 种草作为一种新兴的营销方式&#xff0c;以其强大的传播力和影响力&#xff0c;越来越受到各大品牌的重视。本文伯乐网络传媒将给大家深入探讨小红书 KOL 种草的执行策…

TS协议概念及传输流程

TS协议之PAT&#xff08;节目关联表&#xff09;TS协议之PMT&#xff08;节目映射表&#xff09;TS协议之PES&#xff08;ES数据包&#xff09; 概要 TS协议是一种媒体流封装协议&#xff0c;类似于MP4&#xff0c;FLV等&#xff0c;可以将编码好的视频流(H164,H265等)和音频…

大数据——推荐系统

1 推荐系统的发展 推荐系统是指面对没有需求的用户在进入产品时&#xff0c;要给用户推荐什么东西&#xff0c;现在的APP基本上都会采用推荐系统。 从一开始的1990s开始的门户网站&#xff0c;像Yahoo、搜狐和Hao123等等&#xff0c;都是基于分类目录的网页导航网站&#xff0…

【Elasticsearch】学好Elasticsearch系列-聚合查询

本文已收录至Github&#xff0c;推荐阅读 &#x1f449; Java随想录 先看后赞&#xff0c;养成习惯。 点赞收藏&#xff0c;人生辉煌。 文章目录 概念doc values 和 fielddatamulti-fields&#xff08;多字段&#xff09;类型聚合分类分桶聚合Histogram 聚合 指标聚合Percentil…

用chatGPT从左右眼图片生成点云数据

左右眼图片 需求 需要将左右眼图像利用视差生成三维点云数据 先问问chatGPT相关知识 进一步问有没有现成的软件 chatGPT提到了OpenCV&#xff0c;我们让chatGPT用OpenCV写一个程序来做这个事情 当然&#xff0c;代码里面会有一些错误&#xff0c;chatGPT写的代码并不会做模…

笔记本WIFI连接无网络【实测有效,不用重启电脑】

笔记本Wifi连接无网络实测有效解决方案 问题描述&#xff1a; 笔记本买来一段时间后&#xff0c;WIFI网络连接开机一段时间还正常连接&#xff0c;但是过一段时间显示网络连接不上&#xff0c;重启电脑太麻烦&#xff0c;选择编写重启网络脚本解决。三步解决问题。 解决方案&a…

蓝牙技术在工业物联网 (IIoT)中的应用_串口透传蓝牙模块

物联网 (IoT) 正在通过托管和可扩展的数字解决方案帮助全球各行各业提高效率。 更具体地说&#xff0c;工业物联网 (IIoT) 侧重于连接石油和天然气、水电以及制造业等关键行业的机器和设备。 在工厂中&#xff0c;连接传感器在机器上的应用被用来收集有价值的数据&#xff0c;用…

标题:使用 Python 的 wxPython 模块生成 PPTX 文档

导语&#xff1a;本文介绍了如何使用 wxPython 模块和 python-pptx 模块来编写一个程序&#xff0c;用于生成包含首页、内容页和感谢页的 PPTX 文档。 介绍 PPTX 文档是一种常用的演示文稿格式&#xff0c;用于展示和分享信息。在本文中&#xff0c;我们将使用 Python 的 wxP…

前后端分离式项目架构流程复盘之宿舍管理系统

文章目录 &#x1f412;个人主页&#x1f3c5;JavaEE系列专栏&#x1f4d6;前言&#xff1a;【&#x1f387;前端】先创建Vue-cli项目&#xff08;版本2.6.10&#xff0c;仅包含babel&#xff09;&#xff0c;请选择此项目并创建 【整理简化项目模板】【&#x1f380;创建路由】…

kubesphere 部署 ingress 并使用 80 端口

文章目录 创建集群网关创建应用路由访问域名使用 80 端口 创建集群网关 官方文档&#xff1a;集群网关 点击左上角的平台管理并选择集群管理 点击导航面板中集群设置下的网关设置&#xff0c;选择集群网关选项卡&#xff0c;并点击启用网关 选择 NodePort 模式&#xff0c;配…

如何在风控引擎中快速增加策略

风控策略是由规则和模型组成的、用以实现风险控制目标的集合。模型与规则相辅相成&#xff0c;相互补充&#xff0c;在不同的业务阶段彼此的侧重点不同。新业务上线期&#xff0c;数据量不足&#xff0c;策略以规则为主&#xff1b;当业务运行一段时候后&#xff0c;数据有了一…