之前的文章:
队列
一. 队列的概念
队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出FIFO(First In First Out) 入队列:进行插入操作的一端称为队尾 出队列:进行删除操作的一端称为队头
二. 普通的队列
2.1 前言
队列可以用数组和链表的结构实现,使用链表的结构实现更优一些,因为使用数组的结构,出队列在数组头上出数据,效率会比较低。我们这次的队列由链表实现,如下图所示,队列的主体部分为链表,有两个指针,head 指针指向队头,tail 指针指向队尾,出队在队头,入队在队尾。本次队列的实现共创建了三个文件,分别是Queue.h ,Queue.c ,和main.c 文件。下面我们先把队列的结构体定义和各个函数接口实现,最后再把三个文件的代码分享出来。
2.2 定义队列
typedef int QDataType;
typedef struct QListNode
{
QDataType data;
struct QListNode* next;
}QListNode;
typedef struct Queue
{
QListNode* head;
QListNode* tail;
}Queue;
2.3 函数接口
2.3.1 队列初始化
void QueueInit(Queue* queue)
{
assert(queue);
queue->head = NULL;
queue->tail = NULL;
}
assert 是一个断言函数,程序运行的时候,当括号里面的结果为假时,就会停止运行并且报错。报错显示的信息包括断言语句括号的内容和断言的位置(在哪个文件,哪一行),还有一个错误框,如下图所示。断言能够快速地帮我们定位程序的错误,在实际开发中可以减少很多不必要的麻烦,所以建议大家在写代码的时候也尽量在需要的时候加上断言。
2.3.2 打印队列
void QueuePrint(Queue* queue)
{
assert(queue);
QListNode* cur = queue->head;
while (cur)
{
printf("%d --> ", cur->data);
cur = cur->next;
}
printf("\n");
}
2.3.3 判断队列为不为空
bool QueueEmpty(Queue* queue)
{
assert(queue);
return queue->head == NULL;
}
2.3.4 入队
void QueuePush(Queue* queue, QDataType x)
{
assert(queue);
QListNode* newnode = (QListNode*)malloc(sizeof(QListNode));
assert(newnode);
newnode->data = x;
newnode->next = NULL;
if (queue->head == NULL)
{
queue->head = newnode;
queue->tail = newnode;
}
else
{
queue->tail->next = newnode;
queue->tail = newnode;
}
}
2.3.5 出队
void QueuePop(Queue* queue)
{
assert(queue);
assert(!QueueEmpty(queue));
QListNode* tmp = queue->head;
if (queue->head->next == NULL)
{
queue->tail = NULL;
}
queue->head = queue->head->next;
free(tmp);
tmp = NULL;
}
2.3.6 获取队头队尾元素
QDataType QueueFront(Queue* queue)
{
assert(queue);
assert(!QueueEmpty(queue));
return queue->head->data;
}
QDataType QueueBack(Queue* queue)
{
assert(queue);
assert(!QueueEmpty(queue));
return queue->tail->data;
}
2.3.7 获取队列大小
int QueueSize(Queue* queue)
{
assert(queue);
int count = 0;
QListNode* cur = queue->head;
while (cur)
{
count++;
cur = cur->next;
}
return count;
}
2.3.8 销毁队列
void QueueDestroy(Queue* queue)
{
assert(queue);
while (!QueueEmpty(queue))
{
QueuePop(queue);
}
}
2.4 Queue.h文件
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<assert.h>
typedef int QDataType;
typedef struct QListNode
{
QDataType data;
struct QListNode* next;
}QListNode;
typedef struct Queue
{
QListNode* head;
QListNode* tail;
}Queue;
void QueueInit(Queue* queue);
void QueuePrint(Queue* queue);
void QueuePush(Queue* queue, QDataType x);
void QueuePop(Queue* queue);
QDataType QueueFront(Queue* queue);
QDataType QueueBack(Queue* queue);
int QueueSize(Queue* queue);
bool QueueEmpty(Queue* queue);
void QueueDestroy(Queue* queue);
2.5 Queue.c文件
#define _CRT_SECURE_NO_WARNINGS
#include"Queue.h"
void QueueInit(Queue* queue)
{
assert(queue);
queue->head = NULL;
queue->tail = NULL;
}
void QueuePrint(Queue* queue)
{
assert(queue);
QListNode* cur = queue->head;
while (cur)
{
printf("%d --> ", cur->data);
cur = cur->next;
}
printf("\n");
}
void QueuePush(Queue* queue, QDataType x)
{
assert(queue);
QListNode* newnode = (QListNode*)malloc(sizeof(QListNode));
assert(newnode);
newnode->data = x;
newnode->next = NULL;
if (queue->head == NULL)
{
queue->head = newnode;
queue->tail = newnode;
}
else
{
queue->tail->next = newnode;
queue->tail = newnode;
}
}
void QueuePop(Queue* queue)
{
assert(queue);
assert(!QueueEmpty(queue));
QListNode* tmp = queue->head;
if (queue->head->next == NULL)
{
queue->tail = NULL;
}
queue->head = queue->head->next;
free(tmp);
tmp = NULL;
}
QDataType QueueFront(Queue* queue)
{
assert(queue);
assert(!QueueEmpty(queue));
return queue->head->data;
}
QDataType QueueBack(Queue* queue)
{
assert(queue);
assert(!QueueEmpty(queue));
return queue->tail->data;
}
int QueueSize(Queue* queue)
{
assert(queue);
int count = 0;
QListNode* cur = queue->head;
while (cur)
{
count++;
cur = cur->next;
}
return count;
}
bool QueueEmpty(Queue* queue)
{
assert(queue);
return queue->head == NULL;
}
void QueueDestroy(Queue* queue)
{
assert(queue);
while (!QueueEmpty(queue))
{
QueuePop(queue);
}
}
2.6 main.c文件
#define _CRT_SECURE_NO_WARNINGS
#include"Queue.h"
int main()
{
Queue queue;
QueueInit(&queue);
QueuePush(&queue, 1);
QueuePush(&queue, 2);
QueuePush(&queue, 3);
QueuePush(&queue, 4);
printf("%d\n", QueueSize(&queue));
QueueDestroy(&queue);
return 0;
}
三. 循环队列
3.1 前言
循环队列,一般情况下是定长的,有两个指针分别指向队头和队尾的下一个下标。如下图,左边是它的一个逻辑结构,实际上实现是用循环链表或者数组实现,我们这次是采用数组去实现。如下图的右边就是左边对应的状态,**初始状态下队头指针front 和队尾指针rear 都指向第一个位置。为了方便后面判满、判空,我们在申请数组空间的时候往往会多申请一个空间。**所以说下面的队列大小为7,实际申请的数组大小是8个元素空间。有数据的状态下,队头指针front 指向队头元素,队尾指针rear 指向队尾元素的下一个位置。本次队列的实现共创建了三个文件,分别是CircleQueue.h ,CircleQueue.c ,和main.c 文件。下面我们先把队列的结构体定义和各个函数接口实现,最后再把三个文件的代码分享出来。
3.2 定义循环队列
typedef int CQDataType;
typedef struct CircleQueue
{
CQDataType* arr;
int size;
int front;
int rear;
}CircleQueue;
3.3 函数接口
3.3.1 初始化
void CircleQueueInit(CircleQueue* cq, int size)
{
assert(cq);
cq->arr = (CQDataType*)malloc(sizeof(CQDataType) * (size + 1));
cq->size = size;
cq->front = 0;
cq->rear = 0;
}
3.3.2 打印
- 循环队列的空间是循环利用的,如下图所示,实际在队列的元素是从队头队尾的三个元素,也就是黑色字体的几个。下图的右边,黑色字体表示在队列的元素,红色字体是不在队列的,当新的数据入队时直接覆盖红色的元素就好了。下图的右边可以看成是实际情况下数组的状态,左边的是想象的状态,也就是说把红色字体的数据忽略。下面代码
cur 迭代的时候要模上数组大小size + 1 ,是为了预防想下图这种情况,rear 在front 的左边。后面代码要模上size + 1 ,也是这个原因。
void CircleQueuePrint(CircleQueue* cq)
{
assert(cq);
int cur = cq->front;
while (cur != cq->rear)
{
printf("%d ", cq->arr[cur]);
cur = (cur + 1) % (cq->size + 1);
}
printf("\n");
}
3.3.3 判空
bool CircleQueueEmpty(CircleQueue* cq)
{
assert(cq);
return cq->front == cq->rear;
}
3.3.4 判满
bool CircleQueueFull(CircleQueue* cq)
{
assert(cq);
return (cq->rear + 1) % (cq->size + 1) == cq->front;
}
3.3.4 入队
void CircleQueuePush(CircleQueue* cq, CQDataType x)
{
assert(cq);
assert(!CircleQueueFull(cq));
cq->arr[cq->rear] = x;
cq->rear = (cq->rear + 1) % (cq->size + 1);
}
3.3.5 出队
CQDataType CircleQueuePop(CircleQueue* cq)
{
assert(cq);
assert(!CircleQueueEmpty(cq));
CQDataType val = cq->arr[cq->front];
cq->front = (cq->front + 1) % (cq->size + 1);
return val;
}
3.3.6 取队头元素
CQDataType CircleQueueFront(CircleQueue* cq)
{
assert(cq);
assert(!CircleQueueEmpty(cq));
return cq->arr[cq->front];
}
3.3.7 取队尾元素
- 下面的图片,
index 表示队尾元素下标,左右两边是不同的两种情况,他们对index 的取值都能满足他们各自的情况,把他们综合一下得出的index 取值,能满足所有情况。
CQDataType CircleQueueRear(CircleQueue* cq)
{
assert(cq);
assert(!CircleQueueEmpty(cq));
int index = (cq->rear -1 + (cq->size + 1)) % (cq->size + 1);
return cq->arr[index];
}
3.3.8 销毁
void CircleQueueDestroy(CircleQueue* cq)
{
assert(cq);
free(cq->arr);
cq->arr = NULL;
cq->front = 0;
cq->rear = 0;
cq->size = 0;
}
3.4 CircleQueue.h文件
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
typedef int CQDataType;
typedef struct CircleQueue
{
CQDataType* arr;
int size;
int front;
int rear;
}CircleQueue;
void CircleQueueInit(CircleQueue* cq, int size);
void CircleQueuePrint(CircleQueue* cq);
void CircleQueuePush(CircleQueue* cq, CQDataType x);
CQDataType CircleQueuePop(CircleQueue* cq);
CQDataType CircleQueueFront(CircleQueue* cq);
CQDataType CircleQueueRear(CircleQueue* cq);
bool CircleQueueEmpty(CircleQueue* cq);
bool CircleQueueFull(CircleQueue* cq);
void CircleQueueDestroy(CircleQueue* cq);
3.5 CircleQueue.c文件
#define _CRT_SECURE_NO_WARNINGS
#include"CircleQueue.h"
void CircleQueueInit(CircleQueue* cq, int size)
{
assert(cq);
cq->arr = (CQDataType*)malloc(sizeof(CQDataType) * (size + 1));
cq->size = size;
cq->front = 0;
cq->rear = 0;
}
void CircleQueuePrint(CircleQueue* cq)
{
assert(cq);
int cur = cq->front;
while (cur != cq->rear)
{
printf("%d ", cq->arr[cur]);
cur = (cur + 1) % (cq->size + 1);
}
printf("\n");
}
void CircleQueuePush(CircleQueue* cq, CQDataType x)
{
assert(cq);
assert(!CircleQueueFull(cq));
cq->arr[cq->rear] = x;
cq->rear = (cq->rear + 1) % (cq->size + 1);
}
CQDataType CircleQueuePop(CircleQueue* cq)
{
assert(cq);
assert(!CircleQueueEmpty(cq));
CQDataType val = cq->arr[cq->front];
cq->front = (cq->front + 1) % (cq->size + 1);
return val;
}
CQDataType CircleQueueFront(CircleQueue* cq)
{
assert(cq);
assert(!CircleQueueEmpty(cq));
return cq->arr[cq->front];
}
CQDataType CircleQueueRear(CircleQueue* cq)
{
assert(cq);
assert(!CircleQueueEmpty(cq));
int index = (cq->rear -1 + (cq->size + 1)) % (cq->size + 1);
return cq->arr[index];
}
bool CircleQueueEmpty(CircleQueue* cq)
{
assert(cq);
return cq->front == cq->rear;
}
bool CircleQueueFull(CircleQueue* cq)
{
assert(cq);
return (cq->rear + 1) % (cq->size + 1) == cq->front;
}
void CircleQueueDestroy(CircleQueue* cq)
{
assert(cq);
free(cq->arr);
cq->arr = NULL;
cq->front = 0;
cq->rear = 0;
cq->size = 0;
}
3.6 main.c文件
#define _CRT_SECURE_NO_WARNINGS
#include"CircleQueue.h"
int main()
{
CircleQueue cq;
CircleQueueInit(&cq, 4);
CircleQueuePush(&cq, 1);
CircleQueuePush(&cq, 2);
CircleQueuePush(&cq, 3);
CircleQueuePush(&cq, 4);
CircleQueuePop(&cq);
CircleQueuePush(&cq, 5);
CircleQueuePop(&cq);
CircleQueuePush(&cq, 6);
CQDataType front = CircleQueueFront(&cq);
CQDataType rear = CircleQueueRear(&cq);
CQDataType pop = CircleQueuePop(&cq);
printf("%d\n", front);
printf("%d\n", rear);
printf("%d\n", pop);
CircleQueuePrint(&cq);
CircleQueueDestroy(&cq);
return 0;
}
四. 结语
本文是博主学完这章内容后的个人总结,要是文章里有什么错误还望各位大神指正。或者对我的文章排版和其他方面有什么建议,也可以在评论区告诉我。这篇文章被归纳于《数据结构初级阶段-C语言实现》,这个专栏包括的知识点有顺序表、链表、栈、队列、树、排序算法。按照顺序更新,现在已经更新到队列,树和排序算法会在后面更新。如果我的文章对你的学习有帮助,或者觉得写得不错的话记得分享给你的朋友,非常感谢。
|