赞
踩
队列是一种先进先出(FIFO,First-In-First-Out)的线性表,通常用链表或者数组来实现。队列只能在队尾插入元素,只能在队首删除元素。
队列的一些性质:
1.出队方案唯一
2.队首元素先出
3.新元素插入队尾
线性队列的初始化:
- #include <iostream>
- using namespace std;
- class Queue{
- private:
- int *data; //定义指向整型的指针,从而动态开辟内存
- int head,tail,length; //head指向队首,tail指向队尾,length表示队列的长度
- public:
- Queue (int length_input){ //构造函数,对新声明的队列对象进行初始化
- data = new int [length_input]; //动态开辟100个整型数据的空间
- length = length_input; //为队列的长度赋初始值
- head = 0; //起初head为0,表示head值未改变的情况下,一直指向首元素
- tail = -1; //tail初始值为-1,表示此时队列为空,无任何数据元素
- }
- ~Queue(){
- delete [] data; //析构函数,删除动态开辟的内存
- }
- };
- int main() {
- Queue queue(100); //声明一个队列对象,并初始化
- return 0;
- }
入队操作:
- #include <iostream>
- using namespace std;
- class Queue{
- private:
- int *data; //定义指向整型的指针,从而动态开辟内存
- int head,tail,length; //head指向队首,tail指向队尾,length表示队列的长度
- public:
- Queue (int length_input){ //构造函数,对新声明的队列对象进行初始化
- data = new int [length_input]; //动态开辟100个整型数据的空间
- length = length_input; //为队列的长度赋初始值
- head = 0; //起初head为0,表示head值未改变的情况下,一直指向首元素
- tail = -1; //tail初始值为-1,表示此时队列为空,无任何数据元素
- }
- ~Queue(){
- delete [] data; //析构函数,删除动态开辟的内存
- }
- void push(int element){ //入队操作,只能从队列的尾部插入数据元素
- if(tail+1 < length){ //队列未满的时候才能插入,否则则插入失败
- ++tail; //队尾指针先往后移动一位
- data[tail] = element; //再插入队尾指针指向的位置
- }
- }
- void output(){
- for(int i = head;i <= tail;i++){ //从队首一直遍历到队尾
- cout << data[i] << " ";
- }
- cout << endl;
- }
- };
- int main() {
- Queue queue(100); //声明一个队列对象,并初始化
- for(int i = 1;i <= 10;++i){
- queue.push(i); //将1-10这10个数据元素依次插入队列中
- }
- queue.output(); //调用输出的方法
- return 0;
- }
效果图:
出队以及获取队首元素的操作:
- #include <iostream>
- using namespace std;
- class Queue{
- private:
- int *data; //定义指向整型的指针,从而动态开辟内存
- int head,tail,length; //head指向队首,tail指向队尾,length表示队列的长度
- public:
- Queue (int length_input){ //构造函数,对新声明的队列对象进行初始化
- data = new int [length_input]; //动态开辟100个整型数据的空间
- length = length_input; //为队列的长度赋初始值
- head = 0; //起初head为0,表示head值未改变的情况下,一直指向首元素
- tail = -1; //tail初始值为-1,表示此时队列为空,无任何数据元素
- }
- ~Queue(){
- delete [] data; //析构函数,删除动态开辟的内存
- }
- void push(int element){ //入队操作,只能从队列的尾部插入数据元素
- if(tail+1 < length){ //队列未满的时候才能插入,否则则插入失败
- ++tail; //队尾指针先往后移动一位
- data[tail] = element; //再插入队尾指针指向的位置
- }
- }
- void pop(){
- if(tail < 0){ //队列为空,出队失败
- return;
- }
- ++head; //队首指针后移,表示原来队首已出列
- }
- int top(){
- if(tail > -1){ //队列不为空的情况下才能获取队首元素
- return data[head];
- }
- }
- void output(){
- for(int i = head;i <= tail;i++){ //从队首一直遍历到队尾
- cout << data[i] << " ";
- }
- cout << endl;
- }
- };
- int main() {
- Queue queue(100); //声明一个队列对象,并初始化
- for(int i = 1;i <= 10;++i){
- queue.push(i); //将1-10这10个数据元素依次插入队列中
- }
- queue.output(); //调用输出的方法
- cout << "当前的队首元素为:" << queue.top() << endl;
- queue.pop(); //出队
- queue.output(); //调用输出的方法
- cout << "出队操作后的队首元素为:" << queue.top() << endl;
- return 0;
- }
效果图:
可是,普通队列有什么局限性呢?比如说我们动态申请了能够100个整型数据的数组来容纳队列中的元素,而正如函数中的判断语句:
tail+1 >= length
比如说在队列操作的过程中,不断地有元素出队,而且不断地有元素入队,那么的话,当tail的值达到length-1时,实际上就说明“队列”已经满了,但是如果此时head的值不是0的话,实际上这个队列是没有装满100个数据元素的,head之前的空间都将会被浪费。这种现象在我们程序设计中,称作“假溢出”现象
假溢出:系统作为队列用的存储区还没有满,但队列却发生了溢出,我们把这种现象称为"假溢出"。
我们这时候肯定会想,如果这时候队尾指针tail能够继续越过队尾,往0—head之间增添元素的话,那么就可以避免空间的浪费了,所以呢,基于这种想法,我们便引进了循环队列来对当前队列进行改进
循环队列,顾名思义,就是以循环的方式来存储队列。回顾一下我们之前用到的队尾标记 tail。对于循环队列,当队尾标记 tail 到达数组的上界后,如果队列内的元素个数没有达到容量上限,就跳转到数组的起始位置,也就是 0 的位置;队首标记 head 到达数组上界采取同样的处理。通过这样的方法,我们就能够最大化利用内存空间,避免“假上溢”的情况出现啦。
循环队列:
下面我们将改进线性队列的代码,实现循环队列的一些基本操作
循环队列的一些性质:
1.在循环队列里,如果容量没有达到上限,当队尾队首标记达到数组上界后,就跳转到数组起始位置
2.在线性队列里,当 tail 达到队列上限后,继续插入就会发生“假上溢”的情况
3.循环队列里可通过统计队列里元素个数,判断能否继续往队列里插入元素
首先,我们增加一个count变量来计算入队的元素个数,这样的话,当count = length的时候,才表示真正的队列“上溢”,所以呢,对于循环队列的入队操作,可分两种情况讨论:
1.如果队尾指针此时并未指向队列的最后一位,那么队尾指针直接前移
2.当队尾指针此时指向最后一位时,那么当队列未满时,则队尾指针将跳转至数组起始位置
第一种情况下,我们可以写成:
tail = tail + 1;
而第二种情况下,我们可以写成:
tail = (tail + 1)% length
所以呢,两种情况综合起来,则可写成:
tail = (tail + 1) % length;
同样的,出队操作时,则有:
head = (head + 1) % length;
这里特别需要注意的是,循环队列的遍历输出时,循环截止条件不再是head <= tail,而是head != tail +1,这是为什么呢?原因其实也很简单,因为
当队尾队首标记达到数组上界后,就跳转到数组起始位置,所以tail是可能小于head的,而无论是head<tail,亦或是head > tail,都是顺时针从head循环至tail,所以可用此循环截止条件:
- for(int i = head;i != tail + 1;i = (i + 1)%length){ //从队首一直遍历到队尾,当遍历到最后一位时,跳转至数组起始位置
- //特别注意此时循环截止的条件应该是i != tail + 1,因此可能tail的值小于head
- cout << data[i] << " ";
- }
循环队列初始化,入队出队等操作完整代码实现:
- #include <iostream>
- using namespace std;
- class Queue{
- private:
- int *data; //定义指向整型的指针,从而动态开辟内存
- int head,tail,length,count; //head指向队首,tail指向队尾,length表示队列的长度,count用于记录队列中元素个数,从而判断队列是否已满
- public:
- Queue (int length_input){ //构造函数,对新声明的队列对象进行初始化
- data = new int [length_input]; //动态开辟100个整型数据的空间
- length = length_input; //为队列的长度赋初始值
- head = 0; //起初head为0,表示head值未改变的情况下,一直指向首元素
- tail = -1; //tail初始值为-1,表示此时队列为空,无任何数据元素
- count = 0;
- }
- ~Queue(){
- delete [] data; //析构函数,删除动态开辟的内存
- }
- void push(int element){ //入队操作,只能从队列的尾部插入数据元素
- if(count < length){ //队列未满的时候才能插入,否则则插入失败
- tail = (tail + 1) % length; //分两种情况,如果队尾指针此时并未指向队列的最后一位,那么队尾指针直接前移,而当队尾指针此时指向最后一位时
- data[tail] = element; //那么当队列未满时,则队尾指针将跳转至数组起始位置,再将数据元素插入队尾指针指向的位置
- ++count; //入队成功,队列中元素数量加一
- }
- }
- void pop(){
- if(count < 0){ //队列为空,出队失败
- return;
- }
- head = (head + 1) % length; //同样,根据循环队列的性质得出
- --count; //出队成功,队列中元素数量减一
- }
- int top(){
- if(count > 0){ //队列不为空的情况下才能获取队首元素
- return data[head];
- }
- }
- void output(){
- for(int i = head;i != tail + 1;i = (i + 1)%length){ //从队首一直遍历到队尾,当遍历到最后一位时,跳转至数组起始位置
- //特别注意此时循环截止的条件应该是i != tail + 1,因此可能tail的值小于head
- cout << data[i] << " ";
- }
- cout << endl;
- }
- };
- int main() {
- Queue queue(100); //声明一个队列对象,并初始化
- for(int i = 1;i <= 10;++i){
- queue.push(i); //将1-10这10个数据元素依次插入队列中
- }
- queue.output(); //调用输出的方法
- cout << "当前的队首元素为:" << queue.top() << endl;
- queue.pop(); //出队
- queue.output(); //调用输出的方法
- cout << "出队操作后的队首元素为:" << queue.top() << endl;
- return 0;
- }
如有错误,还请指正,O(∩_∩)O谢谢
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。