设计循环队列

目录

  • 题目
    • 题目要求
    • 示例
  • 解答
    • 方法一、
      • 实现思路
      • 时间复杂度和空间复杂度
      • 代码
    • 方法二、
      • 实现思路
      • 时间复杂度和空间复杂度
      • 代码

题目

设计循环队列

题目要求

设计循环队列_第1张图片
题目链接

示例

解答

方法一、

使用数组来实现循环队列

实现思路

在设计循环队列中我们不能很好的判断队列是否满或空,即此时队列满或空时的状态都是head等于tail。此时有有两种解决方案。
1、增加一个size记录数据个数,同时可以用来区分队列满和空。
2、多开一个空间,不存储数据。
而我们要用数组实现的循环队列就采用了第二种方法,多开辟了一个数组元素的空间,该元素不存储数据,只是用来判断队列的状态。
设计循环队列_第2张图片
使用数组来实现循环队列,设置两个int型变量head和tail用来记录数组中队列的队头和队尾,在申请数组空间时,申请k+1个空间,因为需要多出来一个空间不存储数据,用来判断队列的状态。
设计循环队列_第3张图片
然后接下来就是实现队列的入队操作和出队操作,需要注意的是在遇到边界时需要特殊处理。
在该情景时入队需要特别处理tail。此时数组中下标为tail的空间存储数据后,tail++为5,已经超过了数组长度,所以此时要判断,当tail == k + 1时,要重置tail,即让tail为0。
设计循环队列_第4张图片
当该情景时判断队列满需要特别处理,此时使用next = tail + 1,用next = head来判断队列是否为满已经不起作用了,所以要判断当next = k + 1时,将next重置为0,然后与head比较来判断队列是否满。
设计循环队列_第5张图片
当为此情景时出队需要特别处理,此时head++后为5,已经超过数组长度,所以要判断当head++后,如果head = k+1,就要重置head为0。
设计循环队列_第6张图片

时间复杂度和空间复杂度

时间复杂度:O(1),因为使用了数组来实现,而数组支持随机访问,所以为O(1)
空间复杂度:O(k),因为循环队列的长度为k,所以要申请k+1个数组元素的空间。

代码


typedef struct {
    //使用数组来实现循环队列
    int *data;
    int k;
    //head存队列中队头的下标
    int head;
    //tail存队列中队尾的下标
    int tail;

} MyCircularQueue;

bool myCircularQueueIsEmpty(MyCircularQueue* obj);
bool myCircularQueueIsFull(MyCircularQueue* obj);
MyCircularQueue* myCircularQueueCreate(int k) {
    //申请创建一个MyCircularQueue*类型的结构体指针,即代表创建了一个循环队列
    MyCircularQueue* obj = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
    assert(obj);
    //将该队列中用来放数据的数组申请好空间。
    //队列长度为k,但是我们申请了k+1个空间,因为有一个空间是不用的,以此来判断循环队列是否为空或已满
    int* tmp = (int*)malloc(sizeof(int)*(k+1));
    assert(tmp);
    obj->data = tmp;
    obj->k=k;
    obj->head=0;
    obj->tail=0;

    return obj;
}

bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
    assert(obj);
    //向循环队列中插入数据即为向队列的队尾后插入数据,在插入数据之前我们要先判断循环队列是否已满
    if(!myCircularQueueIsFull(obj))
    {
        obj->data[obj->tail] = value;
        obj->tail++;
        if(obj->tail == (obj->k)+1)
        {
            obj->tail=0;
        }
        return true;
    }
    else
    {
        return false;
    }
}

bool myCircularQueueDeQueue(MyCircularQueue* obj) {
    assert(obj);
    //删除循环队列中的数据就是删除队头的数据,在删除时我们要先判断循环队列是否为空
    if(!myCircularQueueIsEmpty(obj))
    {
        obj->head++;
        if(obj->head == (obj->k)+1)
        {
            obj->head=0;
        }
        return true;
    }
    else
    {
        return false;
    }
}

int myCircularQueueFront(MyCircularQueue* obj) {
    if(!myCircularQueueIsEmpty(obj))
    {
        return obj->data[obj->head];
    }
    else
    {
        return -1;
    }
}

int myCircularQueueRear(MyCircularQueue* obj) {
    if(!myCircularQueueIsEmpty(obj))
    {
        int prev = obj->tail-1;
        if(obj->tail==0)
        {
            prev = obj->k;
        }
        return obj->data[prev];
    }
    else
    {
        return -1;
    }
}

bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
    if(obj->head==obj->tail)
    {
        return true;
    }
    else
    {
        return false;
    }
}

bool myCircularQueueIsFull(MyCircularQueue* obj) {
    int next = (obj->tail)+1;
    if(next==(obj->k)+1)
    {
        next = 0;
    }
    return next==obj->head;
}

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

方法二、

用链表实现

实现思路

使用链表实现循环队列时,可以用一个head指针来指向队列的队头,一个tailPrev指针来指向队列的队尾结点,再使用tail指针来指向下一个要存储入队的数据的结点。这样当需要返回队头元素时就将head指针指向的结点的数据返回,要返回队尾结点的数据时就将tailPrev指针指向的结点的数据返回。因为有三个指针指向不同结点,此时判断队列空或满很麻烦,所以我们直接多加了size来记录队列的长度,当size为0则队列为空,当size=k,代表队列已满。
设计循环队列_第7张图片
当有元素入队时,就将数据存在tail指针指向的结点,然后将tailPrev指针指向tail结点,以保证tailPrev指针指向的是队尾结点,而tail指针指向下一个结点。
设计循环队列_第8张图片
当有元素出队时,也不用释放出队元素的结点所占用的空间,直接将head指针指向下一个结点,另下一个结点作为队头结点即可。
设计循环队列_第9张图片
判断队列空或满直接使用size判断即可。当销毁该队列时,需要从head指针指向的队头结点开始,依次将后面的k个结点的空间都释放,然后再将obj结点的空间释放。

时间复杂度和空间复杂度

时间复杂度:O(1)
空间复杂度:O(k)

代码

ypedef struct SNode
{
    int data;
    struct SNode* next;
}SNode;

typedef struct {
    SNode* head;
    SNode* tail;
    SNode* tailPrev;
    int size;
    int k;
} MyCircularQueue;

bool myCircularQueueIsEmpty(MyCircularQueue* obj);
bool myCircularQueueIsFull(MyCircularQueue* obj);

MyCircularQueue* myCircularQueueCreate(int k) {
    MyCircularQueue* obj = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
    SNode* curr = (SNode*)malloc(sizeof(SNode));
    obj->head=curr;
    obj->tail=curr;
    obj->tailPrev=NULL;
    obj->size=0;
    obj->k = k;
    k--;
    while(k>0)
    {
        SNode* newNode = (SNode*)malloc(sizeof(SNode));
        curr->next=newNode;
        curr=curr->next;
        k--;
    }
    curr->next=obj->head;
    return obj;
}

bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
    if(myCircularQueueIsFull(obj))
    {
        return false;
    }
    else
    {
        obj->tail->data=value;
        obj->tailPrev=obj->tail;
        obj->tail=obj->tail->next;
        obj->size++;
        return true;
    }
}

bool myCircularQueueDeQueue(MyCircularQueue* obj) {
    if(myCircularQueueIsEmpty(obj))
    {
        return false;
    }
    else
    {
        obj->head=obj->head->next;
        obj->size--;
        return true;
    }
}

int myCircularQueueFront(MyCircularQueue* obj) {
    if(myCircularQueueIsEmpty(obj))
    {
        return -1;
    }
    else
    {
        return obj->head->data;
    }
}

int myCircularQueueRear(MyCircularQueue* obj) {
    if(myCircularQueueIsEmpty(obj))
    {
        return -1;
    }
    else
    {
        return obj->tailPrev->data;
    }
}

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

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

void myCircularQueueFree(MyCircularQueue* obj) {
    int n = obj->k;
    while(n)
    {
        SNode* del = obj->head;
        obj->head=obj->head->next;
        free(del);
        n--;
    }
    free(obj);
}

你可能感兴趣的:(刷题,c语言,笔记)