以下是数据结构中关于循环的声明、初始化、判断空与满、出队、入队、遍历队等基础操作(编程风格参考严蔚敏数据结构)。
本代码主要将书本代码进行补充完成调试的(元素最大数量为MAXSIZE-1),以及自己新加进去的遍历操作。
头文件及宏
#include<iostream>
#include<stdio.h>
using namespace std;
#define QElemType char
#define Status int//表示状态
#define OK 1
#define ERROR 0
#define MAXSIZE 5
定义声明及初始化
typedef struct SeQueue{
QElemType *base;
int front;
int rear;
}Squeue;
Status initial(Squeue &sq){
sq.base = new QElemType[MAXSIZE];
if(!sq.base){
cout<<"初始化失败\n";
return ERROR;
}
sq.front = sq.rear = 0;
return OK;
}
步骤:
- 声明顺序队列sq;
- 给sq的base数组分配内存空间;
- 最初没有元素在数组内,队头队尾均设为0。
入队
Status Enqueue(Squeue &sq,QElemType e){
sq.base[sq.rear] = e;
sq.rear = (sq.rear+1)%MAXSIZE;//为了让rear一直在[0,MAXSIZXE-1]这个范围
return OK;
}
void addElem(Squeue &sq){
QElemType elem = '0';
while(elem!='*' && isNotFull(sq)){
cout<<"请输入元素(输入*结束进队):";
cin>>elem;
if(elem!='*'){
Enqueue(sq,elem);
}
}
cout<<"入队完毕\n";
showSqueue(sq);
}
步骤:
- 先判断是否为满队;
- 尾位置新增元素;
- 尾位置自增1。
关于sq.rear = (sq.rear+1)%MAXSIZE的解释:
假设MAXSIZE为5,rear的变化只能是在[0,4]这个区间,因为当rear到4的时候,再增加一个元素, (rear+1)%MAXSIZE = 0,就回到0了。
出队
Status Dequeue(Squeue &sq,QElemType &e){
e = sq.base[sq.front];
sq.front = (sq.front+1)%MAXSIZE;//为了让front一直在[0,MAXSIZXE-1]这个范围
return OK;
}
void delElem(Squeue &sq){
QElemType elem;
if(isNotEmpty(sq)){
if(Dequeue(sq,elem))
cout<<"出队成功,本次出栈元素为:"<<elem<<endl;
}
}
关于sq.front = (sq.front+1)%MAXSIZE的解释:
假设MAXSIZE为5,front的变化只能是在[0,4]这个区间,因为当front到4的时候,再增加一个元素, (front+1)%MAXSIZE = 0,就回到0了。
循环队列的关键就在这里:front和rear的循环计算方式的思想是一致的,在判断空与满的时候就要使用循环的思想进行判断(因为会出现尾指针的数字小于头指针的情况)。
判断队空/队满
Status isNotFull(Squeue sq){
cout<<sq.rear<<" "<<sq.front<<endl;
if((sq.rear+1)%MAXSIZE == sq.front){
cout<<"队满!\n";
return ERROR;
}
return OK;
}
Status isNotEmpty(Squeue sq){
if(sq.front==sq.rear){
cout<<"队空!\n";
return ERROR;
}
return OK;
}
关于满队(sq.rear+1)%MAXSIZE == sq.front的解释:
假设尺寸为5,当front是0时则rear到4的时候就是满队,计算的时候就是(4+1)%5 = 0 = front。
当出队了2个元素后再进队2个元素此时的头位置和尾位置是这样的:
front=2,rear = 1。此时尾指针的数字小于头指针的数据,单纯使用rear-front的思想是行不通的,必须使用循环思想进行思考。
如果说front和rear的最大范围就是MAXSIZE-1,再往下走就是回到循环起点继续递增,周而复始。虽然rear比front小,但是这意味着rear已经走到下一个以MAXSIZE为基数的循环了。对MAXSIZE取余就是去除多出的循环的过程。
(sq.rear+1)%MAXSIZE = (1+1)%5 = 2 = front;此时就是队满。
此时头到尾的下标为:2 3 4 0 1。
但是这种写法的队满数量其实是:MAXSIZE-1(书本也说明了)。
原因如下:
靠(sq.rear+1)%MAXSIZE == sq.front判断队满的话,会导致循环队列里最大元素数量为MAXSIZE-1。
我们来回顾一下入队时的步骤:
入队步骤:
- 先判断是否为满队;
- 尾位置新增元素;
- 尾位置自增1。
我们入队是元素先入队,尾位置再自增。当尾位置在0的时候,元素入队了,此时尾位置在1的位置,此时1的位置是没有元素的。但是我们判断满队的时候,使用的是“(尾位置+1)%最大尺寸==头位置”的方式进行判断,这就会导致如下结果:
设最大尺寸MAXSIZE为5:
当尾位置在3的时候,(3+1)%4 !=0;将新元素填入3的位置,尾位置+1,rear变成4。
(4+1)%4 ==0,直接就判断为满队跳出了,而此时的4的位置其实还是空的。这种写法就导致队列的元素数量最多只能是MAXSIZE-1。
遍历
void showSqueue(Squeue sq){
cout<<"当前队元素:\n";
if(isNotEmpty(sq)){
int i = sq.front;
cout<<sq.base[i]<<" ";
i=(i+1)%MAXSIZE;
while(i!=sq.rear){
cout<<sq.base[i]<<" ";
i=(i+1)%MAXSIZE;
}
cout<<endl;
}
}
运行截图