队列的三种实现方式

简介

三种实现方式,其实就是指,循环队列如何实现判空判满,区别就在这一块,原因是,如果不修改普通队列,会出现二义性,因为空满的状态其实是同一种状态。 下面介绍这三种方式。

方式一

通过空出一个位置,解决判空/满的冲突,这是第一次介绍循环队列,附上全部实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

template<class ,int MAXSIZE> //定义类模板CirQueue
class CirQueue
{
public:
CirQueue();
~CirQueue();

void EnQueue(T x);//入栈操作,将元素x入栈
T DeQueue(); //出栈操作,将队头元素出队
T GetQueue(); //取队头元素
bool IsFull(); //判断队列是否为满
bool Empty(); //判断队列是否为空
private:
T data[MAXSIZE]; //元素域
int front,rear; //队头和队尾的指针
};

队列,其实很简单,他是只允许在一端进行插入,而在另一端进行删除的运算受限的线性表,所以比较容易掌握。

下面附上具体函数的实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
/*
* 无参构造函数,
*/
template<class ,int MAXSIZE>
CirQueue<T,MAXSIZE>::CirQueue()
{
front=rear=0;
}
/*
* 入队函数,因为要求在队尾增加元素,
* 所以,如果线性表未满
* 让尾指针向后移,为了不出现假溢出,充分利用空间,
* 所以有取模操作,当到达尾部最大值,则回到0,重新开始。
* 插入时先移动指针,后插入
*/
template <class ,int QueueSize>
void CirQueue<T,QueueSize>::EnQueue(T x)
{
if(IsFull()) //判满
{
cout<<"上溢"<<endl;
return;
}
rear = (rear+1) % QueueSize; //队尾指针在循环意义上加1
data[rear] = x; //在队尾处插入元素
}
/*
* 出队函数,在队头弹出元素,
* 所以,如果线性表不空
* 头指针后移,指向当前的第一个元素,如果当前头指针指向最后一个格子
* 那么进行取模操作,回到0位置。
*/
template <class ,int QueueSize>
CirQueue<T,QueueSize>::DeQueue()
{

if(Empty()) //判空
{
cerr<<"下溢"<<endl;
}
front = (front+1) % QueueSize; //队头指针在循环意义上加1
return data[front]; //读取并返回出队前的队头元素
}
/*
* 取队头元素,不出队伍。
*/
template <class T,int QueueSize>
T CirQueue<T,QueueSize>::GetQueue()
{
if(Empty()) //判空
{
cerr<<"下溢"<<endl;
exit(0);
}
int i = (front+1) % QueueSize; //队头指针在循环意义上加1
return data[i];
}
/*
* 判空函数
* 如果当两指针重合,队列为空
*/
template <class T,int QueueSize>
bool CirQueue<T,QueueSize>::Empty()
{
return front == rear;
}
/*
* 判满函数
* 如果尾指针逻辑后一格为头指针
* 表示空间满
*/
template <class T,int QueueSize>
bool CirQueue<T,QueueSize>::IsFull()
{
if( (rear+1) % QueueSize == front)
return true;
else
return false;
}

下面对这个函数做个基本的测试。。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
int main()
{
CirQueue<int,5> Q;
//塞满整个队列
cout<<"塞满队列。。。"<<endl;
for (int i=0;;i++)
{
if (!Q.IsFull())
{
Q.EnQueue(pow(2,i));
}
else
break;
}
//测试访问头元素
cout<<"取出头元素:";
cout<<Q.GetQueue()<<endl;

//依次出列
cout<<"依次出列:";
while (!Q.Empty())
{
cout<&l 大专栏  队列的三种实现方式t;Q.DeQueue()<<" ";//因为这个函数写了返回值。。
}
return 0;
}

测试结果如图:
此处输入图片的描述

方式二

为了不浪费一个元素空间,加上一个布尔变量,在入队操作时候把他变为true,如果flag为真,并且front==rear,那么表明队伍满了;在出队的时候,把他设为false,如果最后一个操作为出队,那么又遇到了front==rear,那么表明,这个时候队伍是空的。代码实现如下:

1
2
3
//与上一方式,在这边,仅加了一个bool的控制量
private
bool flag;

下面列出实现时候,与第一种的区别之处:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
template<class T,int MAXSIZE>
CirQueue<T,MAXSIZE>::~CirQueue()
{
}
/*
* 无参构造函数,设置flag为假,保证,初始判断让程序判断为空
*/
template<class T,int MAXSIZE>
CirQueue<T,MAXSIZE>::CirQueue()
{
front=rear=0;
flag=false;
}
/*
* 入队函数,因为要求在队尾增加元素,
* 所以,如果线性表未满
* 让尾指针向后移,为了不出现假溢出,充分利用空间,
* 所以有取模操作,当到达尾部最大值,则回到0,重新开始。
* 插入时先移动指针,后插入
* 同时,让flag置为true
*/
template <class T,int QueueSize>
void CirQueue<T,QueueSize>::EnQueue(T x)
{
if(IsFull()) //判满
{
cout<<"上溢"<<endl;
return;
}
rear = (rear+1) % QueueSize; //队尾指针在循环意义上加1
data[rear] = x; //在队尾处插入元素
flag=true;
}
/*
* 出队函数,在队头弹出元素,
* 所以,如果线性表不空
* 头指针后移,指向当前的第一个元素,如果当前头指针指向最后一个格子,
* 那么进行取模操作,回到0位置。
* 所以队头所指的空间是么有元素的。
* 同时置flag为false
*/
template <class T,int QueueSize>
T CirQueue<T,QueueSize>::DeQueue()
{

if(Empty()) //判空
{
cerr<<"下溢"<<endl;
}
front = (front+1) % QueueSize; //队头指针在循环意义上加1
flag=false;
return data[front]; //读取并返回出队前的队头元素
}
/*
* 取队头元素,不出队伍。
*/
template <class T,int QueueSize>
T CirQueue<T,QueueSize>::GetQueue()
{
if(Empty()) //判空
{
cerr<<"下溢"<<endl;
exit(0);
}
int i = (front+1) % QueueSize; //队头指针在循环意义上加1
return data[i];
}
/*
* 判空函数
* 如果当两指针重合,队列为空
*/
template <class T,int QueueSize>
bool CirQueue<T,QueueSize>::Empty()
{
if ( flag==false && front == rear )
return true;
else
return false;
}
/*
* 判满函数
* 如果尾指针逻辑后一格为头指针
* 表示空间满
*/
template <class T,int QueueSize>
bool CirQueue<T,QueueSize>::IsFull()
{
if ( flag==true && front == rear )
return true;
else
return false;
}

测试函数不做变动,测试结果如下:
此处输入图片的描述

方式三

用计数器来存储个数,当计数器等于0的时候,空;当计数器等于MAXSIZE的时候,满!
程序很简单,不多说。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/*
* 判空函数
*/
template <class T,int QueueSize>
bool CirQueue<T,QueueSize>::Empty()
{
return count==0;
}
/*
* 判满函数
*/
template <class T,int QueueSize>
bool CirQueue<T,QueueSize>::IsFull()
{
return count==QueueSize;
}

测试结果和方法二同。

总结:

三种方法

  • 空出一个格子
  • 加上flag判定
  • 加上计数器

还要注意一点:为了实现循环计数,要掌握下面的语句

  • raer=(rear+1)%MAXSIZE
  • front=(front+1)%MAXSIZE
原文地址:https://www.cnblogs.com/lijianming180/p/12288725.html