数据结构—设计循环队列C语言实现

LeetCode题目链接:设计循环队列

题目描述:

 

思路:

1、因为队列是循环的,所以可以用循环链表实现,让链表尾指向链表头即可,但是会有一个问题就是,插入数据的时候,尾插找队列尾的时候不方便,记录队列尾的指针永远指向的都是队列尾的下一个位置(下面会说原因),就需要用循环来找尾

2、可以用数组来实现,数组在内存中是连续的空间,可以直接用两个数组的下标来控制队列的实现,数组实现的弊端就是在循环的时候数组边界需要特殊考虑,但是可以多加个判断条件来判断边界条件即可,相对于用链表实现更优,所以循环队列大多都用数组实现

实现:

1、定义结构体

定义两个数组下标,front指向队列的头,back指向队尾的下一个位置,原因:back最开始只能指向数组的第一个位置,也就是下标为0的位置,插入一个元素之后,back向后移一个位置,如果back指向的就是队尾的最后一个元素,那最开始队列为空的时候back指向哪里呢?所以就矛盾了(用链表实现也是同理)

typedef struct {
    int* a;
    int front;
    int back;
    int k;

} MyCircularQueue;

2、构造/初始化循环队列

首先需要动态开辟一个之前自己定义实现循环队列的结构体,再动态开辟一个数组,这里需要多开辟一个数组空间

多开一个数组空间的原因如下图所示:

多开一个数组空间就是为了判断队列满的时候避免与判断队列空的时候混淆,多开的一个数组空间可以相当于是整个数组中的任何位置,不一定是最后一个位置,随着多次的插入和删除数据,多出来的一个数组空间也会随着改变

MyCircularQueue* myCircularQueueCreate(int k) {

    MyCircularQueue* obj = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
    assert(obj);
    obj->a = (int*)malloc(sizeof(int) * (k + 1));
    obj->front = 0;
    obj->back = 0;
    obj->k = k;

    return obj;
}

3、向循环队列插入一个元素

插入一个元素首先要判断队列是否满了(这里判断可以直接调用后面实现的检查队列是否已满的函数直接判断)。队列没有满再进行插入操作:将所要插入的元素放到数组下标为back的位置处,然后back++指向下一个位置,这里就要考虑边界问题了,因为每次插入一个元素之后,back都要向后走一个位置,若back在插入之前就指向的是数组的最后一个位置处(也就是下标为k指向的位置),则插入一个元素之后,back就要指向数组的第一个位置,这样就相当于循环起来了

bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
    assert(obj);
    if(myCircularQueueIsFull(&obj->a))
    {
        return false;
    }
    obj->a[obj->back] = value;
    if(obj->back == obj->k)
    {
        obj->back = 0;
    }
    else
    {
        obj->back++;
    }
    return true;
}

4、从循环队列中删除一个元素

删除队列的元素首先要判断队列是否为空(这里的判断使用后面实现的检查队列是否为空的函数直接进行判断)。队列不为空再进行删除队列元素操作:因为下标front永远都指向队列的头,删除元素也就是出队列是从队列的头出的,所以在数组实现中直接将下标front++后指向下一个位置,就相当于删除了队列的头元素,这里也就要多一个判断来考虑边界问题,当front指向最后一个位置时也就是下标为k位置时,front向后走一个位置就到了下标为0位置处

bool myCircularQueueDeQueue(MyCircularQueue* obj) {
    assert(obj);

    if(myCircularQueueIsEmpty(&obj->a))
    {
        return false;
    }
    if(obj->front == obj->k)
    {
        obj->front = 0;
    }
    else
    {
        obj->front++;
    }
    return true;

}

5、从队首获取元素

首先也需要判断队列是否为空(使用下面实现的检查队列是否为空的函数进行判断)。不为空:数组下标front永远指向队列的首元素,所以直接返回数组下标为front位置处的元素即可

int myCircularQueueFront(MyCircularQueue* obj) {
    assert(obj);
    if(myCircularQueueIsEmpty(&obj->a))
    {
        return -1;
    }
    return obj->a[obj->front];
}

6、获取队尾元素

获取队列元素就需要判断队列是否为空(调用下面实现的检查队列是否为空的函数进行判断)。因为back永远指向队列尾的下一个位置处,所以返回队尾元素时,需要返回下标为back-1位置处的元素,边界情况就是back指向数组0位置时,就需要返回它的前一个位置,也就是数组的最后一个位置(下标为k位置)对应的元素

int myCircularQueueRear(MyCircularQueue* obj) {
    assert(obj);
    if(myCircularQueueIsEmpty(&obj->a))
    {
        return -1;
    }
    if(obj->back == 0)
    {
        return obj->a[obj->k];
    }
    return obj->a[obj->back-1];
}

7、检查循环队列是否为空

front和back相等指向同一个位置时为空

bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
    assert(obj);
    return obj->front == obj->back;
}

8、检查循环队列是否已满

back的下一个位置是front时队列为满,特殊情况:同时满足front为0并且back指向数组最后一个位置(也就是k位置处)则队列为满

bool myCircularQueueIsFull(MyCircularQueue* obj) {
    assert(obj);
    if(obj->front == 0 && obj->back == obj->k)
    {
        return true;
    }
    else
    {
        return obj->front == obj->back + 1;
    }
}

9、释放空间

free动态开辟的实现循环队列的数组,再free动态开辟的循环队列结构体

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

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值