数据结构入门---队列

本篇开始我们将开始下一个线性数据结构-队列的学习。在我们的日常生活中,排队是再常见不过的事情,先到先得,后来的等着,这没有什么问题。队列这一数据结构顾名思义,就是生活中排号排队的抽象化。例如客服人工服务时,有时会要求客户等待,待有客服人员空闲下来时会按照等待客户们的先后次序安排人工服务。或者在银行等待服务窗服务时,需要先获取一个排号单,这个排号单表示你是第几个客户,当排号次序到你的时候你才可以进行业务处理。等等之类都是应用了队列这的特性。

队列是一种特殊的线性表,它限定在一段插入元素,在另一端删除元素。其特性可以总结为 “先进先出”对于一个队列,我们称第一个元素为队头元素,最后一个元素为队尾元素。比如 Q ={ a1,a2,a3……,an} ,a1就是队头元素,an就是队尾元素

队列的抽象数据类型:

/*入队操作*/
void EnQueue(T e)
/*出队操作*/
void DeQueue(T &e)
/*清空队列*/
void Clear()
/*判断是否为空队列*/
bool IsEmpty()
/*获取队头元素*/
T GetHead()
/*获取队列长度*/
int Length()
/*从队头T队尾*/
void Traverse()

队列既然是线性表的一种,所以也会有顺序与链式两种存储结构,我们还是先来介绍队列的顺序存储,也就是用数组的方式
用数组来存放一个队列,当我们想在队列中添加元素时,直接在队尾追加一个元素即可,但对于删除操作,因为队列的删除端在队头,在数组的下表为0的位置,所以删除队头元素需要将整个队列元素逐个往前移。现实生活中的排队即是如此,前一个排队的走了,后面补上去,这似乎没什么问题。不过编程与现实生活不同,我们需要时刻考虑代码的时间性能,对于每删除一个元素,我们都耗费O(n)的时间,这样性能是不是有些低呢?
其实我们不必拘泥于惯性思维,不去限制队列必须在数组前几个元素,对头也不一定非要在下标为0的位置。我们可以使用两个指针front,rear分别指向对头和队尾。空栈如下图所示
这里写图片描述
接下来我们入队a1,a2,a3,a4
这里写图片描述
继续入队a5,a6,a7,a8,出队a1,a2
这里写图片描述
这时我们发现出现了溢出的状况,因为数组无法再向后添加元素。但是这时数组的最前方却空留两个空闲空间。这种溢出我们称之为假溢出。
那么如何解决这种情况呢?道理也很简单,只要能让rear回来就行了呗。这样将我们的队列数组想象成一种循环的结构,就有了循环队列。
数组后面满了,就回来从头开始,这种首尾相接的数组队列我们称为循环队列
对于空队列,就是 front==rear。对于队列满,front与rear相差1,但它们可能只差一个单位,也可能差整一圈这里写图片描述
这里写图片描述

所以我们对于队列满的判断就为 (rear+1)%MaxSize这样取余操作。
对于队列的长度计算,也是有两种情况,当rear在front后面时,就直接用rear-front即可。当rear在front前面时,队列长度分为两段,MaxSize-front以及rear-0因此满足所有情况的长度计算公式就为 (rear-front+MaxSize)%MaxSize

那么接下来就将循环队列实现为具体的代码吧
Queue类成员变量:

template<class T>
class ReQueue {
private:
    T *elements;
    int front;  //队头
    int rear;       //队尾
    int MAXSIZE;        //最大长度

初始化操作:

    ReQueue()
    {
        /*队头队尾置0*/
        this->front = 0;
        this->rear = 0;
        MAXSIZE = 10;   //队列最大长度
        elements = new T[MAXSIZE];  //分配数组空间
    }

入队操作:

    void EnQueue(T e)
    {
        if ((rear + 1) % MAXSIZE == front)
        {
            cout << "队列满" << endl;
            return;
        }
        this->elements[rear] = e;
        this->rear = (this->rear + 1) % MAXSIZE;    //如果到数组末尾则跳到数组头部
    }

出队操作:

void DeQueue(T &e)
    {
        if (this->IsEmpty())
        {
            cout << "队列空" << endl;
            return;
        }
        e = this->elements[this->front];
        this->front = (this->front + 1) % MAXSIZE;  //如果到数组末尾则跳到数组头部
    }

返回队列长度:

int Length()
    {
        return (this->rear - this->front + MAXSIZE) % MAXSIZE;
    }

队列的链式存储依然与普通单链表区别不大,只是将插入限制在链表尾,删除在链表头部。

这里写图片描述

空队列时front与rea同时指向头结点。其余操作与单链表基本相同,下面直接放出入队与出队的代码

void EnQueue(T e)
    {
        Node *q = new Node; //生成新结点
        q->data = e;        //为新结点赋值
        /*链表尾插入一个元素*/
        this->rear->next = q;
        this->rear = q;
        this->rear->next = NULL;
        this->length++; //队长+1 


    }
void DeQueue(T &e)
    {
        if (this->IsEmpty())
        {
            cout << "空队列" << endl;
            return;
        }
        Node *q = this->front->next;    //定位对头指针
        e = q->data;        //回收删除结点数据
        this->front->next = q->next;
        delete q;   //回收结点空间
        this->length--;     //队长-1
    }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值