简介
三种实现方式,其实就是指,循环队列如何实现判空判满,区别就在这一块,原因是,如果不修改普通队列,会出现二义性,因为空满的状态其实是同一种状态。 下面介绍这三种方式。
方式一
通过空出一个位置,解决判空/满的冲突,这是第一次介绍循环队列,附上全部实现:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17//模板头文件
template<class T,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 T,int MAXSIZE>
CirQueue<T,MAXSIZE>::CirQueue()
{
	front=rear=0;
}
/*
*	入队函数,因为要求在队尾增加元素,
*   所以,如果线性表未满
*	让尾指针向后移,为了不出现假溢出,充分利用空间,
*   所以有取模操作,当到达尾部最大值,则回到0,重新开始。
*   插入时先移动指针,后插入
*/
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;							//在队尾处插入元素
}
/*
*	出队函数,在队头弹出元素,
*   所以,如果线性表不空
*	头指针后移,指向当前的第一个元素,如果当前头指针指向最后一个格子
*   那么进行取模操作,回到0位置。
*/
template <class T,int QueueSize>
T 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
26int 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<<Q.DeQueue()<<"  ";//因为这个函数写了返回值。。
	}
	return 0;
}
测试结果如图:
方式二
为了不浪费一个元素空间,加上一个布尔变量,在入队操作时候把他变为true,如果flag为真,并且front==rear,那么表明队伍满了;在出队的时候,把他设为false,如果最后一个操作为出队,那么又遇到了front==rear,那么表明,这个时候队伍是空的。代码实现如下:
1  | //与上一方式,在这边,仅加了一个bool的控制量  | 
下面列出实现时候,与第一种的区别之处: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
92template<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