数据结构与算法之美【7】-队列

目录

一、概述

二、顺序队列

三、链式队列

三、循环队列

四、示例代码


一、概述

队列跟栈非常相似,也是一种操作受限的线性表数据结构。跟栈一样,队列可以用数组来实现,也可以用链表来实现,用数组实现的队列叫作顺序队列,用链表实现的队列叫作链式队列

队列最基本的操作也是两个:入队 enqueue()(放一个数据到队列尾部)和出队 delqueue()(从队列头部取一个元素),所以队列需要两个指针:一个是 head 指针,指向队头,一个是 tail 指针,指向队尾。

在这里插入图片描述

二、顺序队列

顺序队列的底层实现结构是数组,实现思想是:预分配一段空间,当入队时,若空间满,则丢弃或者阻塞,若空间不满,再判断tail指针是否在数组尾部,如果不在尾部,则直接入队即可,若tail指针已经在尾部,则将head至tail之间的数据迁移到数组起始位置,然后再入队。

此时队满的判断条件是 tail == n,队空的判断条件是 head == tail。

此时的时间复杂度如何分析呢?

如果队尾没有满,可以直接入队,时间复杂度为O(1)。

当tail=n的时候时间复杂度才迅速飙升为O(n),即需要进行n次搬移,此时n次的搬移如果均摊到0~n这n次上,其实总体的均摊复杂度还是O(1)

三、链式队列

链式队列同样需要两个指针:head 指针和 tail 指针。它们分别指向链表的第一个结点和最后一个结点。如图所示,入队时,tail->next= new_node, tail = tail->next;出队时,head = head->next。

因为链式队列没有数据迁移的操作,所以它的入队和出队的时间复杂度都是O(1)。

三、循环队列

上面我们讲到顺序队列时候,当tail指针到达数据尾部时,此时如果再入队,将不得不进行数据迁移,实际上,我们可以通过代码来规避这个操作,那就是将顺序队列改造成循环队列。原本数组是有头有尾的,是一条直线。现在我们把首尾相连,改成了一个环。

为了更好的理解,我们模拟一下这个循环队列入队的情况:下左图中这个队列的大小为 8,当前 head=4,tail=7。当有一个新的元素 a 入队时,我们放入下标为 7 的位置。但这个时候,我们并不把 tail 更新为 8,而是将其在环中后移一位,到下标为 0 的位置。当再有一个元素 b 入队时,我们将 b 放入下标为 0 的位置,然后 tail 加 1 更新为 1。所以,在 a,b 依次入队之后,循环队列中的元素就变成了下右图的样子:

那我们又如何判断循环队列的队空和队满呢?

队空时,和顺序队列一样,tail == head。

队满时呢?如下图,当队列处于如下状态时,再入队,此时tail将移动到head位置,即tail == head,但很明显,这和我们判断队空的条件一模一样,这样我们就无法区分出当tail == head时,队列处于什么状态,所以,为了区分开,我们将队列最接近满的时候的条件认定为队列满,即head == tail+1,也可以使用(tail+1)%n=head作为判断。

从下图可以看出,此时tail里面是没有存储数据的,即循环队列会浪费一个数组的存储空间,但这也是没办法。

四、示例代码

// Queue.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <iostream>  
#include <new>  
 
//QueueList  队列 
//定义  
template<class T>  
class QueueList  
{  
public :  
    QueueList(int MaxListSize=10);  //构造函数
    ~QueueList()                             //析构函数 
    {
        delete[] elements;
    }  
  
    bool IsEmpty() const             //判断是否为空
    {
        return rear==front;
    }  

    int Length() const               //获取队列长度
    {
        return rear-front;
    }  

    int EnQueue(T& x); //入队 

    T DeQueue();  //出队

    T geteQueueData();  //获取队首数据,不出队

    bool IsFull() const             //判断是否满
    {
        return (rear+1)%MaxSize==front;
    }  

    void clear() ;  
  
private:  
    int rear;  //队尾指针
    int front;  //队首指针
    int MaxSize;  //队列最大长度
    T *elements;//一维动态数组  
  
};  

//实现...  
template<class T>  
QueueList<T>::QueueList(int MaxListSize)  
{   
    //基于公式的线性表的构造函数  
    MaxSize=MaxListSize;  
    elements=new T[MaxSize];  
    rear = front = 0;  
}  
  
template<class T>  
int QueueList<T>::EnQueue(T& x) 
{  
    if(IsFull()) 
    {
        return -1;  
    }

    elements[rear] = x;  
    rear = (rear+1)%MaxSize;
    return 0;  
}  
  
template<class T>  
T QueueList<T>::DeQueue()  
{  
    T data;
    if(IsEmpty()) 
    {
        return NULL;  
    }

    data = elements[front];
    front = (front+1)%MaxSize;

    return data;  
}  
  
template<class T>  
T QueueList<T>::geteQueueData() 
{  
    if(IsEmpty()) 
    {
        return NULL;  
    }

    return elements[front];
}  
  
template<class T>  
void QueueList<T>::clear()
{  
    delete []elements;

    return ;  
}  
  

 
void QueueListSample()  
{  
    int j = 0;
    int a = 10,b = 20,c = 30,d = 40,e = 50;
    QueueList<int> Q(6);  
    std::cout<<"Length="<<Q.Length()<<std::endl;  
    std::cout<<"IsEmpty="<<Q.IsEmpty()<<std::endl;  
  
    Q.EnQueue(a);
    Q.EnQueue(b);  
    Q.EnQueue(c);  
    Q.EnQueue(d);  
    Q.EnQueue(e);  
    
    j = Q.DeQueue(); 
    std::cout<<j<<std::endl;  

    j = Q.DeQueue(); 
    std::cout<<j<<std::endl; 

    j = Q.DeQueue(); 
    std::cout<<j<<std::endl; 

    j = Q.DeQueue(); 
    std::cout<<j<<std::endl; 

    j = Q.DeQueue(); 
    std::cout<<j<<std::endl; 

    return;
}  

int _tmain(int argc, _TCHAR* argv[])  
{  
    QueueListSample();  
  
    //暂停操作  
    char str;  
    std::cin>>str;  
    //程序结束  
    return 0;  
}  

运行结果为:

在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Chiang木

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值