【数据结构】栈和队列(队列篇)

这篇具有很好参考价值的文章主要介绍了【数据结构】栈和队列(队列篇)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

上期我们已经学习了数据结构中的栈,这期我们开始学习队列。

目录

1.队列的概念及结构

2.队列的实现

队列结构体定义

常用接口函数

初始化队列

队尾入队列

队头出队列

获取队列头部元素、

获取队列队尾元素

获取队列中有效元素个数

检测队列是否为空

销毁队列

3.循环队列


1.队列的概念及结构

队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出 FIFO(First In First Out) 入队列:进行插入操作的一端称为队尾 出队列:进行删除操作的一端称为队头

【数据结构】栈和队列(队列篇),数据结构,数据结构

 从网上找来的队列结构示意图:【数据结构】栈和队列(队列篇),数据结构,数据结构

【数据结构】栈和队列(队列篇),数据结构,数据结构

2.队列的实现

队列也可以数组和链表的结构实现,使用链表的结构实现更优一些,因为如果使用数组的结构,出队列在数组头上出数据,效率会比较低。

队列结构体定义

我们使用的是不带头节点的单向链表来实现队列,而队列要在队尾插入数据,因此每次都要遍历链表找队尾,这样会使得程序的运行效率变低。这里采取了一种解决办法:定义一个指向队尾的指针tail 每次插入新的数据就只要将tail中的next指针指向新节点即可,同时插入新的数据后再更新tail的位置。

typedef int QDataType;
typedef struct QueueNode
{
	struct QueueNode* next;
	QDataType data;
}QNode;

这里单独定义了一个结构体来存储头指针head和尾指针tail

typedef struct Queue
{
	QNode* head;
	QNode* tail;
}Queue;

常用接口函数

//初始化
void QueueInit(Queue* pq);
//插入数据
void QueuePush(Queue* pq, QDataType x);
//销毁
void QueueDestroy(Queue* pq);
//弹出
void QueuePop(Queue* pq);
//取队头数据
QDataType QueueFront(Queue* pq);
//取队尾数据
QDataType QueueBack(Queue* pq);
//判断是否为空
bool QueueEmpty(Queue* pq);
//计算大小
int QueueSize(Queue* pq);

初始化队列

由于这里使用的是不带头节点的单向链表,所以初始化队列只需要将头指针和尾指针都指向空即可。如果是要定义一个带头节点的链表,则需要在这里创建一个头节点

//初始化
void QueueInit(Queue* pq)
{
	assert(pq);
	pq->head = pq->tail = NULL;
}

队尾入队列

由于我们已经将链表的尾部给记录了下来,所以不需要遍历链表,只要尾部节点中的next指针指向新节点

【数据结构】栈和队列(队列篇),数据结构,数据结构

 同时需要判断链表是否为空,如果链表是第一次插入数据,需要将头指针和尾指针都指向新节点。

【数据结构】栈和队列(队列篇),数据结构,数据结构

//队尾插入数据
void QueuePush(Queue* pq, QDataType x)
{
	assert(pq);
	//创建新节点
	QNode* newNode = (QNode*)malloc(sizeof(QNode));
	if (newNode == NULL)
	{
		printf("malloc fail\n");
		exit(-1);
	}
	newNode->data = x;
	newNode->next = NULL;
	
	//将新节点链接起来
	if (pq->tail == NULL)
	{
		pq->head = pq->tail = newNode;
	}
	else
	{
		pq->tail->next = newNode;
		pq->tail = newNode;
	}
}

队头出队列

在弹出数据之前,要判断链表是否为空,这里可以用assert断言:

【数据结构】栈和队列(队列篇),数据结构,数据结构

接下来是具体的弹出操作:

  • 判断队列是否只有一个节点。如果是,表示队列中只剩下一个节点,直接释放这个节点,同时将头指针和尾指针都置为空。
  • 如果队列中有多个节点,将头指针pq->head指向下一个节点,然后释放原来的头节点。这样就完成了一个节点的弹出操作。
//弹出
void QueuePop(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));

	if (pq->head->next == NULL)
	{
		free(pq->head);
		pq->head = pq->tail = NULL;
	}
	else
	{
		QNode* next = pq->head->next;
		free(pq->head);
		pq->head = next;
	}
}

获取队列头部元素、

返回队列头节点的数据 pq->head->data 即可。

//取队头数据
QDataType QueueFront(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));

	return pq->head->data;
}

获取队列队尾元素

  • 返回队列尾节点的数据 pq->tail->data 即可。
//取队尾数据
QDataType QueueBack(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));

	return pq->tail->data;
}

获取队列中有效元素个数

计算大小有多种方法,常用的两种是:

一种是在定义Queue结构体时增加一个用于计数的变量,每次插入数据就自加一下,弹出数据就自减一下。

typedef struct Queue
{
	int size;
	QNode* head;
	QNode* tail;
}Queue;

另外一种是遍历链表,这种方法效率比较低,当然,如果你不在乎这点效率问题,你可以使用这种方法,我们这里也是使用这种方式:

//计算大小
int QueueSize(Queue* pq)
{
	assert(pq);
	QNode* cur = pq->head;
	int size = 0;

	while (cur)
	{
		++size;
		cur = cur->next;
	}

	return size;
}

检测队列是否为空

如果头节点head为空,则队列为空。

//判断是否为空
bool QueueEmpty(Queue* pq)
{
	assert(pq);
	return pq->head == NULL;
} 

销毁队列

销毁队列和销毁普通链表一样:

  • 使用一个指针cur指向队列的头节点pq->head
  • 在一个循环中,遍历队列的所有节点,直到cur为空。
  • 循环结束后,将队列的头指针和尾指针都置为空,表示队列已经被销毁。
//销毁
void QueueDestroy(Queue* pq)
{
	assert(pq);
	QNode* cur = pq->head;
	while (cur)
	{
		QNode* next = cur->next;
		free(cur);
		cur = next;
	}

	pq->head = pq->tail = NULL;
}

3.循环队列

循环队列的概念

实际中我们有时还会使用一种队列叫循环队列。如操作系统课程讲解生产者消费者模型 时可以就会使用循环队列。环形队列可以使用数组实现,也可以使用循环链表实现。

循环队列是一种在固定大小的数组或链表上实现的队列,它可以通过循环利用数组或链表中的空间来实现入队和出队操作。当队列满时,新的元素会从开头重新进入队列,实现循环利用。                                                                                         【数据结构】栈和队列(队列篇),数据结构,数据结构       

通常循环队列有两个指针,一个是头指针head,另一个是尾指针tail, 当head和tail指向同一个节点时,表示队列为空。

当从队尾插入一个数据时,tail就会向后移动。当弹出队头数据时,head向后移动。

空的循环队列:

【数据结构】栈和队列(队列篇),数据结构,数据结构

插入3个数据:

【数据结构】栈和队列(队列篇),数据结构,数据结构

 弹出一个队首的数据:

【数据结构】栈和队列(队列篇),数据结构,数据结构

按照上面这种情况,当队列已经满了的时候,head和tail也会指向同一个节点。这样我们就无法区分队列是空还是满了。因此想出了这两种方法:

方案一:设置一个变量size计算队列元素个数,如果size与队列容量相等时,说明队列已满。

方案二:多开一个空间,不存储数据。  当 tail->next==head 时,表示已经满了。

【数据结构】栈和队列(队列篇),数据结构,数据结构

一般方案二的实用性较强。

【数据结构】栈和队列(队列篇),数据结构,数据结构

 用方案二这种方法我们会发现,如果使用链表的形式实现队列,由于tail每次都是指向尾节点的下一个节点,当我们要取尾节点的数据时,不是很方便。

因此,我们可以使用数组来实现队列。

用数组实现循环队列

原题链接:力扣

【数据结构】栈和队列(队列篇),数据结构,数据结构

 文章来源地址https://www.toymoban.com/news/detail-536909.html

typedef struct 
{
    int* a;
    int k;
    int head;
    int tail;
} MyCircularQueue;


MyCircularQueue* myCircularQueueCreate(int k) 
{
    MyCircularQueue* obj = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
    //带有一个不存储数据的头
    obj->a = (int*)malloc(sizeof(int)*(k+1));
    obj->k = k;
    obj->head = obj->tail = 0;

    return obj;
}

bool myCircularQueueIsEmpty(MyCircularQueue* obj) 
{
    return obj->head == obj->tail;
}

bool myCircularQueueIsFull(MyCircularQueue* obj) 
{
    int next = obj->tail + 1;
    if(obj->tail == obj->k)
    {
        next = 0;
    }

    return next == obj->head;
}

bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) 
{
    if(myCircularQueueIsFull(obj))
    {
        return false;
    }

    obj->a[obj->tail] = value;
    obj->tail++;

    //tail到达数组临界,调整tail位置到开头,以达到循环队列效果
    if(obj->tail == obj->k + 1)
    {
        obj->tail = 0;
    }

    return true;
}

bool myCircularQueueDeQueue(MyCircularQueue* obj) 
{
    if(myCircularQueueIsEmpty(obj))
    {
        return false;
    }

    obj->head++;

    if(obj->head == obj->k + 1)
    {
        obj->head = 0;
    }

    return true;
}

int myCircularQueueFront(MyCircularQueue* obj) 
{
    if(myCircularQueueIsEmpty(obj))
    {
        return -1;
    }

    return obj->a[obj->head];
}

int myCircularQueueRear(MyCircularQueue* obj) 
{
    if(myCircularQueueIsEmpty(obj))
    {
        return -1;
    }

    int prev = obj->tail - 1;
    if(obj->tail == 0)
    {
        prev = obj->k;
    }

    return obj->a[prev];
}

void myCircularQueueFree(MyCircularQueue* obj) 
{
    free(obj->a);
    free(obj);
}

到了这里,关于【数据结构】栈和队列(队列篇)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包赞助服务器费用

相关文章

  • 数据结构---栈和队列

    栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作,进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。 压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。 出栈:栈的删除操作

    2024年01月18日
    浏览(10)
  • [数据结构】栈和队列

    [数据结构】栈和队列

    目录 1.栈 1.1概念 1.2 栈的使用 1.3.栈的模拟实现 2.队列 2.1概念 2.2队列的使用 2.3队列的模拟实现 2.4 循环队列 2.5双端队列   栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素

    2024年02月07日
    浏览(14)
  • 数据结构:栈和队列

    数据结构:栈和队列

    朋友们、伙计们,我们又见面了,本期来给大家解读一下栈和队列方面的相关知识点,如果看完之后对你有一定的启发,那么请留下你的三连,祝大家心想事成! C 语 言 专 栏:C语言:从入门到精通 数据结构专栏:数据结构 个 人 主 页 :  stackY、 目录 前言:  1.栈 1.1栈的

    2024年02月06日
    浏览(12)
  • 栈和队列【数据结构】

    栈和队列【数据结构】

    2024年02月16日
    浏览(17)
  • 【数据结构】栈和队列(链表模拟队列)

    【数据结构】栈和队列(链表模拟队列)

      学习本章节必须具备 单链表的前置知识, 建议提前学习:点击链接学习:单链表各种功能函数 细节 详解 本章节是学习用 单链表模拟队列 1. 单链表实现队列 思路如下 队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先 进先出

    2024年04月27日
    浏览(17)
  • 【数据结构】2.栈和队列

    【数据结构】2.栈和队列

    【数据结构】1.线性表的数组描述和链式描述 - imXuan - 博客园 (cnblogs.com) 【数据结构】1.线性表的数组描述和链式描述 - imXuan - 博客园 (cnblogs.com) 队列由一个数组来描述,队首指针是队首的前一个元素,队尾指针是队尾所在元素,当队首和队尾指针重叠时表示队列为空;当 (队

    2024年02月08日
    浏览(15)
  • 【数据结构】栈和队列(栈篇)

    【数据结构】栈和队列(栈篇)

    目录 1.栈的概念及结构 2.栈的实现 2.1栈的结构体定义 2.2栈的常用接口函数 🐾栈的初始化 🐾插入数据 🐾删除数据 🐾取栈顶元素 🐾判断栈是否为空 🐾计算栈的大小 🐾栈的销毁 2.3完整的代码  3.与栈有关的面试题 栈: 一种特殊的线性表,其只允许在固定的一端进行插入

    2024年02月12日
    浏览(14)
  • 数据结构3:栈和队列

    数据结构3:栈和队列

    目录 1.栈 1.1栈的概念及结构 ​1.2栈的实现 2.队列 2.1队列的概念及结构  2.2队列的实现 2.3循环队列 ​3.栈和队列的面试题 4.概念选择题 栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。 进行数据插入和删除的一端称为栈顶,另一端称为栈底。 栈中数

    2023年04月27日
    浏览(11)
  • 考研数据结构--栈和队列

    考研数据结构--栈和队列

    内容 栈 :栈的抽象数据类型定义、栈的存储表示及基本操作实现、栈的应用 栈的定义(特点) 栈是一种后进先出(LIFO)的线性表,只能在一端进行插入和删除操作,这一端称为栈顶,另一端称为栈底。 打个比方: 有一个胡同很窄只能通过一辆车,而且是死胡同,只能从胡

    2023年04月19日
    浏览(10)
  • 数据结构栈和队列

    数据结构栈和队列

    3.1栈和队列的定义和特点 栈和队列是两种常用的、重要的数据结构 栈和队列是限定插入和删除只能在表的 “ 端点 ”进行的 线性表 栈和队列是线性表的子集(是插入和删除位置受限的线性表) 栈的应用: ​ 由于栈的操作具有 后进先出 的固有特性,使得栈成为程序设计中

    2024年02月16日
    浏览(10)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包