一、环形队列的各个接口的实现
1.初始化
MyCircularQueue* myCircularQueueCreate(SLTDataType k)//初始化 { MyCircularQueue* oq = (MyCircularQueue*)malloc(sizeof(MyCircularQueue)); //此时就是进行结构体中的成员变量的初始化 oq->a = (int*)malloc(sizeof(int) * (k + 1));//此时这个就是表示我要开辟多少个空间出来(根据上面的原理可以得到,这边开辟的空间是k+1个) //所以有了上面这个例子,下次就可以很好的理解空间需要开辟的大小了 oq->front = oq->tail = 0; oq->k = k;//这个就是存放数据的个数 return oq; }
2.判空
bool myCircularQueueIsEmpty(MyCircularQueue* obj)//判空 { return obj->front == obj->tail; }
3.判满
bool myCircularQueueIsFull(MyCircularQueue* obj)//判满 { return (obj->tail + 1) % (obj->k + 1) == obj->front; }
4.录入数据
bool myCircularQueueEnQueue(MyCircularQueue* obj, SLTDataType x)//录入数据 { if (myCircularQueueIsFull(obj)) { return false;//此时这个表示我的空间如果满了,就直接出程序,因为我的空间已经满了就不需要再录入数据了 } else { //不满,我就只要把数据存进去,然后个数加加就可以了 obj->a[obj->tail] = x;//这个的理解就是表示:在obj的tail这个位置存放进我的数据 obj->tail++; //存完一个数据就让我的tail指针向后走,指向新的空间, 然后如果我的tail此时走到了k+1(用于判空判满的这个位置时),此时我就要让我的tail回到front的位置(这样此时一个环形的队列) //如果不判断我们就写成下面这个写法(反正主要的目的就是让我的tail回到我的front的位置上) obj->tail %= (obj->k + 1); } return true; }
5.删除数据
bool myCircularQueueDeQueue(MyCircularQueue* obj)//删除数据 { if (myCircularQueueIsFull(obj)) { return false; } else { //队列实现删除操作就是非常的抽象,只要front指针加加就行了(因为我一定要满足队列的基本原则,先进先出),所以只要加加就行,最后加到k+1的位置时,此时再回到最头上的位置(循环往复的走) obj->front++; obj->front %= (obj->k + 1);//这步就是让我的front回到最头上的那个空间 } return true; }
6.取队头数据
int myCircularQueueFront(MyCircularQueue* obj)//取队头的数据 { if (myCircularQueueIsFull(obj)) { return -1; } return obj->a[obj->front];//此时这个位置就是表示我的队头的位置 }
7.取队尾数据
int myCircularQueueRear(MyCircularQueue* obj)//取队尾的数据 { if (myCircularQueueIsFull(obj)) { return -1; } /* if (obj->tail == 0) { return obj->a[k]; //放回队尾,只是条件不同而已 } else { return obj->a[obj->tail - 1];//放回队尾,只是条件不同而已 } */ //此时可以不按以上的判断来写,按照下面这样写也是一样的 int i = (obj->tail + obj->k) % (obj->k + 1);//这个就是在硬算,就像是算菱形的那个次数一样,硬算出来的而已(目的还是为了让我的tail回到原来的位置,并且刚好可以把此时队尾的数据给返回去) return obj->a[i];//返回队尾的数据而已 }
8.销毁
void myCircularQueueFree(MyCircularQueue* obj)//销毁 { //注意此时有两层结构,所以要进行两层的free free(obj->a); free(obj); }
9.整体代码如下:
#include<stdio.h> #include<stdlib.h> #include<assert.h> #include<stdbool.h> //数据结构刷题从现在开始:(不管你是在学什么,有空的时候一定要自己去练几个题目) //1.题目:设计你的循环队列实现。循环队列是一种线性数据结构,其操作表现给予先进先出原则并且队尾被链接在队首之后以形成一个循环。它也被称为“环形缓冲器” //循环队列的一个试除是我们可以利用这个队列之前用过的空间。在一个普通的队列里,一但一个队列满了,我们就不能插入下一个元素,及时在队列前面还有空间。但是使用循环队列,我们就能使用这些空间去存储新的值 //你的设计应该支持以下的操作: //构造器,设置队列的长度为k //从队首获取元素。如果队列为空,返回-1 //获取队尾元素。如果队列为空,返回-1。 //向循环队列插入一个元素。如果成功插入则返回真 //此时做题前我们来一个循环队列的介绍(因为下面这个题目会用到循环队列的概念) //特点:循环队列就是一个固定大小的队列(可以重复使用之前的空间的队列)(数组和链表都可以实现) //实现循环队列的重点:无论是使用数组还是链表都要多开一个空间,也就意味着一个存K个数据的循环队列,就要开K+1个空间,否则无法实现(判空和判满)(因为K个空间中此时存的都是数据) //并且我在实现我的环形队列的时候,使用数组的方式(此时用到的结构还是一个双指针的结构),并且当我的front指针等于我的tail指针的时候(此时就是表示这个环形队列为空) //并且当 (tail + 1)% (k + 1)= front 此时当tail和front符合这个表达式的时候就是表示为满的时候 //当我们使用链表的结构实现我的环形队列的时候(当然也是用双指针来实现的),此时 tail->next = front 就是表示满了 //当我们的front = tail 两个指针想等的时候也就是为空的时候 //这个我们一定要自己去实现一下(因为我么要学会灵活运用,才是王道) //首先不管是题目还是环形队列,此时第一步都是一样的就是构造一个结构体 typedef int SLTDataType; typedef struct MyCircularQueue//不要不懂这个,这个叫匿名结构体(就是只可以使用一次的结构体) { SLTDataType* a; SLTDataType front; SLTDataType tail; SLTDataType k; }MyCircularQueue; MyCircularQueue* myCircularQueueCreate(SLTDataType k)//初始化 { MyCircularQueue* oq = (MyCircularQueue*)malloc(sizeof(MyCircularQueue)); //此时就是进行结构体中的成员变量的初始化 oq->a = (int*)malloc(sizeof(int) * (k + 1));//此时这个就是表示我要开辟多少个空间出来(根据上面的原理可以得到,这边开辟的空间是k+1个) //所以有了上面这个例子,下次就可以很好的理解空间需要开辟的大小了 oq->front = oq->tail = 0; oq->k = k;//这个就是存放数据的个数 return oq; } bool myCircularQueueIsEmpty(MyCircularQueue* obj)//判空 { return obj->front == obj->tail; } bool myCircularQueueIsFull(MyCircularQueue* obj)//判满 { return (obj->tail + 1) % (obj->k + 1) == obj->front; } bool myCircularQueueEnQueue(MyCircularQueue* obj, SLTDataType x)//录入数据 { if (myCircularQueueIsFull(obj)) { return false;//此时这个表示我的空间如果满了,就直接出程序,因为我的空间已经满了就不需要再录入数据了 } else { //不满,我就只要把数据存进去,然后个数加加就可以了 obj->a[obj->tail] = x;//这个的理解就是表示:在obj的tail这个位置存放进我的数据 obj->tail++; //存完一个数据就让我的tail指针向后走,指向新的空间, 然后如果我的tail此时走到了k+1(用于判空判满的这个位置时),此时我就要让我的tail回到front的位置(这样此时一个环形的队列) //如果不判断我们就写成下面这个写法(反正主要的目的就是让我的tail回到我的front的位置上) obj->tail %= (obj->k + 1); } return true; } bool myCircularQueueDeQueue(MyCircularQueue* obj)//删除数据 { if (myCircularQueueIsFull(obj)) { return false; } else { //队列实现删除操作就是非常的抽象,只要front指针加加就行了(因为我一定要满足队列的基本原则,先进先出),所以只要加加就行,最后加到k+1的位置时,此时再回到最头上的位置(循环往复的走) obj->front++; obj->front %= (obj->k + 1);//这步就是让我的front回到最头上的那个空间 } return true; } int myCircularQueueFront(MyCircularQueue* obj)//取队头的数据 { if (myCircularQueueIsFull(obj)) { return -1; } return obj->a[obj->front];//此时这个位置就是表示我的队头的位置 } int myCircularQueueRear(MyCircularQueue* obj)//取队尾的数据 { if (myCircularQueueIsFull(obj)) { return -1; } /* if (obj->tail == 0) { return obj->a[k]; //放回队尾,只是条件不同而已 } else { return obj->a[obj->tail - 1];//放回队尾,只是条件不同而已 } */ //此时可以不按以上的判断来写,按照下面这样写也是一样的 int i = (obj->tail + obj->k) % (obj->k + 1);//这个就是在硬算,就像是算菱形的那个次数一样,硬算出来的而已(目的还是为了让我的tail回到原来的位置,并且刚好可以把此时队尾的数据给返回去) return obj->a[i];//返回队尾的数据而已 } void myCircularQueueFree(MyCircularQueue* obj)//销毁 { //注意此时有两层结构,所以要进行两层的free free(obj->a); free(obj); }