数据结构循环队列-设计循环队列

数据结构循环队列-设计循环队列


前言:我们知道队列底层并不适合用顺序结构进行实现,因为队列为尾插头删的操作,当进行头删时我们需要将后序元素进行往前搬移,时间复杂度便是O(N),这样出队的效率便大大降低了,故我们通常选用链表作为队列实现的底层结构。
问题提出:那若使用连续的存储空间时,出队列的操作就无法达到O(1)吗?

循环队列的提出

1.front为对头元素下标,back为队尾元素下标。当队列进行头删时,为使出队列的操作达到O(1),我们不进行元素的搬移,将对头front往后移动一步。
我们将1 2 3 4进行依次入队列,当对1进行头删操作后,我们将front进行往后移动一步,如下所示:
在这里插入图片描述
2.在1的操作上将5 6 7 8 9 0进行依次入队操作
blog.csdnimg.cn/b0482025560e41a092447e850b3ae0e6.png)
我们可以看到,在对元素进行入队的时候,back一直往后移动,移动到空间末尾的时候,认为此队列已满,而空间起始位置是有剩余空间存在,我们将此种情况称为***队列假溢出问题。***
在这里插入图片描述
而若队列中有效元素已将空间彻底填充满,而后序还需要进行元素的入对操作,对队列的操作一直为入队,且在入队列过程中没有出队列造成空间为满,我们将此种情况称为***队列真溢出***。
3.循环队列能有效地解决队列空间假溢出问题。

循环队列

1.我们按照队列元素头删front向后移动,元素尾插back向后移动,初始队列为空时,front和back在初始位置0。
在这里插入图片描述
我们往空循环队列里插入a1,a2,a3,a4,a5,a6,a7,a8,每插入一个元素back指针往后移动一步,当队列空间被有效元素填满时,则我们可以看到,空队列的情况和队列为满的情况一样,那我们该怎么区分两种情况呢?
2.一般循环队列空间大小固定,以N为循环队列的空间容纳元素个数。

方式一:少用一个存储空间。
队列为空:front=back
队列为满:(back+1)%N=front
少用一个存储空间队列为空为满情况

方式二:使用标记flag。
flag初始值设置为0,当元素进行入队操作时,flag设置为1,当元素进行出队操作时,flag设置为0。
队列为空:front=back && flag=1
队列为满:front=back && flag=0
使用标记时队列为空为满情况
方式三:使用计数count。
当元素进行入队操作时,count进行加1操作。
当元素进行出队操作时,count进行减1操作。
队列为空:count=0
队列为满:count=N

循环队列相关操作

此处我们以OJ题来进行循环队列的相应操作展示。链接如下:https://leetcode-cn.com/problems/design-circular-queue/
我们定义循环队列结构如下:

typedef struct 
{
    //循环队列一般空间大小固定
    int*array;
    int capacity;//空间大小
    int size;//队列有效元素个数,主要用来判断队列是空还是满
    int front;//对头
    int back;//队尾

} MyCircularQueue;

1.MyCircularQueue(k): 构造器,设置队列长度为 k 。

MyCircularQueue* myCircularQueueCreate(int k) 
{
    MyCircularQueue* mq=(MyCircularQueue*)malloc(sizeof(MyCircularQueue));
    if(mq==NULL)
    {
        assert(0);
        return NULL;
    }
    mq->array=(int*)malloc(sizeof(int)*k);
    mq->capacity=k;
    mq->size=0;
    mq->front=0;
    mq->back=0;
    return mq;
}

2.Front: 从队首获取元素。如果队列为空,返回 -1 。

//获取对头元素,队列为空返回-1
int myCircularQueueFront(MyCircularQueue* obj) 
{
    if(myCircularQueueIsEmpty(obj))
    {
        return -1;
    }
    return obj->array[obj->front];
}

3.Rear: 获取队尾元素。如果队列为空,返回 -1 。

//获取队尾元素,队列为空返回-1
int myCircularQueueRear(MyCircularQueue* obj) 
{
    if(myCircularQueueIsEmpty(obj))
    {
        return -1;
    }
    return obj->array[(obj->back-1+obj->capacity)%obj->capacity];
}

4.enQueue(value): 向循环队列插入一个元素。如果成功插入则返回真。

//向循环队列插入元素,成功插入则返回真
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) 
{
    if(myCircularQueueIsFull(obj))//队列满则无法插入数值
    {
        return false;
    }
    //元素存储到back的位置上
    obj->array[obj->back]=value;
    obj->back++;
    //插入元素后可能造成队列为满,则得将back放置到队列的起始位置
    if(obj->back==obj->capacity)
    obj->back=0;

    obj->size++;
    return true;
}

5.deQueue(): 从循环队列中删除一个元素。如果成功删除则返回真。

//删除元素,成功删除返回真
bool myCircularQueueDeQueue(MyCircularQueue* obj) 
{
    //队列判空,为空则无法删除元素
    if(myCircularQueueIsEmpty(obj))
    {
        return false;
    }
    //队列不为空,则进行队列元素删除操作
    obj->front++;
    //front移动到队列末尾时,将front置为空
    if(obj->front==obj->capacity)
    {
        obj->front=0;
    }
    obj->size--;
    return true;
}

6.isEmpty(): 检查循环队列是否为空。

bool myCircularQueueIsEmpty(MyCircularQueue* obj) 
{
    return obj->size==0;
}

7.isFull(): 检查循环队列是否已满。

bool myCircularQueueIsFull(MyCircularQueue* obj) 
{
    if(obj->size==obj->capacity)
    {
        return true;
    }
    return false;
}

8.Free();销毁队列

void myCircularQueueFree(MyCircularQueue* obj) 
{
    free(obj->array);
    free(obj);
}

9.完整代码如下:

typedef struct 
{
    //循环队列一般空间大小固定
    int*array;
    int capacity;//空间大小
    int size;//队列有效元素个数,主要用来判断队列是空还是满
    int front;//对头
    int back;//队尾

} MyCircularQueue;

MyCircularQueue* myCircularQueueCreate(int k) 
{
    MyCircularQueue* mq=(MyCircularQueue*)malloc(sizeof(MyCircularQueue));
    if(mq==NULL)
    {
        assert(0);
        return NULL;
    }
    mq->array=(int*)malloc(sizeof(int)*k);
    mq->capacity=k;
    mq->size=0;
    mq->front=0;
    mq->back=0;
    return mq;
}
bool myCircularQueueIsEmpty(MyCircularQueue* obj) 
{
    return obj->size==0;
}

bool myCircularQueueIsFull(MyCircularQueue* obj) 
{
    if(obj->size==obj->capacity)
    {
        return true;
    }
    return false;
}

//向循环队列插入元素,成功插入则返回真
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) 
{
    if(myCircularQueueIsFull(obj))//队列满无法插入函数
    {
        return false;
    }
    //元素存储到back的位置上
    obj->array[obj->back]=value;
    obj->back++;
    //插入元素后可能造成队列为满,则得将back放置到队列的起始位置
    if(obj->back==obj->capacity)
    obj->back=0;

    obj->size++;
    return true;
}
//删除元素,成功删除返回真
bool myCircularQueueDeQueue(MyCircularQueue* obj) 
{
    //队列判空,为空则无法删除元素
    if(myCircularQueueIsEmpty(obj))
    {
        return false;
    }
    //队列不为空,则进行队列元素删除操作
    obj->front++;
    //front移动到队列末尾时,将front置为空
    if(obj->front==obj->capacity)
    {
        obj->front=0;
    }
    obj->size--;
    return true;
}
//获取对头元素,队列为空返回-1
int myCircularQueueFront(MyCircularQueue* obj) 
{
    if(myCircularQueueIsEmpty(obj))
    {
        return -1;
    }
    return obj->array[obj->front];
}
//获取队尾元素,队列为空返回-1
int myCircularQueueRear(MyCircularQueue* obj) 
{
    if(myCircularQueueIsEmpty(obj))
    {
        return -1;
    }
    return obj->array[(obj->back-1+obj->capacity)%obj->capacity];
}

void myCircularQueueFree(MyCircularQueue* obj) 
{
    free(obj->array);
    free(obj);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值