循环队列的实现

目录

一、什么是循环队列

二、链表实现循环队列

2.1 单链表实现

2.2 双链表实现

三、数组实现循环队列

3.1 思路分析

3.1.1 如何判空判满

3.1.2 如何处理回环问题

3.1.3 如何取出尾部元素

3.2 开辟空间法实现

3.3 创建size变量实现


一、什么是循环队列

        循环队列是一种数据结构,它与普通队列类似,但是在循环队列中,当队列满时,新的元素可以覆盖队列前面的元素。循环队列通常使用数组来实现,在队列尾部插入元素,在队列头部删除元素,并且可以循环利用数组空间,使得队列的操作更加高效。循环队列可以解决普通队列需要频繁的元素搬移的问题,提高了队列的操作效率。

        LeetCode原题链接:. 622.- 力扣(LeetCode)

二、链表实现循环队列

2.1 单链表实现

        通过增加了一个limit变量实现,仅仅实现了逻辑上的循环队列,没有实现物理结构上的循环,该方法即使可以通过LeetCode判题,但不是推荐解法。

typedef struct {
    struct ListNode *head;
    struct ListNode *tail;
    int limit;    //限制容量
    int size;     //实际容量
} MyCircularQueue;

MyCircularQueue* myCircularQueueCreate(int k) 
{
    MyCircularQueue *obj = (MyCircularQueue *)malloc(sizeof(MyCircularQueue));
    obj->limit = k;
    obj->size = 0;
    obj->head = obj->tail = NULL;
    return obj;
}
//入队
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) 
{
    if (obj->size >= obj->capacity) 
    {
        return false;
    }
    struct ListNode *node = (struct ListNode *)malloc(sizeof(struct ListNode));
    if(node==NULL)
    {
        perror("malloc");
        exit(1);
    }
    node->val = value;
    node->next = NULL;
    //头插
    if (!obj->head) 
    {
        obj->head = obj->tail = node;
    } 
    else 
    {
        obj->tail->next = node;
        obj->tail = node;
    }
    obj->size++;
    return true;
}
//出队
bool myCircularQueueDeQueue(MyCircularQueue* obj) {
    if (obj->size == 0) {
        return false;
    }
    struct ListNode *node = obj->head;
    obj->head = obj->head->next;  
    obj->size--;
    free(node);
    return true;
}
//返回队首元素
int myCircularQueueFront(MyCircularQueue* obj) {
    if (obj->size == 0) {
        return -1;
    }
    return obj->head->val;
}
//返回队尾元素
int myCircularQueueRear(MyCircularQueue* obj) {
    if (obj->size == 0) {
        return -1;
    }
    return obj->tail->val;
}
//判空
bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
    return obj->size == 0;
}
//判满
bool myCircularQueueIsFull(MyCircularQueue* obj) {
    return obj->size == obj->limit;
}
//销毁
void myCircularQueueFree(MyCircularQueue* obj) {
    for (struct ListNode *temp = obj->head; temp;) {
        struct ListNode *newnode = temp;
        temp = temp->next;
        free(newnode);
    }
    free(obj);
}

2.2 双链表实现

        使用定长无头双向链表,实现循环队列

typedef struct ListNode
{
    int data;
    struct ListNode* next; // 指针保存下⼀个节点的地址
    struct ListNode* prev; // 指针保存前⼀个节点的地址
}ListNode;

typedef struct {
    ListNode* phead;   //指向队首元素
    ListNode* ptail;   //指向队尾元素的下一个位置
    int k;
    int size;
} MyCircularQueue;

//重复开辟空间操作单独封装
ListNode* ListBuyNode(int x)
{
    ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));
    if (newnode == NULL)
    {
        perror("malloc");
        exit(1);
    }
    newnode->data = x;
    newnode->next = newnode;
    newnode->prev = newnode;

    return newnode;
}
//尾插
void ListPushBack(ListNode* head)
{
    assert(head);
    ListNode* newnode = ListBuyNode(0);
    newnode->prev = head->prev;
    newnode->next = head;

    head->prev->next = newnode;
    head->prev = newnode;
}
//创建物理上的循环队列
MyCircularQueue* myCircularQueueCreate(int k)
{
    MyCircularQueue* obj = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
    if (obj == NULL)
    {
        perror("malloc");
        exit(1);
    }
    obj->phead = obj->ptail = ListBuyNode(0);
    obj->size = 0;
    obj->k = k;
    while (k-1)
    {
        ListPushBack(obj->phead);
        k--;
    }
    obj->ptail = obj->phead;
    return obj;
}
//判空
bool myCircularQueueIsEmpty(MyCircularQueue* obj) 
{
    assert(obj);
    return obj->size == 0;
}
//判满
bool myCircularQueueIsFull(MyCircularQueue* obj) 
{
    assert(obj);
    return obj->size == obj->k;
}
//入队
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) 
{
    assert(obj);
    if (myCircularQueueIsFull(obj))
    {
        return false;
    }
    obj->ptail->data = value;
    obj->ptail = obj->ptail->next;
    obj->size++;
    return true;
}
//出队
bool myCircularQueueDeQueue(MyCircularQueue* obj) 
{
    assert(obj);
    if (myCircularQueueIsEmpty(obj))
    {
        return false;
    }
    obj->phead = obj->phead->next;
    obj->size--;
    return true;
}
//返回队首元素
int myCircularQueueFront(MyCircularQueue* obj) 
{
    assert(obj);
    if (myCircularQueueIsEmpty(obj))
    {
        return -1;
    }
    return obj->phead->data;
}
//返回队尾元素
int myCircularQueueRear(MyCircularQueue* obj) 
{
    assert(obj);
    if (myCircularQueueIsEmpty(obj))
    {
        return -1;
    }
    return obj->ptail->prev->data;
}
//销毁队列
void myCircularQueueFree(MyCircularQueue* obj) 
{
    assert(obj);
    ListNode* temp = NULL;
    while (obj->size)
    {
        temp = obj->phead;
        obj->phead = obj->phead->next;
        free(temp);
        temp = NULL;
        obj->size--;
    }
    obj->phead = obj->ptail = NULL;
    free(obj);
}

三、数组实现循环队列

3.1 思路分析

3.1.1 如何判空判满

        看到这个问题可能最直接的思路就是,当头指针与尾指针重合时,链表为满。但是通过图示不难发现,空和满的最后的结果都是一样的:头指针等于尾指针。

        对于这个问题有以下两种解决方式:

  • 在结构体中设置size变量:最直接且最有效的方式
  • 额外开辟一个空间:将空和满的情况区分开,tail+1 = head,但是仍然涉及到了回环问题,见下文解释。

3.1.2 如何处理回环问题

tail指向最后一个元素,指向下一个元素时如何将tail的值变为从0开始?

  • 若设计的空间等于有效空间个数:采用取模的做法。因为:任何小于被操作数的取模都等于它自身,等于被操作数的取模等于0。只需取空间的大小,便完美解决了该问题。
  • 若设计的空间比有效空间的个数多一个:可以判断tail所指的元素是否为0,为0则tail置为0

同理:head也可以这样处理

3.1.3 如何取出尾部元素

        如果是正常的情况(物理结构上头指针在尾指针的前面),无需考虑这个问题;主要考虑特殊情况(物理结构上头指针在尾指针的后面)。本质上讨论的依然是回环的问题。

  • 既可以采用if判断tail是否为0
  • 也可以采用取模的办法

3.2 开辟空间法实现

typedef struct {
    int head;   //指向首元素
    int tail;   //指向尾元素的下一个位置
    int k;
    int* a;
} MyCircularQueue;

MyCircularQueue* myCircularQueueCreate(int k) 
{
    MyCircularQueue* obj = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
    if (obj == NULL)
    {
        perror("malloc");
        exit(1);
    }
    obj->a = (int*)malloc((k + 1) * sizeof(int));
    if (obj->a == NULL)
    {
        perror("malloc");
        exit(1);
    }
    obj->k = k;
    obj->head = 0;
    obj->tail = 0;
    return obj;
}

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

bool myCircularQueueIsFull(MyCircularQueue* obj) {
    return (obj->tail + 1) % (obj->k + 1) == obj->head;
}

bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
    if (myCircularQueueIsFull(obj)) 
    {
        return false;
    }
    obj->a[obj->tail] = value;
    obj->tail++;
    //判断是否绕环
    obj->tail %= (obj->k + 1);
    return true;
}

bool myCircularQueueDeQueue(MyCircularQueue* obj) {
    if (myCircularQueueIsEmpty(obj)) 
    {
        return false;
    }
    obj->head++;
    //判断是否绕环
    obj->head %= (obj->k + 1);
    return true;
}

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

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

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

3.3 创建size变量实现

typedef struct {
    int head;   //指向首元素
    int tail;   //指向尾元素的下一个位置
    int k;
    int* a;
    int size;
} MyCircularQueue;

MyCircularQueue* myCircularQueueCreate(int k)
{
    MyCircularQueue* obj = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
    if (obj == NULL)
    {
        perror("malloc");
        exit(1);
    }
    obj->a = (int*)malloc(k * sizeof(int));
    if (obj->a == NULL)
    {
        perror("malloc");
        exit(1);
    }
    obj->k = k;
    obj->head = 0;
    obj->tail = 0;
    obj->size = 0;
    return obj;
}

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

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

bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
    if (myCircularQueueIsFull(obj))
    {
        return false;
    }
    obj->a[obj->tail] = value;
    obj->tail++;
    //判断是否绕环
    obj->tail %= (obj->k);
    return true;
}

bool myCircularQueueDeQueue(MyCircularQueue* obj) {
    if (myCircularQueueIsEmpty(obj))
    {
        return false;
    }
    obj->head++;
    //判断是否绕环
    obj->head %= (obj->k);
    return true;
}

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

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

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

  • 17
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值