队列:也是一种受到限制的线性表,一端进行入队,另一端进行出队,我们将入队的一端一般称作队尾,另一端称作队头。如果没有元素叫做空队。
队列的特点:先进先出(FIFO)
顺序表实现的队列为什么叫循环队列?
关于循环队列的三个难点:(重点)
第一个难点:怎么让队列也可以实现入队和出队的时间复杂度都为 O(1)
此时,入队的时间复杂度为多少?(入106) O(1) 不需要挪动元素
出队的时间复杂度为多少?(出101) O(n) 需要挪动元素
所以,解决方案就是,删除元素的时候,记得不要去挪动数据,而是让队头指针往后挪动一步(想象成环形,数据不动,指针动)。
注意:这个循环只是一个概念,真实不存在,也就是说,这个循环是我们臆想出来的
第二个难点:这时候在第一个问题的解决方案上,会发现一个bug:判空判满条件一致,所以有什么好方法,可以将判空判满条件区分开?
第一种:加一个计数器count(保存有效值的个数)
判空:队头指针==队尾指针 && count==0 判满:队头指针==队尾指针 && count !=0
第二种:在队尾处,浪费一个空间不用,这个浪费的空间不再存储有效值,而是当做一个标记位
判空:队头指针==队尾指针 判满:队尾指针向后再走一步,遇到了队头指针此时我们认为已经满了
注意:浪费的标记位是不固定的!如下图所示:
第三个难点:怎么求有效长度?
length = ( rear - front + MAX_SIZE ) % MAX_SIZE (一定要记住这个公式!!!)
(+ MAX_SIZE是为了防止rear - front为负;% MAX_SIZE是为了防止rear - front不为负而加多了 MAX_SIZE)
接下来写代码,先写循环队列的头文件queue.h (注意:队列是不可以扩容的,所以在写之前就要申请稍微大一点的空间)
#pragma once
#define MAX_SIZE 100
//循环队列结构体设计
typedef int ELEM_TYPE;
typedef struct Queue
{
ELEM_TYPE* base; //这个指针用来接受malloc从堆里申请的数组
int front; //队头指针(实际上保存的是数组下标,int)
int rear; //队尾指针(实际上保存的是数组下标,int)
}Queue, *PQueue;
//增删改查
//初始化
void Init_queue(PQueue pq);
//入队
bool Push(PQueue pq, ELEM_TYPE val);
//出队(还要获取出队的值,需要借助一个输出参数rtval)
bool Pop(PQueue pq, ELEM_TYPE* rtval);
//获取队头值
bool Top(PQueue pq, ELEM_TYPE* rtval);
//判空
bool IsEmpty(PQueue pq);
//判满
bool IsFull(PQueue pq);
//获取有效值长度
int Get_length(PQueue pq);
//查找
int Search(PQueue pq, ELEM_TYPE val);
//清空
void Clear(PQueue pq);
//销毁
void Destory(PQueue pq);
//打印
void Show(PQueue pq);
再写queue.cpp文件
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include"queue.h"
//初始化
void Init_queue(PQueue pq)
{
//assert
pq->base = (ELEM_TYPE*)malloc(MAX_SIZE * sizeof(ELEM_TYPE));
assert(pq->base != NULL);
pq->front = 0;
pq->rear = 0;
}
//入队
bool Push(PQueue pq, ELEM_TYPE val)
{
//assert
if (IsFull(pq))
{
return false;
}
pq->base[pq->rear] = val;
pq->rear = (pq->rear + 1) % MAX_SIZE;
return true;
}
//出队
bool Pop(PQueue pq, ELEM_TYPE* rtval)
{
//assert pq trval
*rtval = pq->base[pq->front];
//pq->front++; //error
pq->front = (pq->front + 1) % MAX_SIZE;
return true;
}
//获取队头值
bool Top(PQueue pq, ELEM_TYPE* rtval)
{
//assert pq trval
*rtval = pq->base[pq->front];
return true;
}
//判空
bool IsEmpty(PQueue pq)
{
return pq->front == pq->rear;
}
//判满(队尾指针再向后走一步遇到队头,则判满)
bool IsFull(PQueue pq)
{
//assert
return pq->front == (pq->front + 1) % MAX_SIZE;
}
//获取有效值长度
int Get_length(PQueue pq)
{
//assert
return (pq->rear - pq->front + MAX_SIZE) % MAX_SIZE;
}
//查找
int Search(PQueue pq, ELEM_TYPE val)
{
//assert
for (int i = pq->front; i != pq->rear; i=(i+1) % MAX_SIZE)
{
if (pq->base[i] == val)
{
return i;
}
}
return -1;
}
//清空
void Clear(PQueue pq)
{
pq->front = pq->rear ;
}
//销毁
void Destory(PQueue pq)
{
free(pq->base);
pq->base = NULL;
pq->front = pq->rear = 0;
}
//打印
void Show(PQueue pq)
{
for (int i = pq->front; i != pq->rear; i = (i + 1) % MAX_SIZE)
{
printf("%d ", pq->base[i]);
}
printf("\n");
}
在主函数中进行代码测试
#include<stdio.h>
#include <stdlib.h>
#include<assert.h>
#include<vld.h>
#include"queue.h"
//循环队列模的测试用例
int main()
{
Queue head;
Init_queue(&head);
for (int i = 1; i <= 20; i++)
{
Push(&head, i);
}
Show(&head);
ELEM_TYPE tmp;
Pop(&head, &tmp);
printf("Pop = %d\n", tmp);
Show(&head);
printf("length = %d\n", Get_length(&head));
ELEM_TYPE flag;
Top(&head, &flag);
printf("Top = %d\n", flag);
Show(&head);
printf("length = %d\n", Get_length(&head));
Clear(&head);
Destory(&head);
return 0;
}
测试结果如下: