循环队列图解

设计循环队列

622. 设计循环队列

1.思路

使用数组存储元素,使用front和back指针实现循环,k用来记录元素的个数

循环队列图解_第1张图片

这是队列开始时,两指针相等,代表队列为空,但是随后会出现一个问题,导致两指针相等无法准确判断。

循环队列图解_第2张图片

所以当要创建容量为k的循环队列时,开容量为k+1大小的数组,循环队列始终有一个空位,用来判断队列是否是满队。此时队列的状态是满的,back指向的位置是不存放元素的,或者元素是无效的。back+1==front很好理解,就是back的后面是front,back+1%k+1就是起到一个循环的作用。

循环队列图解_第3张图片

删除一个元素,就是将front指针向后移动

循环队列图解_第4张图片

继续添加一个元素,back指针向后移动,但是发现back指针走到了数组之外,所以需要将back+1后的值模上k+1在赋值给back,这样才能起到循环的效果。
back指向6 back+1=7 k=6 k+1=7 7%7=0 back就循环到了0,即使位置0上有元素也是无关紧要的,因为只会访问到back-1的位置。

循环队列图解_第5张图片

继续删除一个元素并添加一个元素

循环队列图解_第6张图片

随后删除四个元素,front指向下标为6的位置,要继续删除时,要像back循环一样。front+1后取模k+1赋值给front,front就会循环到0。

循环队列图解_第7张图片

循环队列图解_第8张图片

在删最后一个,将front指向front+1,此时front==back,队列元素全部出队,再次入队会覆盖之前出队的元素。

循环队列图解_第9张图片

2结构设计以及接口

typedef struct {
    int *a;               	
    int front;
    int back;
    int k;
} MyCircularQueue;
MyCircularQueue* myCircularQueueCreate(int k);//创建循环队列,返回循环队列指针,k为元素个数。
bool myCircularQueueIsEmpty(MyCircularQueue* obj);//判断循环队列是否还有未出队元素。
bool myCircularQueueIsFull(MyCircularQueue* obj);//判断循环队列是否是满队。
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value);//向循环队列插入一个元素。如果成功插入则返回真。
bool myCircularQueueDeQueue(MyCircularQueue* obj);//从循环队列中删除一个元素。如果成功删除则返回真。
int myCircularQueueFront(MyCircularQueue* obj);//从队首获取元素。如果队列为空,返回 -1 。
int myCircularQueueRear(MyCircularQueue* obj);//获取队尾元素。如果队列为空,返回 -1 。   

3接口的实现

1.MyCircularQueue(k)

因为要流出一个空位来判断是否是满队,所以在申请数组内存的时候要多申请一个。由于obj要返回给外部,所以要使用malloc在堆上开辟,栈的空间在函数结束会收回。

MyCircularQueue* myCircularQueueCreate(int k) {
    MyCircularQueue* obj = malloc(sizeof(MyCircularQueue));
    obj->a=malloc(4*(k+1));
    obj->k=k;
    obj->front=0;
    obj->back=0;
    return obj;
}
2.Front

如果队列为空就返回-1,否则返回队头元素。

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

如果队列为空就返回-1,否则返回队尾元素。front指针是指向第一个元素的指针,但是back指针是指向最后一个元素的下一个位置,那么直接返回a[back-1]也是不可行的,因为这时back为0,-1会导致下标越界。可以写判断返回a[k]或a[back-1]。也可以统一成a[back-1+k+1%k+1]

循环队列图解_第10张图片

int myCircularQueueRear(MyCircularQueue* obj) {
      if(myCircularQueueIsEmpty(obj))
      return -1;
       return obj->a[(obj->back+obj->k)%(obj->k+1)];
}

4.enQueue(value)

添加一个元素,如果满了就返回false。每次back+1后要判断是否可以循环回去。

bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
    if(myCircularQueueIsFull(obj))return false;
    obj->a[obj->back++]=value;
    obj->back%=(obj->k+1);
    return true;
}
5.deQueue()

删除也要判断front是否需要循环

bool myCircularQueueDeQueue(MyCircularQueue* obj) {
    if(myCircularQueueIsEmpty(obj))return false;
    obj->front++;
    obj->front%=(obj->k+1);
    return true;
}

6.isEmpty()

队列为空就是两指针指向同一下标

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

满的条件就是back+1%k+1=front

bool myCircularQueueIsFull(MyCircularQueue* obj) {
    return obj->front==(obj->back+1)%(obj->k+1);
}
8.free()
void myCircularQueueFree(MyCircularQueue* obj) {
    free(obj->a);
    obj->a = NULL;
    free(obj);
}

你可能感兴趣的:(数据结构,刷题,leetcode,c语言,数据结构,算法)