• 队列的三种实现方式


    简介

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

    方式一

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

    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
  • 相关阅读:
    java:选择排序
    java:快速排序
    MYSQL 5.7版本修改密码
    多级代理下获取客户端真实IP
    map相关操作:map遍历,map转换为list
    【导航】微信开发者相关网址导航
    【java】微信开发后台官方后台配置篇
    HTML页面加载loading
    表单JS提交失效
    后台request获得所有参数(当你不知道它包含什么参数)
  • 原文地址:https://www.cnblogs.com/lijianming180/p/12288725.html
Copyright © 2020-2023  润新知