C语言:设计循环队列

目录

前言:

顺序表代码实现循环队列:

   代码思路: 

        1、创建一个顺序表

        2、 创建一个循环队列

        3、判断

        4、插入

        5、删除

        6、取数据

        7、销毁循环队列

链表代码实现循环队列:

     代码思路:

        1、创建节点和链表结构

        2、创建一个循环队列

        3、判断

        4、插入

        5、删除

        6、取数据

         7、销毁循环队列


前言:

        本篇博客是写完LeetCode_662.设计循环队列之后有感而发,于是写下了一些思路和总结。

本题用了两种方法来解答——顺序表和链表。

顺序表代码实现循环队列:

//创建顺序表
typedef struct 
{
    int* a;  //顺序表
    int head;//顺序表的头
    int tail;//顺序表的尾
    int k;   //能存放数据的个数
} MyCircularQueue;
//判断循环队列是否为空
bool myCircularQueueIsEmpty(MyCircularQueue* obj) 
{
    //当尾的位置与头的位置相同时,说明循环队列已经空了                    
    return obj->head == obj->tail;
}
//判断循环队列是否为满
bool myCircularQueueIsFull(MyCircularQueue* obj) 
{
    //当尾的位置与头的位置相差一的时候,说明循环队列已经满了
    return (obj->tail + 1) % (obj->k + 1) == obj->head;
}
//创建循环队列
MyCircularQueue* myCircularQueueCreate(int k) 
{
    MyCircularQueue* newnode = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
    //初始化顺序表
    //创建比要存放的数据个数多一个,便于以后的判断
    newnode->a = (int*)malloc(sizeof(int) * (k + 1));
    newnode->head = 0;
    newnode->tail = 0;
    newnode->k = k;

    return newnode;
}

bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) 
{
    //当循环队列满的时候不能插入
    if(myCircularQueueIsFull(obj))
        return false;
    else
    {
        //当插入一个数据的时候,尾向后移动一位
        obj->a[obj->tail++] = value;
        //确保可以循环
        obj->tail %= obj->k + 1;
        return true;
    }
}
//删除循环队列的数据
bool myCircularQueueDeQueue(MyCircularQueue* obj) 
{
    //当循环队列满的时候不能插入
    if(myCircularQueueIsEmpty(obj))
        return false;
    else
    {
        //当删除一个数据的时候,头向后移动一位
        obj->head++;
        //确保可以循环
        obj->head %= obj->k + 1;
        return true;
    }
}
//取循环队列的头的数据
int myCircularQueueFront(MyCircularQueue* obj) 
{
    //当循环队列不为空的时候,取头的数据
    if(myCircularQueueIsEmpty(obj))
    {
        return -1;
    }
    else
    {
        return obj->a[obj->head];
    }
}
//取循环队列的尾的数据
int myCircularQueueRear(MyCircularQueue* obj) 
{
    //当循环队列不为空的时候,取尾的数据
    if(myCircularQueueIsEmpty(obj))
    {
        return -1;
    }
    else
    {
        //原来的式子 obj->a[(obj->tail - 1 + obj->k + 1) % (obj->k + 1)];
        return obj->a[(obj->tail + obj->k) % (obj->k + 1)];
    }
}
//销毁循环队列
void myCircularQueueFree(MyCircularQueue* obj) 
{
    free(obj->a);
    free(obj);
}

   代码思路: 

        我们要以顺序表来设计一个循环队列。首先,创建一个顺序表,其中的head代表在数组中的tail代表k代表可以插入的数据个数。每插入一个数据,尾的位置向后移一位,当尾的位置下一位是头的位置的时候,循环队列就满了。每删除一个数据,头的位置向后移一位,当头的位置和尾的位置一样的时候,循环队列就空了。


        1、创建一个顺序表

        顺序表的结构如下:

//创建顺序表
typedef struct 
{
    int* a;  //顺序表
    int head;//顺序表的头
    int tail;//顺序表的尾
    int k;   //能存放数据的个数
} MyCircularQueue;

        2、 创建一个循环队列

        注意:需要malloc一个变量用来返回,不能直接创建一个循环队列,因为不在堆上,当出了作用域空间会被回收。且创建数组的时候要多一块空间便于我们判断循环队列是否满了。

//创建循环队列
MyCircularQueue* myCircularQueueCreate(int k) 
{
    MyCircularQueue* newnode = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
    //初始化顺序表
    //创建比要存放的数据个数多一个,便于以后的判断
    newnode->a = (int*)malloc(sizeof(int) * (k + 1));
    newnode->head = 0;
    newnode->tail = 0;
    newnode->k = k;

    return newnode;
}

        3、判断

        当尾的位置与头的位置相同时,说明循环队列已经空了。

        当尾的后一位就是头的位置时,说明循环队列已经满了。但会出现这种情况,tail的下一位会越界,所以用tail+1 %(k +1) == head ,当它们相等的时候,队列就满了。

//判断循环队列是否为空
bool myCircularQueueIsEmpty(MyCircularQueue* obj) 
{
    //当尾的位置与头的位置相同时,说明循环队列已经空了                    
    return obj->head == obj->tail;
}
//判断循环队列是否为满
bool myCircularQueueIsFull(MyCircularQueue* obj) 
{
    //当尾的位置与头的位置相差一的时候,说明循环队列已经满了
    return (obj->tail + 1) % (obj->k + 1) == obj->head;
}

        4、插入

        首先,我们要判断循环队列是否满了,如果满了就返回false,反之,插入数据。当插入数据的时候,tail++

        注意:每次tail++完之后,记得要确保tail没有越界,如果越界了,就表明要返回到0的位置。

所以有tail %= k + 1。

bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) 
{
    //当循环队列满的时候不能插入
    if(myCircularQueueIsFull(obj))
        return false;
    else
    {
        //当插入一个数据的时候,尾向后移动一位
        obj->a[obj->tail++] = value;
        //确保可以循环
        obj->tail %= obj->k + 1;
        return true;
    }
}

        5、删除

        首先,我们要判断循环队列是否为空了,如果空了就返回false,反之,删除数据。当插入数据的时候,head++

        注意:每次head++完之后,记得要确保head没有越界,如果越界了,就表明要返回到0的位置。所以有head%= k + 1。

//删除循环队列的数据
bool myCircularQueueDeQueue(MyCircularQueue* obj) 
{
    //当循环队列满的时候不能插入
    if(myCircularQueueIsEmpty(obj))
        return false;
    else
    {
        //当删除一个数据的时候,头向后移动一位
        obj->head++;
        //确保可以循环
        obj->head %= obj->k + 1;
        return true;
    }
}

        6、取数据

        当循环队列不为空的时候,取头的数据。

        当循环队列不为空的时候,取尾的数据。注意:还有这种特殊的情况——tail的前一位在数组的末尾,就需要用(tail - 1 + k + 1) % (k + 1)

//取循环队列的头的数据
int myCircularQueueFront(MyCircularQueue* obj) 
{
    //当循环队列不为空的时候,取头的数据
    if(myCircularQueueIsEmpty(obj))
    {
        return -1;
    }
    else
    {
        return obj->a[obj->head];
    }
}
//取循环队列的尾的数据
int myCircularQueueRear(MyCircularQueue* obj) 
{
    //当循环队列不为空的时候,取尾的数据
    if(myCircularQueueIsEmpty(obj))
    {
        return -1;
    }
    else
    {
        //原来的式子 obj->a[(obj->tail - 1 + obj->k + 1) % (obj->k + 1)];
        return obj->a[(obj->tail + obj->k) % (obj->k + 1)];
    }
}

        7、销毁循环队列

        注意:不要只释放了循环队列,顺序也得释放,不然会导致内存泄漏。

//销毁循环队列
void myCircularQueueFree(MyCircularQueue* obj) 
{
    free(obj->a);
    free(obj);
}

 以上就是用顺序表来实现一个循环队列的代码。


链表代码实现循环队列:

struct List
{
    int _val;
    struct List* next;
};

typedef struct {
    struct List* head;
    struct List* tail;
    struct List* tail_prev;
} MyCircularQueue;

struct List* BuyNewNode()
{
    struct List* new = (struct List*)malloc(sizeof(struct List));
    new->_val = 0;
    new->next = NULL;
    return new;
}

MyCircularQueue* myCircularQueueCreate(int k) 
{
    MyCircularQueue* obj = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));

    obj->tail_prev = NULL;
    obj->head = obj->tail = BuyNewNode();

    for(int i = 0; i < k; i++)
    {
        struct List* new = BuyNewNode();

        obj->tail_prev = obj->tail;
        obj->tail = new;
        obj->tail_prev->next = obj->tail;
    }

    obj->tail->next = obj->head;

    obj->tail_prev = NULL;
    obj->tail = obj->head;

    return obj;
}

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

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

bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) 
{
    if(myCircularQueueIsFull(obj))
        return false;
    
    obj->tail->_val = value;
    obj->tail_prev = obj->tail;

    obj->tail = obj->tail->next;
    return true;
}

bool myCircularQueueDeQueue(MyCircularQueue* obj)
{
    if(myCircularQueueIsEmpty(obj))
        return false;

    obj->head = obj->head->next;
    return true;

}

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

int myCircularQueueRear(MyCircularQueue* obj) 
{
    if(!myCircularQueueIsEmpty(obj))
    {
        return obj->tail_prev->_val;
    }
    else
    {
        return -1;
    }
}

void myCircularQueueFree(MyCircularQueue* obj) 
{
    struct List* cur = obj->head;
    obj->tail->next = NULL;

    while(cur)
    {
        struct List* next = cur->next;
        free(cur);
        cur = next;
    }

    free(obj);
}

     代码思路:

        我们首先要自定义一个链表结构体节点,接着自定义一个结构体来存放结构体节点的头、尾尾的前一个节点,以便于后面获取头和尾节点的数据;记得要我们自己来将链表连成一个环(如下图所示);当你插入一个数据的时候,尾指针要向后移动一位;当你要删除数据的时候,头指针向后移动一位;最后,当头指针和尾指针在同一位置的时候说明此时循环队列为空,当尾指针的下一位是头指针的时候说明此时循环队列为满。


        1、创建节点和链表结构

        head是用来记录循环队列队首的数据tail_prev是用来记录循环队列队尾的数据tail是与head相比较来判断循环队列是否为空(满)。

//创建节点
struct List
{
    int _val;//存放数据
    struct List* next;
};

typedef struct {
    struct List* head;//创建头指针来获取头节点的数据
    struct List* tail;//创建尾指针的前一位指针来获取尾节点的数据
    struct List* tail_prev;//创建尾指针来确定循环队列是否为空(满)
} MyCircularQueue;

        2、创建一个循环队列

        要创建一个循环队列,首先我们要malloc一个结构体自变量来接收它;先创建一个节点,随后开始创建一个比实际存储个数多一的链表,然后将链表首位相连,最后重新初始化尾指针和尾指针的前一位指针。

//创建一个节点
struct List* BuyNewNode()
{
    struct List* new = (struct List*)malloc(sizeof(struct List));
    new->_val = 0;
    new->next = NULL;
    return new;
}
//创建一个循环队列
MyCircularQueue* myCircularQueueCreate(int k) 
{
    //创建一个结构体自变量,使其成为循环队列
    MyCircularQueue* obj = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
    //初始化链表
    obj->tail_prev = NULL;
    obj->head = obj->tail = BuyNewNode();
    //创建一个比要存放数据个数多一的链表
    for(int i = 0; i < k; i++)
    {
        struct List* new = BuyNewNode();

        obj->tail_prev = obj->tail;
        obj->tail = new;
        obj->tail_prev->next = obj->tail;
    }
    //将链表首尾相连
    obj->tail->next = obj->head;
    //将尾指针和尾指针的前一位指针重新初始化
    obj->tail_prev = NULL;
    obj->tail = obj->head;

    return obj;
}

        3、判断

        用链表实现循环队列就比较简单了。当头尾指针相等的时候,循环队列为空,当尾指针的下一位是头指针的时候,循环队列为满。

//当头尾指针相等的时候,循环队列为空
bool myCircularQueueIsEmpty(MyCircularQueue* obj) 
{
    return obj->head == obj->tail;
}
//当尾指针的下一位是头指针的时候,循环队列为满
bool myCircularQueueIsFull(MyCircularQueue* obj) 
{
    return obj->tail->next == obj->head;
}

        4、插入

        首先我们要判断循环队列是否为满,如果没有满就插入数据。成功插入数据到尾指针的成员上,首先尾指针的前一位指针要指向当前尾指针的地址,然后尾指针后移动一位。(顺序不能错)

//插入数据到循环队列
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) 
{
    //当循环队列满的时候,返回false
    if(myCircularQueueIsFull(obj))
        return false;
    //成功插入数据到尾指针的成员上,首先尾指针的前一位指针要指向当前尾指针的地址
    obj->tail->_val = value;
    obj->tail_prev = obj->tail;
    //然后尾指针后移动一位
    obj->tail = obj->tail->next;
    return true;
}

        5、删除

        首先判断一下循环队列是否为空,如果不为空,直接将头节点向后移动一位就可以了。

//删除数据到循环队列
bool myCircularQueueDeQueue(MyCircularQueue* obj)
{
    if(myCircularQueueIsEmpty(obj))
        return false;
    //直接将头节点向后移动一位
    obj->head = obj->head->next;
    return true;
}

        6、取数据

        因为我们有head指针和tail_prev指针,所以我们可以很方便的取到队首和队尾的数据。

//取队首的数据
int myCircularQueueFront(MyCircularQueue* obj) 
{
    if(!myCircularQueueIsEmpty(obj))
    {
    return obj->head->_val;
    }
    else
    {
        return -1;
    }
}
//取队尾的数据
int myCircularQueueRear(MyCircularQueue* obj) 
{
    if(!myCircularQueueIsEmpty(obj))
    {
        return obj->tail_prev->_val;
    }
    else
    {
        return -1;
    }
}

         7、销毁循环队列

        我们首先要销毁循环链表,然后再销毁循环队列。

//销毁循环队列
void myCircularQueueFree(MyCircularQueue* obj) 
{
    //先销毁链表
    struct List* cur = obj->head;
    obj->tail->next = NULL;

    while(cur)
    {
        struct List* next = cur->next;
        free(cur);
        cur = next;
    }
    //再销毁销毁队列
    free(obj);
}

以上就是用链表实现一个循环队列的代码和思路。

(完) 

  • 40
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
C语言中实现高效循环队列需要注意以下几点: 1. 循环队列的头尾指针需要特殊处理,当头指针和尾指针相遇时,需要进行特殊处理。 2. 循环队列的大小需要预先确定,一旦确定就不能改变。 3. 循环队列中最多只能容纳 (队列大小 - 1) 个元素,因为需要一个空位来判断队列是否为空或已满。 4. 循环队列的入队和出队操作都需要特殊处理,具体可以根据头尾指针的位置关系和队列大小进行判断。 以下是一个基于数组实现的高效循环队列的示例代码: ```c #define MAX_SIZE 100 typedef struct { int data[MAX_SIZE]; int head; int tail; } MyCircularQueue; MyCircularQueue* myCircularQueueCreate(int k) { MyCircularQueue *queue = (MyCircularQueue *)malloc(sizeof(MyCircularQueue)); queue->head = 0; queue->tail = 0; return queue; } bool myCircularQueueIsEmpty(MyCircularQueue* obj) { return (obj->head == obj->tail); } bool myCircularQueueIsFull(MyCircularQueue* obj) { return ((obj->tail + 1) % MAX_SIZE == obj->head); } bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) { if (myCircularQueueIsFull(obj)) { return false; } obj->data[obj->tail] = value; obj->tail = (obj->tail + 1) % MAX_SIZE; return true; } bool myCircularQueueDeQueue(MyCircularQueue* obj) { if (myCircularQueueIsEmpty(obj)) { return false; } obj->head = (obj->head + 1) % MAX_SIZE; return true; } int myCircularQueueFront(MyCircularQueue* obj) { if (myCircularQueueIsEmpty(obj)) { return -1; } return obj->data[obj->head]; } int myCircularQueueRear(MyCircularQueue* obj) { if (myCircularQueueIsEmpty(obj)) { return -1; } return obj->data[(obj->tail - 1 + MAX_SIZE) % MAX_SIZE]; } void myCircularQueueFree(MyCircularQueue* obj) { free(obj); } ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值