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

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

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

目录

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模板网!

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

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

相关文章

  • 数据结构【栈和队列】

    一、栈 1.定义:只允许一端进行插入和删除的线性表,结构与手枪的弹夹差不多,可以作为实现递归函数(调用和返回都是后进先出)调用的一种数据结构; 栈顶:允许插入删除的那端; 栈底:固定的,不允许插入或删除; 空栈:不含元素; 2.特点:后进先出; 3.操作:入

    2024年02月15日
    浏览(21)
  • 数据结构---栈和队列

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

    2024年01月18日
    浏览(23)
  • 数据结构——栈和队列

    目录  一.前言 二.前文回顾 三.栈 3.1 栈的概念及结构 3.2 栈的实现 3.2.1 初始化函数 3.2.2 销毁函数 3.2.3 入栈函数 3.2.4 出栈函数 3.2.5 计算大小函数 3.2.6 空栈函数 3.2.7 获取栈顶函数  3.2.8 小测试 3.3 全部代码 四.栈的练习 4.1 有效的括号 五.队列 5.1队列的概念及结构 5.2 队列的实

    2024年01月25日
    浏览(23)
  • 数据结构 | 栈和队列

    … 📘📖📃本文已收录至:数据结构 | C语言 更多知识尽在此专栏中! 栈(Stack) 又名堆栈,它是一种运算受限的线性表,限定仅在表尾进行插入和删除操作的线性表。 队列(Queue) 也是一种特殊的线性表,特殊之处在于它只允许在表的前端(Front)进行删除操作,而在表的

    2024年01月23日
    浏览(25)
  • 【数据结构】栈和队列(链表模拟队列)

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

    2024年04月27日
    浏览(18)
  • 数据结构3:栈和队列

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

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

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

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

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

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

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

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

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

    2024年02月08日
    浏览(21)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包