C++实现普通队列,循环队列的基本操作(初始化,入队,出队,获取队列首元素等)

来源:互联网 发布:前端优化14个性能规则 编辑:程序博客网 时间:2024/04/24 04:28

队列是一种先进先出(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时,实际上就说明“队列”已经满了。但是实际情况真的是如此吗?

比如说在队列操作的过程中,不断地有元素出队,而且不断地有元素入队,那么的话,当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谢谢



1 0
原创粉丝点击