在学习了栈和队列的相关概念并且在之前也实现了栈和队列,接下来我们就来试着写一些栈和队列的算法题,在这些算法题当中将会使用到之前实现的栈或者队列,栈、队列在这当中将会变为工具一样,我们就会像工具一样拿着它们去解决问题,相信通过本篇算法题的解决,你会对栈和队列有更深层次的理解,一起加油吧!!!
1.有效的括号
通过以上的题目描述可以看出该算法题要我们实现的是是否是有效括号进行判断,那么在实现该算法题的代码前我们先要来了解什么是有效的括号
首先有效的括号每个左括号必须要有相对应的右括号且每个右括号都有一个对应的相同类型的左括号。
例如以下的示例
在以上的示例当中左括号 [ 和 ( 都有相对应的右括号 [ 和 ),这时就满足有效括号的第一个条件,但要判断是否为有效括号还需要进行其他条件的判断
有效括号的第二个条件是有效的括号一定是按之前的顺序闭合的,在此正确的闭合方式是指整体必须是对称的。在以上示例当中对称的,这就符合正确的括号闭合方式,而在以下示例当中整体就不是对称的这时就不符合正确的闭合方式
在了解了有效的括号后就来思考该如何来实现该算法题
确实在解决该算法题中我们使用到了我们之前实现的栈,首先通过遍历括号的字符串一开始如果遍历到字符是左括号就将该括号入栈;如果一开始遍历到的括号就是右括号就直接说明这不是一个有效的括号,遍历完了之后继续遍历之后的字符直到遍历到\0,在此过程中如果遍历到字符为左括号就入栈;是右括号就和取栈顶元素进行比较,若匹配就继续进行操作;不匹配就说明不为有效的括号
最终遍历完字符串后要看此时栈内是否为空,如果不为空的话就说明该字符串不为有效的括号
例如以下示例:
当字符串为以上的括号时,先遍历因为前两个都为左括号就将这两个字符入栈
之后再继续遍历原字符串由于遍历到位右括号就取栈顶元素和遍历到的字符进行匹配,这时为有效的括号就继续进行遍历
最终遍历完字符串时栈为空就说明原字符串中字符为有效的括号
在完成该算法题的分析以及示例的讲解后接下来就来实现该算法题的代码
在实现该算法题的代码前先要将之前实现的栈的结构和实现各个功能的函数导入到 isValid函数之前,之后isValid内的代码就按照我们以上的想法来实现,先定义一个栈st,之后遍历字符串进行括号的判断
当在实现以上过程中还要对两种特殊的情况进行判断,一种是当字符串中都是左括号时,这时就不会进行出栈这一操作,因此在isValid函数的最后要判断栈是否为空,为空就返回true;不为空就返回false。另一种是当字符串中都是右括号时,这时就不会有字符入栈,因此在遍历到字符是右括号时要判断栈是否为空,为空就返回false
以下是完整的代码
typedef char STDataType;
typedef struct stack
{
STDataType* a;
int capacity;
int top;
}stack;
//初始化栈
void stackInit(stack* ps)
{
assert(ps);
ps->a = NULL;
ps->top = ps->capacity = 0;
}
//销毁栈
void stackDestory(stack* ps)
{
assert(ps);
if (ps->a)
{
free(ps->a);
}
ps->a = NULL;
ps->top = ps->capacity = 0;
}
//检测栈是否为空
bool stackEmpty(stack* ps)
{
assert(ps);
return ps->top==0;
}
//入栈
void stackPush(stack* ps,STDataType x)
{
assert(ps);
if (ps->top == ps->capacity)
{
int newcapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
STDataType* tmp = (STDataType*)realloc(ps->a, newcapacity * sizeof(STDataType));
if (tmp == NULL)
{
perror("realloc fail!");
exit(1);
}
ps->a=tmp;
ps->capacity = newcapacity;
}
ps->a[ps->top++] = x;
}
//出栈
void stackPop(stack* ps)
{
assert(ps);
assert(!stackEmpty(ps));
--ps->top;
}
//获取栈顶元素
STDataType stackTop(stack* ps)
{
assert(ps);
assert(!stackEmpty(ps));
return ps->a[ps->top - 1];
}
//获取栈中有效元素个数
int stackSize(stack* ps)
{
assert(ps);
return ps->top;
}
bool isValid(char* s)
{
stack st;
stackInit(&st);
char* ps=s;
while(*ps!='\0')
{
if(*ps=='('||*ps=='['||*ps=='{')//判断是否为左括号
{
stackPush(&st,*ps);
}
else//判断是否为右括号
{
if(stackEmpty(&st))
{
return false;
}
char ch=stackTop(&st);
if((*ps==')'&&ch=='(' )||(*ps==']'&&ch=='[')||(*ps=='}'&&ch=='{'))
{
stackPop(&st);
}
else
{
stackDestory(&st);
return false;
}
}
ps++;
}
bool p=stackEmpty(&st);
stackInit(&st);
return p;
}
2.用队列实现栈
在之前的栈是我们按照底层是数组来实现的,而以上算法题需要的是我们基于队列来实现栈,接下来我们就来分析如何实现
在结合题目的描述以及该算法题代码部分的初始模式就可以了解到和之前实现基于数组实现栈一样在此算法题中我们要实现也是能完成栈中各个功能的函数,不过在此我们不在是直接基于数组来实现而是基于队列的各个功能来实现,那么接下来我们就来依次分析该如何实现各个函数
首先由于栈先进先出的特性就和队列先进后出的特性不同因此在使用队列来实现栈时,只用一个队列肯定是无法来模拟栈的,因此在此我们使用的是两个队列来模拟实现栈,接下来就来实现
首先在实现各个函数之前要完成以上栈结构的定义,在此由于在此是用两个队列来实现栈因此栈的结构定义和栈的初始化如下所示:
typedef struct { Queue q1; Queue q2; } MyStack; MyStack* myStackCreate() { Mytsack* pst=(Mystack*)malloc(sizeof(Mystack)); QueueInit(&pst->q1); QueueInit(&pst->q2); return pst; }
接下来看如何实现入栈和出栈
在实现入栈过程,首先一开始两个队列都是空的时在第一个数据入队列选取任意选一个队列将数据入队列,但在模拟之后的数据入栈时就需要选取两个队列中不为空的将后将数据入队列
而在出栈过程中首先先要找到不为空的队列,之后将该队列中size-1个数据提取出来将这些数据入另一个队列,最后将原提取数据的队列中的元素出队列即可
简单来说入栈和出栈的过程就为:
入栈:往不为空的队列中插入数据
出栈:找不为空的队列,将size-1个数据导入到另一个队列当中
例如以下示例
若我们要完成的操作是首先将数据1 2 3依次入栈,之后再将栈顶元素出栈,最后再入栈一个元素4,那么再使用两个队列该如何来实现整个过程呢?
首先是将 1 2 3三个数据依次入非空的队列,之后模拟实现出栈顶元素就先要将第一个队列内的前两个都移动到另一个队列内
之后再将第一个队列内的数据出队列就模拟实现了栈的出栈顶元素
在此之后再要将元素4入栈这时就需要往空队列中插入数据,在以上的示例当中就是在第二个队列中插入4
通过以上的示例就可以理解了使用两个队列来实现栈的过程,那么接下来我们就来实现栈中入栈和出栈的函数
在实现出栈的函数时,在此我们是先判断两个队列中哪一个为空,哪一个为非空,之后再进行出栈的操作,这样会使得函数的代码量相比使用if……else判断来实现会简洁很多
void myStackPush(MyStack* obj, int x)
{
if(!QueueEmpty(&obj->q1))
{
QueuePush(&obj->q1,x);
}
else
{
QueuePush(&obj->q2,x);
}
}
int myStackPop(MyStack* obj)
{
Queue* emp=&obj->q1;
Queue* none=&obj->q2;
if(!QueueEmpty(emp))//找出空队列和非空队列
{
emp=&obj->q2;
none=&obj->q1;
}
while(QueueSize(none)>1)//将非空队列中的size-1个数据传达空队列中
{
int tmp=QueueFront(none);
QueuePush(emp,tmp);
QueuePop(none);
}
int pop=QueueFront(none);
QueuePop(none);
return pop;
}
接下来看如何实现取栈顶元素
在以上我们实现了出栈的函数,那么你可能就会想到取栈顶元素相对于出栈不就只是少了最后将一开始不为空的队列进行出队列的操作吗?这种情况下将以上出栈的代码最后修改成不出队列后不就是取栈顶元素的函数吗?
int myStackTop(MyStack* obj)
{
Queue* emp=&obj->q1;
Queue* none=&obj->q2;
if(!QueueEmpty(emp))//找出空队列和非空队列
{
emp=&obj->q2;
none=&obj->q1;
}
while(QueueSize(none)>1)//将非空队列中的size-1个数据传达空队列中
{
int tmp=QueueFront(none);
QueuePush(emp,tmp);
QueuePop(none);
}
return QueueFront(none);
}
以上代码看似是没有问题的,但实际上以上的代码存在一个非常严重的问题,那就是当按照以上这种方法来来实现一次取栈顶元素后,之后会使得两个队列都不为空,那么之后如果再进行出栈就会使得出栈的元素是不确定的
例如以下示例:
在以上示例当中先将 1 2 3依次入栈后若按照以上的方法进行一次取栈顶元素后,两个队列就变为以上形式,这时如果再进行出栈的操作就会出栈的元素就可能为3也可能为1
通过分析就可以看出以上的实现的取栈顶元素函数是存在问题的,那么使用什么方法才能正确的实现呢?
其实在取栈顶元素中不用想的那么复杂,因为在队列当中我们已经实现了取队尾元素,因此就可以直接取不为空的队列的队尾元素得到的就是栈顶元素
这时实现的代码如下所示:
int myStackTop(MyStack* obj)
{
if(!QueueEmpty(&obj->q1))
{
return QueueBack(&obj->q1);
}
else
{
return QueueBack(&obj->q2);
}
}
接下来来实现判断栈是否为空和栈的销毁
在此这两个函数的实现比较简单,接下来我们就直接展示代码
bool myStackEmpty(MyStack* obj)
{
return QueueEmpty(&obj->q1)&&QueueEmpty(&obj->q2);
}
void myStackFree(MyStack* obj)
{
QueueDestory(&obj->q1);
QueueDestory(&obj->q2);
free(obj);
obj=NULL;
}
完整代码
typedef int QDataType;
typedef struct QueueNode
{
QDataType data;
struct QueueNode* next;
}QueueNode;
typedef struct Queue
{
struct QueueNode* phead;
struct QueueNode* ptail;
int size;
}Queue;
//初始化队列
void QueueInit(Queue* pq)
{
assert(pq);
pq->phead = pq->ptail = NULL;
pq->size = 0;
}
//销毁队列
void QueueDestory(Queue* pq)
{
assert(pq);
//assert(!QueueEmpty(pq));
QueueNode* pcur = pq->phead;
while (pcur)
{
QueueNode* Next = pcur->next;
free(pcur);
pcur = Next;
}
pq->phead = pq->ptail = NULL;
pq->size = 0;
}
//队列判空
bool QueueEmpty(Queue* pq)
{
assert(pq);
return pq->phead ==NULL && pq->ptail == NULL;
}
//入队列
void QueuePush(Queue* pq, QDataType x)
{
assert(pq);
QueueNode* newnode = (QueueNode*)malloc(sizeof(QueueNode));
if (newnode == NULL)
{
perror("malloc fail!");
exit(1);
}
newnode->data = x;
newnode->next = NULL;
if (pq->phead== NULL)
{
pq->phead = pq->ptail = newnode;
}
else
{
pq->ptail->next = newnode;
pq->ptail = newnode;
}
pq->size++;
}
//出队列
void QueuePop(Queue* pq)
{
assert(pq);
assert(!QueueEmpty(pq));
if (pq->phead == pq->ptail)
{
free(pq->phead);
pq->phead = pq->ptail = NULL;
}
else
{
QueueNode* Next = pq->phead->next;
free(pq->phead);
pq->phead = Next;
}
pq->size--;
}
//取队列头数据
QDataType QueueFront(Queue* pq)
{
assert(pq);
assert(!QueueEmpty(pq));
return pq->phead->data;
}
//取队列尾数据
QDataType QueueBack(Queue* pq)
{
assert(pq);
assert(!QueueEmpty(pq));
return pq->ptail->data;
}
//队列有效数据个数
int QueueSize(Queue* pq)
{
assert(pq);
return pq->size;
}
typedef struct
{
Queue q1;
Queue q2;
} MyStack;
MyStack* myStackCreate()
{
MyStack* pst=(MyStack*)malloc(sizeof(MyStack));
QueueInit(&pst->q1);
QueueInit(&pst->q2);
return pst;
}
void myStackPush(MyStack* obj, int x)
{
if(!QueueEmpty(&obj->q1))
{
QueuePush(&obj->q1,x);
}
else
{
QueuePush(&obj->q2,x);
}
}
int myStackPop(MyStack* obj)
{
Queue* emp=&obj->q1;
Queue* none=&obj->q2;
if(!QueueEmpty(emp))
{
emp=&obj->q2;
none=&obj->q1;
}
while(QueueSize(none)>1)
{
int tmp=QueueFront(none);
QueuePush(emp,tmp);
QueuePop(none);
}
int pop=QueueFront(none);
QueuePop(none);
return pop;
}
int myStackTop(MyStack* obj)
{
if(!QueueEmpty(&obj->q1))
{
return QueueBack(&obj->q1);
}
else
{
return QueueBack(&obj->q2);
}
}
bool myStackEmpty(MyStack* obj)
{
return QueueEmpty(&obj->q1)&&QueueEmpty(&obj->q2);
}
void myStackFree(MyStack* obj)
{
QueueDestory(&obj->q1);
QueueDestory(&obj->q2);
free(obj);
obj=NULL;
}
/**
* Your MyStack struct will be instantiated and called as such:
* MyStack* obj = myStackCreate();
* myStackPush(obj, x);
* int param_2 = myStackPop(obj);
* int param_3 = myStackTop(obj);
* bool param_4 = myStackEmpty(obj);
* myStackFree(obj);
*/
注:在以上代码中在栈的销毁中我们会调用到栈的销毁,这时由于我们创建的队列在销毁时一定会有一个为空,因此就需要将原队列销毁的函数中assert(!QueueDestory)这个assert断言去掉,否则以上的代码就无法通过
3.用栈实现队列
通过以上的题目描述就可以看出该算法题要我们实现的是使用实现好的栈来实现队列,和我们之前直接实现队列一样在此要实现队列中的各种功能
接下来就来分析如何来利用栈来模拟实现队列
首先是由于队列先进先出的特性和栈先进后出的特性,因此只使用一个栈是无法模拟实现队列的,因此在此我们需要两个栈来完成模拟实现队列,接下来就来实现
首先在实现各个函数之前要完成以上队列结构的定义,在此由于在此是用两个栈来实现队列因此队列的结构定义和队列的初始化如下所示:
typedef struct { stack push; stack pop; } MyQueue; MyQueue* myQueueCreate() { MyQueue* pst=(MyQueue*)malloc(sizeof(MyQueue)); stackInit(&pst->push); stackInit(&pst->pop); return pst; }
接下来看如何实现入队列和出队列
在使用两个栈来模拟实现队列中的入队列我们使用的方法是一个栈来实现入队列,将该栈定义为PushST当有数据要入队列时都将数据入PushST,另一个栈来实现出队列,将该栈定义PopST,首先判断PopST是否为空如果为空的话就将PushST的数据导入到该栈中;如果不为空就直接出栈
简单来说入队列和出队列的过程就为:
入队列:往PushST内插入数据
出队列:先判断PopST是否为空,不为空的话直接出栈;为空话先将PushST的数据导入到PopST再出栈
来看以下的示例:
在以上示例当中若我们先要将数据 1 2 3依次入队列,之后再取进行一次出队列操作后将数据4入队列这时使用两个栈的过程就如下所示
首先是将数据 1 2 3依次入PushST
之后进行出队列,这时由于PopST为空因此要先将PushST的数据导入到PopST,之后再将PopST进行一次出栈即可
最后将数据4入栈PushST即可
完成入队列和出队列的分析,接下来就来实现这两个的代码
void myQueuePush(MyQueue* obj, int x)
{
stackPush(&obj->push,x);
}
int myQueuePop(MyQueue* obj)
{
if(stackEmpty(&obj->pop))
{
while(stackSize(&obj->push))
{
int tmp=stackTop(&obj->push);
stackPush(&obj->pop,tmp);
stackPop(&obj->push);
}
}
int tmp=stackTop(&obj->pop);
stackPop(&obj->pop);
return tmp;
}
接下来看如何实现取队头元素
在取队列的队头元素时,这时整个的过程就和以上出队列相识,只是最后不在是将PopST内进行出栈,而是只取出PopST的栈顶数据
以下就是取队头元素的函数
int myQueuePeek(MyQueue* obj)
{
if(stackEmpty(&obj->pop))
{
while(stackSize(&obj->push))
{
int tmp=stackTop(&obj->push);
stackPush(&obj->pop,tmp);
stackPop(&obj->push);
}
}
int tmp=stackTop(&obj->pop);
return tmp;
}
接下来来实现判断队列是否为空和队列的销毁
在此这两个函数的实现比较简单,接下来我们就直接展示代码
bool myQueueEmpty(MyQueue* obj)
{
return stackEmpty(&obj->push)&&stackEmpty(&obj->pop);
}
void myQueueFree(MyQueue* obj)
{
stackDestory(&obj->push);
stackDestory(&obj->pop);
free(obj);
obj=NULL;
}
完整代码
typedef int STDataType;
typedef struct stack
{
STDataType* a;
int capacity;
int top;
}stack;
//初始化栈
void stackInit(stack* ps)
{
assert(ps);
ps->a = NULL;
ps->top = ps->capacity = 0;
}
//销毁栈
void stackDestory(stack* ps)
{
assert(ps);
if (ps->a)
{
free(ps->a);
}
ps->a = NULL;
ps->top = ps->capacity = 0;
}
//检测栈是否为空
bool stackEmpty(stack* ps)
{
assert(ps);
return ps->top==0;
}
//入栈
void stackPush(stack* ps,STDataType x)
{
assert(ps);
if (ps->top == ps->capacity)
{
int newcapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
STDataType* tmp = (STDataType*)realloc(ps->a, newcapacity * sizeof(STDataType));
if (tmp == NULL)
{
perror("realloc fail!");
exit(1);
}
ps->a=tmp;
ps->capacity = newcapacity;
}
ps->a[ps->top++] = x;
}
//出栈
void stackPop(stack* ps)
{
assert(ps);
assert(!stackEmpty(ps));
--ps->top;
}
//获取栈顶元素
STDataType stackTop(stack* ps)
{
assert(ps);
assert(!stackEmpty(ps));
return ps->a[ps->top - 1];
}
//获取栈中有效元素个数
int stackSize(stack* ps)
{
assert(ps);
return ps->top;
}
typedef struct
{
stack push;
stack pop;
} MyQueue;
MyQueue* myQueueCreate()
{
MyQueue* pst=(MyQueue*)malloc(sizeof(MyQueue));
stackInit(&pst->push);
stackInit(&pst->pop);
return pst;
}
void myQueuePush(MyQueue* obj, int x)
{
stackPush(&obj->push,x);
}
int myQueuePop(MyQueue* obj)
{
if(stackEmpty(&obj->pop))
{
while(stackSize(&obj->push))
{
int tmp=stackTop(&obj->push);
stackPush(&obj->pop,tmp);
stackPop(&obj->push);
}
}
int tmp=stackTop(&obj->pop);
stackPop(&obj->pop);
return tmp;
}
int myQueuePeek(MyQueue* obj)
{
if(stackEmpty(&obj->pop))
{
while(stackSize(&obj->push))
{
int tmp=stackTop(&obj->push);
stackPush(&obj->pop,tmp);
stackPop(&obj->push);
}
}
int tmp=stackTop(&obj->pop);
return tmp;
}
bool myQueueEmpty(MyQueue* obj)
{
return stackEmpty(&obj->push)&&stackEmpty(&obj->pop);
}
void myQueueFree(MyQueue* obj)
{
stackDestory(&obj->push);
stackDestory(&obj->pop);
free(obj);
obj=NULL;
}
/**
* Your MyQueue struct will be instantiated and called as such:
* MyQueue* obj = myQueueCreate();
* myQueuePush(obj, x);
* int param_2 = myQueuePop(obj);
* int param_3 = myQueuePeek(obj);
* bool param_4 = myQueueEmpty(obj);
* myQueueFree(obj);
*/
4.设计循环队列
在以上算法题提到我们之前未了解的一种结构——循环队列,那么循环队列的结构是什么样的呢?
在此循环队列和我们之前实现的有所不同,循环队列的结构如下所示通过以上的图示就可以看出循环队列和普通的队列不同的是普通队列在队列满了之后就无法再插入数据,而循环队列再满了之后再插入数据就会从队头开始覆盖数据,就例如以上的循环队列再插入一个数据a7就变为以下形式
在了解了循环队列的结构特征后接下来我们来以上算法题该如何来实现
首先是循环链表的底层结构我们该选择数组还是链表呢?你可能会想之前在实现普通的队列时选择的是链表那么在这也选择链表呗。但在此和之前有所不同,在该算法题中的队列大小也就是队列的空间大小是题目给的,如果底层使用链表就要在一开始创建相应的节点并且将各个节点连接起来,并且在此的节点我们还要再创建一个新的变量让插入新数据时可以判断相应的节点是否为空。因此总的来看底层选择链表就会很繁琐。
相反底层使用数组就简易很多了,在一开始数组大小就是确定的,并且由于数组的空间是连续存放的,这样在一开始空间创建时不需要像链表一样要将节点连接,之后在插入数据时也不用检查是否为空
通过以上的分析就可以得出在此循环队列的底层结构选择数组
接下来就来分析循环队列的结构该如何定义
在此我们创建两个变量front和rear来分别表示数组中的头部和尾部,当插入数据时rear就向后移动一位,一开始front和rear都指为数组的首元素下标
那么循环队列为空和循环队列满了时的特征是什么呢?
首先是循环队列为空通过以上就可以看出这时front=rear,那队列满的时候呢?这时来看以下示例
这时可以看出当循环队列满时特征也是front=rear,如果按照以上的结构这时就会有问题就是当队列为空时和队列满时的特征都是一样的,这就会造成无法区分的问题,因此要如何去优化呢?
在此解决方法是使循环队列的实际空间大小比可使用的空间大一,就例如以上的队列当队列为满时就为以下形式
假设在此队列可用的空间大小为capacity,则这时队列满的特征就(rear+1)% (capacity+1)=front
接下来我们就来实现代码
//循环队列结构
typedef struct
{
int*arr;
int Front;
int rear;
int capacity;
} MyCircularQueue;
//初始化
MyCircularQueue* myCircularQueueCreate(int k)
{
MyCircularQueue* pst=(MyCircularQueue*)malloc(sizeof(MyCircularQueue));
pst->arr=(int*)malloc(sizeof(int)*(k+1));
pst->Front=pst->rear=0;
pst->capacity=k;
return pst;
}
//判断队列是否为空
bool myCircularQueueIsEmpty(MyCircularQueue* obj)
{
return obj->Front==obj->rear;
}
//判断队列是否为满
bool myCircularQueueIsFull(MyCircularQueue* obj)
{
return (obj->rear+1)%(obj->capacity+1)==obj->Front;
}
//插入数据
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value)
{
if(myCircularQueueIsFull(obj))
{
return false;
}
obj->arr[obj->rear++]=value;
obj->rear%=obj->capacity+1;
return true;
}
//删除数据
bool myCircularQueueDeQueue(MyCircularQueue* obj)
{
if(myCircularQueueIsEmpty(obj))
{
return false;
}
++obj->Front;
obj->Front%=obj->capacity+1;
return true;
}
//取队头元素
int myCircularQueueFront(MyCircularQueue* obj)
{
if(myCircularQueueIsEmpty(obj))
{
return -1;
}
return obj->arr[obj->Front];
}
//取队尾元素
int myCircularQueueRear(MyCircularQueue* obj)
{
if(myCircularQueueIsEmpty(obj))
{
return -1;
}
int prev=obj->rear-1;
if(obj->rear==0)
{
prev=obj->capacity;
}
return obj->arr[prev];
}
//销毁队列
void myCircularQueueFree(MyCircularQueue* obj)
{
free(obj->arr);
free(obj);
obj=NULL;
}
/**
* Your MyCircularQueue struct will be instantiated and called as such:
* MyCircularQueue* obj = myCircularQueueCreate(k);
* bool param_1 = myCircularQueueEnQueue(obj, value);
* bool param_2 = myCircularQueueDeQueue(obj);
* int param_3 = myCircularQueueFront(obj);
* int param_4 = myCircularQueueRear(obj);
* bool param_5 = myCircularQueueIsEmpty(obj);
* bool param_6 = myCircularQueueIsFull(obj);
* myCircularQueueFree(obj);
*/