队列
1.队列定义
1.1 结构定义
说明:首先考虑普通队列的实现,不考虑队列假溢出的情况
- 首先要有一块连续的存储空间,存储的数量,以及指向队列头部和尾部的指针元素
- 在此处head和tail同样也只是数组的索引值,然后通过数组就可以取值
typedef struct Queue{
int *data; //一块连续的存储空间
int size;
int head,tail;
}Queue;
1.2 初始化
- 初始化,开辟内存空间,只需传入队列变量的引用,和队列的最大数量即可
- 声明变量后数据要在堆区开辟空间才行
void init_queue(Queue &q,int n){
q.data = new int[n];
q.size = n;
//头和尾均指向空
q.head = 0;
q.tail = 0;
}
1.3 判空操作
bool empty(Queue q){
return q.head == q.tail;
}
1.4 结构操作
结构操作,推入(将值放入到队列中)和弹出,队列是先进先出,而且不能指定位置
1.4.1 推入
进入时改变的一直都是尾部索引,头部不变 进入时改变的一直都是尾部索引,头部不变
void push(Queue &q,int val){
//1.首先判断是否满了
if(q.size == q.tail) return;
//2.接着进行推入
q.data[q.tail++] = val;//因为初始时tail指向的是0,所以先对data[0]赋值之后再加一
return ;
}
1.4.2 弹出
尾部出去,尾部放的是head指向的,所以改变的是头部
void pop(Queue &q){
//1.首先判断队列是否是空的
if(empty(q)) return;
//2.头部指针向后移动
q.head++;
}
1.5 展示
从head开始到tail进行循环
void display(Queue q){
cout<<"queue["<<q.size<<"]= [";
for(int i=q.head;i<q.tail;i++){
cout<<q.data[i]<<" ";
}
cout<<"]"<<endl;
}
1.6 整理执行
#define MAX_OP 30 //定义最大的队列长度
int main(){
Queue q1;// 当传入的是引用时就不能使用指针了
init_queue(q1,MAX_OP);
for(int i=0;i<MAX_OP;i++){
int val = rand()%100;
int op = rand()%4;
switch(op){
case 0:{
cout<<"push "<<val<<" into queue"<<endl;
push(q1,val);
break;
}
case 1:{
cout<<"push "<<val<<" into queue"<<endl;
push(q1,val);
break;
}
case 2:{
cout<<"push "<<val<<" into queue"<<endl;
push(q1,val);
break;
}
case 3:{
cout<<"pop "<<q1.data[q1.head]<<" from queue"<<endl;
pop(q1);
break;
}
}
display(q1);
}
return 0;
}
*** 执行结果 😗**
循环30次,对可以存放30个元素的队列中进行随机的推入和弹出操作,所以不需要考虑出现假溢出的情况
2.循环队列
只是将上面的队列进行了简单修改,注释即修改后的地方
以下是考虑假溢出的循环队列
循环队列是为了解决
假如一个空队列只能有5个元素,而此时队列中已经存有5个元素,此时head == 0,tail == 5,下一时刻弹出一个元素,即head == 1,tail == 5,
若此时再推入一个元素,如果仍然如同上面所写的那样 的判断条件 if(q.size == q.tail) return;就直接返回了是不对的
,但显然此时队列还没有存满,循环队列就是为了解决这个问题
//1.结构定义
typedef struct Queue{
int *data;
int cnt;//循环队列需要加上一个计数的变量 ,用于表示当前队列中的数量
int size;
int head,tail;
}Queue;
//2.初始化
void init_queue(Queue &q,int n){
q.data = new int[n];
q.size = n;
q.head = 0;
q.tail = 0;
q.cnt = 0; //需要初始化为0
}
//3. 清除队列
void clear(Queue &q){
delete q.data;
}
//4.结构操作
//4.1 推入
void push(Queue &q,int val){
//1.插入时就不能仅仅通过判断尾部表示已满,而是要判断实际所存的数才对
// if(q.size == q.tail) return;
if(q.cnt == q.size) {
cout<<"队列已满,不可再推入"<<endl;
return;
}
//2.接着进行推入
q.data[q.tail++] = val;
//3.并且当尾部指向末尾时要重新指向队列的实际头部
if (q.tail == q.size) {
q.tail -= q.size;//即此时tail == 0
}
//4.且计数要加1
q.cnt += 1;
return ;
}
//4.2 判空
bool empty(Queue q){
//显然当计数为0时队列为空,
//因为head,tail循环所以很有可能当head == tail时并不为0
// return q.head == q.tail;
return q.cnt == 0;
}
//4.3 弹出,
void pop(Queue &q){
if(empty(q)) return;
q.head++;
//1.弹出则时当头指针指向末尾后重新返回真正的头
if (q.head == q.size) {
q.head -= q.size;
}
//2.同时计数要减一
q.cnt -= 1;
}
//5.展示
void display(Queue q){
cout<<"queue["<<q.cnt<<"]= [";
//同样输出也是要从头部输出,要循环计数次
for(int i=q.head,j = 0;j< q.cnt;j++){
// cout<<q.data[i]<<" ";
int ind = (i + j) % q.size;
cout<<q.data[ind]<<" ";
}
cout<<"]"<<endl;
cout<<endl;
}
*** 结果显示: ***
循环30次,向只能存放10个元素的队列中推入或弹出数据的结果