目录
前言:
本篇博客是写完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);
}
以上就是用链表实现一个循环队列的代码和思路。
(完)