队列的概念及结构
队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出FIFO(First In First Out)的性质。
入队列:队尾进行插入操作
出队列:队头进行删除操作
可以想象一下排队做核酸检测的场景,排在队头的人,肯定是先来的,做完核酸先走(先进先出)。
队列的实现
队列可以用数组或链表的结构实现,使用链表的结构实现更优一些,因为如果使用数组的结构,出队列在数组头上出数据,需要挪动数据,效率会比较低。
以下介绍的是链表结构实现的队列。
分为Queue.h和Queue.c两个文件。
Queue.h头文件声明结构和函数,在Queue.c文件中实现函数。
Queue.h
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <assert.h>
//声明节点
typedef int QDataType;
typedef struct QueueNode
{
QDataType val;
struct QueueNode* next;
}QNode;
//声明队列结构
typedef struct Queue
{
QNode* phead;//队头——删除数据
QNode* ptail;//队尾——插入数据
int size;//记录数据个数
}Queue;
//初始化
void QueueInit(Queue* pq);
//销毁
void QueueDestroy(Queue* pq);
//入队列
void QueuePush(Queue* pq, QDataType x);
//出队列
void QueuePop(Queue* pq);
//获取数据个数
int QueueSize(Queue* pq);
//判空
bool QueueEmpty(Queue* pq);
//获取队头数据
QDataType QueueFront(Queue* pq);
//获取队尾数据
QDataType QueueBack(Queue* pq);
Queue.c
#define _CRT_SECURE_NO_WARNINGS
#include "Queue.h"
//初始化
void QueueInit(Queue* pq)
{
assert(pq);
pq->phead = pq->ptail = NULL;
pq->size = 0;
}
//入队列
void QueuePush(Queue* pq, QDataType x)
{
assert(pq);
//申请节点
QNode* newnode = (QNode*)malloc(sizeof(QNode));
if (newnode == NULL)
{
perror("malloc fail");
return;
}
newnode->next = NULL;
newnode->val = x;
//队尾插入数据
if (pq->phead == NULL)//空链表
{
pq->phead = pq->ptail = newnode;
}
else//非空链表
{
pq->ptail->next = newnode;
pq->ptail = pq->ptail->next;
}
pq->size++;
}
//出队列
void QueuePop(Queue* pq)
{
assert(pq);
assert(pq->phead);
//队头删除数据
if (pq->phead->next == NULL)//一个节点
{
free(pq->phead);
pq->phead= pq->ptail = NULL;
}
else//多个节点
{
QNode* next = pq->phead->next;
free(pq->phead);
pq->phead = next;
}
pq->size--;
}
//获取数据个数
int QueueSize(Queue* pq)
{
assert(pq);
return pq->size;
}
//判空
bool QueueEmpty(Queue* pq)
{
assert(pq);
return pq->size == 0;
}
//获取队头数据
QDataType QueueFront(Queue* pq)
{
assert(pq);
assert(pq->phead);
return pq->phead->val;
}
//获取队尾数据
QDataType QueueBack(Queue* pq)
{
assert(pq);
assert(pq->phead);
return pq->ptail->val;
}
//销毁
void QueueDestroy(Queue* pq)
{
assert(pq);
QNode* cur = pq->phead;
while (cur)
{
QNode* next = cur->next;
free(cur);
cur = next;
}
pq->phead = pq->ptail = NULL;
pq->size = 0;
}
循环队列问题
做题链接
问题描述
设计你的循环队列实现。 循环队列是一种线性数据结构,其操作表现基于 FIFO(先进先出)原则并且队尾被连接在队首之后以形成一个循环。它也被称为“环形缓冲器”。
循环队列的一个好处是我们可以利用这个队列之前用过的空间。在一个普通队列里,一旦一个队列满了,我们就不能插入下一个元素,即使在队列前面仍有空间。但是使用循环队列,我们能使用这些空间去存储新的值。
你的实现应该支持如下操作:
MyCircularQueue(k)
: 构造器,设置队列长度为 k 。Front
: 从队首获取元素。如果队列为空,返回 -1 。Rear
: 获取队尾元素。如果队列为空,返回 -1 。enQueue(value)
: 向循环队列插入一个元素。如果成功插入则返回真。deQueue()
: 从循环队列中删除一个元素。如果成功删除则返回真。isEmpty()
: 检查循环队列是否为空。isFull()
: 检查循环队列是否已满。
解题思路
此题最为关键的问题是如何判断队列是队空还是队满。
我们可以选择开辟k+1个空间,对队空和队满进行区分:
队空时,rear=front;队满时,rear+1=front。
解题代码
以下使用数组实现循环队列。
typedef struct {
int* a;
int head;
int tail;
int k;
} MyCircularQueue;
MyCircularQueue* myCircularQueueCreate(int k) {
MyCircularQueue* pq = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
pq->a = (int*)malloc(sizeof(int)*(k+1));
pq->head = 0;
pq->tail = 0;//tail指向队尾元素的下一个位置
pq->k = k;
return pq;
}
bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
return obj->head == obj->tail;
}
bool myCircularQueueIsFull(MyCircularQueue* obj) {
return (obj->tail+1)%(obj->k+1) == obj->head;//%上空间大小k+1,解决回绕问题
}
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
if(myCircularQueueIsFull(obj))
return false;
obj->a[obj->tail] = value;
obj->tail++;
obj->tail %= obj->k+1;//%上空间大小k+1,解决回绕问题
return true;
}
bool myCircularQueueDeQueue(MyCircularQueue* obj) {
if(myCircularQueueIsEmpty(obj))
return false;
obj->head++;
obj->head %= obj->k+1;//%上空间大小k+1,解决回绕问题
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;
return obj->a[(obj->tail-1 + obj->k+1)%(obj->k+1)];
//当tail=0时,tail-1=-1,队尾元素下标造成越界访问,因此需要加上一个空间大小k+1,解决下标越界问题;%空间大小k+1也是为了解决越界问题
}
void myCircularQueueFree(MyCircularQueue* obj) {
free(obj->a);
obj->head = obj->tail = 0;
free(obj);
}
以上就是本篇文章关于队列和循环队列的所有内容,感谢各位读者的支持!