目录
一 了解队列
- 概念: 队列(queue)是一种遵循先入先出(FIFO)规则的线性数据结构。结构和排队类似,队尾不断有新的人加入(入队),而队头不断有人离开(出队)。由于队列的特点:只允许在队头进行删除操作,而在队尾进行插入操作。和栈一样,队列也可以看做是一种操作受限制的数组或链表。
- 应用:打印机的打印任务;对网站的访问请求等
二 队列的操作实现
(1)定义队列结构; (2)队列的初始化/构造;
(3)获取栈的长度; (4)判断栈是否为空;
(5)获取队头/尾元素; (6)入队;
(7)出队; (8)销毁队列
1, 基于链表实现的队列
若不是很了解链式存储结构,可参考这篇文章:链表入门:“单链表“的基本操作详解(C语言)
https://blog.csdn.net/Mzyh_c/article/details/133841591?spm=1001.2014.3001.5502
/*定义链队*/
typedef struct LinkQueue
{
int data;
struct LinkQueue* next;
struct LinkQueue* top; //记录队头结点
struct LinkQueue* end; //记录队尾结点
}LinkQueue;
/* 初始化链队 */
LinkQueue* InitLinkQueue()
{
LinkQueue* L = (LinkQueue*)malloc(sizeof(LinkQueue));
if(!L)
return NULL; //开辟失败
L->top = NULL;
L->end = NULL;
return L; //返回开辟成功的链队
}
/* 获取队列长度 */
int GetLength(LinkQueue* L)
{
int count = 1;
//为了不改变队列中的指针,创建临时指针进行遍历
LinkQueue* temp = L->top;
while (1)
{
temp = temp->next;
count++;
//当到循环到队尾的时候停止并返回count
if (temp == L->end)
return count;
}
}
/* 判断链队是否为空 */
int IsEmpty(LinkQueue* L)
{
if (L->top == NULL && L->end == NULL)
return 1;//链队为空
else
return 0;//链队不为空
}
/* 入队 */
void Push(LinkQueue* L, int e)
{
//在队尾插入
//创建一个新结点
LinkQueue* P = (LinkQueue*)malloc(sizeof(LinkQueue));
if (!P)
return; //p创建失败
P->data = e;
if (IsEmpty(L))
{
//当队列为空时,头指针和尾指针都指向新结点P
L->end = P;
L->top = L->end;
return; //结束方法
}
//当队列不为空时,新结点插入在队尾,尾指针指向新结点
L->end->next = P;
L->end = P;
}
/* 出队 */
int Pop(LinkQueue* L)
{
if (IsEmpty(L))
{
return 0;
}
//在队头删除
LinkQueue* temp = (LinkQueue*)malloc(sizeof(LinkQueue));
if (!temp)
return; //temp创建失败
//存储将删除结点的元素
temp = L->top;
int e = temp->data;
L->top = L->top->next; //top指向下一个结点
free(temp); //释放空间
temp = NULL; //防止产生野指针
//返回已删除结点的元素
return e;
}
/* 获取队头元素 */
int GetTop(LinkQueue* L)
{
if (IsEmpty(L))
{
return 0;
}
return L->top->data;
}
/* 获取队尾元素 */
int GetEnd(LinkQueue* L)
{
if (IsEmpty(L))
{
return 0;
}
return L->end->data;
}
/* 销毁链队 */
void Destroy(LinkQueue* L)
{
if (IsEmpty(L))
return;
while (L->top != L->end)
{
LinkQueue* temp = L->top;
L->top = L->top->next;
free(temp); //释放空间
temp = NULL; //防止产生野指针
}
//在 L->top == L->end 时并没有执行循环体内容
free(L->end);
L->end = NULL;
}
2, 基于数组实现的队列
在数组中删除首元素每次都得向后访问n次找到首元素后再进行删除,时间复杂度为O(n),这样的做法效率很低。所以我们将定义一个头指针 top 在每次删除元素后都进行头指针的自增(top++)指向下一个首元素,入队也会对尾指针 end 进行自增,这样时间复杂度就为O(n)了。而数组的长度是有限的,这样会导致当它们到达数组尾部时就无法继续移动了,所以我们可以使用循环(环形)数组解决这个问题。(见下面代码)
![]()
循环队列的示意图 此实现方式的局限性是其长度不可变,读者可以自己尝试将数组改用动态数组,引入扩容机制进行解决。
/* 定义顺序队 */
typedef struct SqQueue
{
int top; //队头
int end; //队尾
ElemType* data; //数据
}SqQueue;
/* 初始化顺序队列 */
SqQueue* InitQuese()
{
SqQueue* q = (SqQueue*)malloc(sizeof(SqQueue));
q->data = (ElemType*)malloc(sizeof(ElemType) * MAXSIZE);
if (q->data == NULL)
return 0;//开辟空间失败
q->end = q->top = 0;
return q; //开辟空间成功返回队列q
}
/* 判断队列是否为空 */
int IsEmpty(SqQueue* q)
{
if (q->end == q->top)
return 1;
else
return 0;
}
/* 获取队列长度 */
int GetLength(SqQueue* q)
{
return (q->end - q->top);
}
/* 入队 */
void Push(SqQueue* q, ElemType e)
{
if ((q->end % MAXSIZE == q->top) && (q->end != 0))
{
printf("Push.err: 队列已满!\n");
return;//队列已满
}
//q->data[(q->end)++] = e;
q->data[(q->end) % MAXSIZE] = e;
(q->end)++;
//把队列变成循环的队列
}
/* 出队 */
ElemType Pop(SqQueue* q)
{
//队列为空则返回
if (IsEmpty(q))
return -1;
//存储要删除的数据并返回
ElemType e = q->data[q->top];
(q->top)++;
return e;
}
/* 获得队尾的元素 */
ElemType GetEnd(SqQueue* q)
{
return q->data[(q->end - 1) % MAXSIZE];
}
/* 删除队列 */
void DelQuese(SqQueue* q)
{
if (IsEmpty(q))
return;
q->end = q->top = 0;
}
/* 销毁队列 */
void DestoryQuese(SqQueue* q)
{
free(q->data);//释放空间
q->data = NULL;//防止产生野指针
q->end = q->top = 0;
}
对于循环队列,我们需要让
top
或end
在越过数组尾部时,直接回到数组头部继续遍历。这种周期性规律可以通过“取余操作”来实现。(即每次指针移动时 % 队列的最大长度,可以防止指针访问越界。)
三 测试效果


在学习了队列和栈后, 你可以尝试完成以下的练习, 可能会有所收获!
停车场管理系统 - 栈和队列的应用(C语言)-CSDN博客
https://blog.csdn.net/Mzyh_c/article/details/135175641