欢迎来到博主的专栏——C语言进阶指南
博主ID:代码小豪
文章目录
- 队列
- 顺序结构的队列
- 顺序队列的初始化
- 顺序队列的入队列操作
- 判断队列是否为空
- 顺序队列的出队列操作
- 顺序队列操作的所有代码
- 链式结构的队列
- 链式队列的初始化
- 链式队列的初始化
- 链式队列的入队列操作
- 链式队列的出队列的操作
- 链式队列的所有代码
队列
队列也是一种特殊的线性表,与栈先进后出(First In Last Out)的特性不同,队列的特性是先进先出(Firsr In First Out),即在表尾插入数据,在表头删除数据。
向队列插入数据的操作叫做入队列
向队列删除数据的操作叫做出队列
当数据出队列时,处于队头的元素被删除,队头的后继元素成为新的队头
当数据入队列时,将新数据插入队尾元素的后继元素,并将新数据成队尾
队列的物理存储结构可以分为两种:
一是顺序存储结构的队列,即以数组的形式实现的队列
二是链式存储结构的队列,即以链表的形式实现的队列
通常来说,链式存储结构的队列性能更好,适用性也更佳
顺序结构的队列
顺序结构的队列分为两种,一种是使用静态开辟的空间的队列,称为静态队列
静态队列的结构类型为:
#define MAXSIZE 10
typedef int QueueData;
typedef struct queue
{
QueueData vals[MAXSIZE];
int front;
int rear;
};
静态队列的缺点很明显,那就是队列会出现满的情况,在这种情况下无法进行入队列的操作。
动态队列主要作用就是可以对队列进行扩容,当队列出现满队列的状况时,可以对队列进行操作。
动态队列的结构如下:
#define MAXSIZE 10
typedef int QueueData;
typedef struct queue
{
QueueData* vals;
int front;
int rear;
int capacity;
}queue;
顺序队列的初始化
将队列中的数据根据需要进行初始化。
void SeqQueueInit(queue* q1)
{
q1->vals = NULL;
q1->front = 0;
q1->rear = 0;
q1->capacity = 0;
}
顺序队列的入队列操作
将待入队的数据插入至队尾(rear)当中,并将队尾(rear)指向下一个待插入数据的空间位置
void SeqQueuePush(queue* q1, QueueData e)
{
if (SeqQueueFull(q1))//判断队列是否满
{
//满则进行扩容
int newcapacity = q1->capacity == 0 ? 4 : 2 * q1->capacity;
QueueData* tmp = (QueueData*)realloc(q1->vals,sizeof(QueueData) * newcapacity);
assert(tmp);
q1->vals = tmp;
q1->capacity = newcapacity;
}
q1->vals[q1->rear] = e;
q1->rear++;
}
判断队列是否为空
队列为空的情况很简单,当rear和front相等的时候队列为空
bool SeqQueueEmpty(queue* q1)
{
return q1->front == q1->rear;
}
顺序队列的出队列操作
当数据出队列后,front需要指向新的队头元素,完成这个要求的方法有两种。
(1)当队头元素出队列后,将front的指针指向下一个元素
这种方法会导致队列的部分空间被浪费掉。
(2)
将队列的队头的位置保持不变,将后面的所有元素向前移动一位
这种处理方法虽然没有浪费空间,但是可以发现删除数据的时间复杂度到了O(N),也是存在不小的缺点
void SeqQueuePop(queue* q1)//方法1
{
if (SeqQueueEmpty(q1))
{
perror("Queue is empty\n");
exit(1);
}
q1->front++;
}
顺序队列操作的所有代码
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<assert.h>
#define MAXSIZE 10
typedef int QueueData;
typedef struct queue
{
QueueData* vals;
int front;
int rear;
int capacity;
}queue;
void SeqQueueInit(queue* q1);
void SeqQueueDestory(queue* q1);
void SeqQueuePush(queue* q1,QueueData e);
void SeqQueuePop(queue* q1);
QueueData SeqQueueFront(queue* q1);
bool SeqQueueEmpty(queue* q1);
bool SeqQueueFull(queue* q1);
void SeqQueueInit(queue* q1)
{
q1->vals = NULL;
q1->front = 0;
q1->rear = 0;
q1->capacity = 0;
}
void SeqQueueDestory(queue* q1)
{
free(q1->vals);
}
void SeqQueuePush(queue* q1, QueueData e)
{
if (SeqQueueFull(q1))//判断队列是否满
{
//满则进行扩容
int newcapacity = q1->capacity == 0 ? 4 : 2 * q1->capacity;
QueueData* tmp = (QueueData*)realloc(q1->vals,sizeof(QueueData) * newcapacity);
assert(tmp);
q1->vals = tmp;
q1->capacity = newcapacity;
}
q1->vals[q1->rear] = e;
q1->rear++;
}
void SeqQueuePop(queue* q1)
{
if (SeqQueueEmpty(q1))
{
perror("Queue is empty\n");
exit(1);
}
q1->front++;
}
QueueData SeqQueueFront(queue* q1)
{
if (SeqQueueEmpty(q1))
{
perror("Queue is empty\n");
exit(1);
}
return q1->vals[q1->front];
}
bool SeqQueueEmpty(queue* q1)
{
return q1->front == q1->rear;
}
bool SeqQueueFull(queue* q1)
{
return q1->rear == q1->capacity;
}
链式结构的队列
既然顺序队列有着空间存储要求大,删除的时间复杂度高的缺点,那么链式结构饿的队列能不能解决这些问题呢?
链式队列的初始化
我们创建一个队列节点的结构,再创建一个队列的整体结构,将队列元素储存在节点当中。
typedef int LQueueData;
typedef struct QueueNode
{
LQueueData val;
struct QueueNode* next;
}QueueNode;
typedef struct ListQueue
{
QueueNode* front;//指向队头
QueueNode* rear;//指向队尾
int size;//队列大小
}ListQueue;
链式队列的初始化
根据使用需求对队列中的元素进行初始化,当链式队列为空队列时,队头与队尾指针都应该置为NULL。
void ListQueueInit(ListQueue* lq)
{
lq->front = NULL;
lq->rear = NULL;
lq->size = 0;
}
链式队列的入队列操作
用尾插法,在队尾结点插入一个新的节点。
void ListQueuePush(ListQueue* lq, LQueueData e)
{
//生成新节点
QueueNode* newnode = (QueueNode*)malloc(sizeof(QueueNode));
newnode->val = e;
newnode->next = NULL;
if (ListQueueEmpty(lq))
{
lq->front = newnode;
lq->rear = newnode;
lq->size++;
return;
}
//尾插法
lq->rear->next = newnode;
lq->rear = newnode;
lq->size++;
}
链式队列的出队列的操作
将front指针指向的节点释放,并将front指针指向下一个节点的位置
void ListQueuePop(ListQueue* lq)
{
if (ListQueueEmpty(lq))
{
perror("Queue is empty\n");
exit(1);
}
QueueNode* del = lq->front;
lq->front = lq->front->next;
lq->size--;
free(del);
}
链式队列的所有代码
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<assert.h>
typedef int LQueueData;
typedef struct QueueNode
{
LQueueData val;
struct QueueNode* next;
}QueueNode;
typedef struct ListQueue
{
QueueNode* front;//指向队头
QueueNode* rear;//指向队尾
int size;//队列大小
}ListQueue;
void ListQueueInit(ListQueue* lq);
void ListQueueDestory(ListQueue* lq);
void ListQueuePush(ListQueue* lq,LQueueData e);
void ListQueuePop(ListQueue* lq);
bool ListQueueEmpty(ListQueue* lq);
LQueueData ListQueueFront(ListQueue* lq);
int ListQueueSize(ListQueue* lq);
void ListQueueInit(ListQueue* lq)
{
lq->front = NULL;
lq->rear = NULL;
lq->size = 0;
}
void ListQueueDestory(ListQueue* lq)
{
QueueNode* cur = lq->front;
while (cur)
{
QueueNode* del = cur;
cur = cur->next;
free(del);
}
}
void ListQueuePush(ListQueue* lq, LQueueData e)
{
//生成新节点
QueueNode* newnode = (QueueNode*)malloc(sizeof(QueueNode));
newnode->val = e;
newnode->next = NULL;
if (ListQueueEmpty(lq))
{
lq->front = newnode;
lq->rear = newnode;
lq->size++;
return;
}
//尾插法
lq->rear->next = newnode;
lq->rear = newnode;
lq->size++;
}
void ListQueuePop(ListQueue* lq)
{
if (ListQueueEmpty(lq))
{
perror("Queue is empty\n");
exit(1);
}
QueueNode* del = lq->front;
lq->front = lq->front->next;
lq->size--;
free(del);
}
bool ListQueueEmpty(ListQueue* lq)
{
if (lq->front == NULL)
{
lq->rear = NULL;
return true;
}
return false;
}
LQueueData ListQueueFront(ListQueue* lq)
{
if (ListQueueEmpty(lq))
{
perror("Queue is empty\n");
exit(1);
}
return lq->front->val;
}
int ListQueueSize(ListQueue* lq)
{
return lq->size;
}