有效的括号
给定一个只包括“(”,“(”,‘{’,‘}’,'[', ']'的字符串s,判断字符串是否有效。
有效的字符串需要满足:
1.左括号必须用相同类型的右括号闭合。
2.左括号必须以正确的顺序闭合。
如上述示例,是对栈的先进后出这个特性的利用,我们只需要让左括号入栈,右括号与上一个数据匹配。我们用两个指针*s和top,*s指针指向下一个需要插入或者需要匹配的数据位置,top指针指向栈顶的位置。然后如果*s指针指向的数据是左括号就放入栈顶,如果是右括号就取出栈顶数据进行匹配,匹配成功就让 ”*s++“,如果匹配失败就直接false。
需要注意的是,有给的数据只有左括号的例子,这时候就要判断一下。代码中的---1。这里使用了一个bool类型的返回值,来接收栈顶数据,这时候如果全部匹配的话栈里应该为空,如果栈不为空,说明有一个左括号没有匹配,就返回false。
还有只有右括号的情况,如代码中的--2,代码中因为是else的情况所以*s指向的数据是右括号的情况,这时如果栈中是空的情况满足了只有一个右括号这个示例,就直接返回false。
typedef char STDataType;
typedef struct stack
{
STDataType* a;
int top;
int capacity;
}ST;
void StackInit(ST* ps)
{
assert(ps);
ps->a = (STDataType*)malloc(sizeof(STDatraType)*4);
if (ps->a == NULL) //判断malloc函数是否使用成功
{
printf("realloc fail\n");
exit(-1);
}
ps->top = 0; //top的初始化
ps->capacity = 4; //capacity的初始化
}
void StackPush(ST* ps,STDataType x)
{
assert(ps);
if(ps->top == ps->capacity)
{
STDataType* tmp = (STDataType*)realloc(ps->a,ps->capacity*2sizeof(STDataType));
if (tmp == NULL) //判断realloc函数是否使用成功
{
printf("realloc fail\n");
exit(-1);
}
else
{
ps->a = tmp;
ps->capacity *= 2;
}
ps->a = tmp; //对开辟的空间进行分布
ps->capacity = newCapacity;
}
ps->a[ps->top] = x;
ps->top++;
}
void StackDestroy(ST* ps)
{
assert(ps);
free(ps->a);
ps->a = NULL;
ps->capacity = ps->top = 0;
}
void StackPop(ST* ps)
{
assert(ps);
assert(ps->top > 0); //判断栈是否为空
ps->top--;
}
STDataType StackTop(ST* ps)
{
assert(ps);
assert(ps->top > 0); //判断这个栈是否为空,为空就报错
return ps-a[ps->top - 1];
}
bool stackEmpty(ST* ps)
{
assert(ps);
return ps->top == 0;
}
bool isValid(char* s)
{
ST st;
StackInit(&st);
while(*s);
{
if(*s == '('
|| *s == '{'
|| *s == '[')
{
StackPush(&st,*s); //如果是左括号就把他放入栈里面 -----2
++s;
}
else
{ //遇到右括号了,但是,栈里面没有数据,说明前面没有左括号,不匹配,返回false
if(StackEmpty(&st))
{
return false;
}
STDataType top = StackTop(&st); //取栈顶的元素
StackPop(&st);
if((*s == '}' && top != '{')
|| (*s == ')' && top != '[')
|| (*s == ']' && top != '['))
{
StackDestroy(&st); //把栈全部释放一下,防止内存泄露
return false;
}
else
{
++s;
}
}
}
bool ret = StackEmpty(&st); //如果栈不是空,说明栈中还有左括号未出 ------1
//没有匹配,返回是false,如果是空就返回ture
StackDestroy(&st);
return ret;
}
用队列实现栈
只是用两个队列实现一个后入先出的栈,并支持普通栈的全部四种操作(push,top,pop,empty)。
实现MyStack类:
void push(int x) //将元素x压入栈顶
int pop() //溢出并返回栈顶的元素
int top() //返回栈顶的元素
boolean empty() //如果栈是空的,返回true ;否则返回false
首先先不考虑功能函数实现的问题,先考虑用两个队列实现栈的方法,栈的规则是后入先出,所有假设把队列的队尾看做是栈的栈顶,那么对于数据的插入队列和栈是一样的,那插入数据就直接插入就行,但是删除数据不同,队列删除数据是在队头一端,栈删除数据是在队列的队尾一端。所以这时候就用到第二个队列了。
假设我们第一个队列里面按照顺序存储了1,2,3,4这四个数据,那么假设我们要按照栈的方式删除一个数据,也就是”4“这个数据,那么我们只需要把栈中的“1”这个数据先保存到另一个栈里面,在保存“2”,这样依次保存,知道保存到“3”,这时原本那个队列里还剩个“4”,在把“4”删除掉,这时因为第一个队列里面只剩下一个“4”,所以删除可以按照队列的方式删除。
届时,我们不需要再把数据传回原本的那个队列,直接使用前面用来转换数据的队列来进行之后的操作。如下图所示,我们执行push插入“4”和“5“的操作。
核心思路:
1.入数据,往不为空的一个队列插入,保持另一个队列为空
2.出数据,依次出队头的数据,转移另一个队列保存,只剩下最后一个的时候,Pop(删除)掉。
这样就用两个队列实现了栈的“后入先出”的原则。
代码实现“栈”和“栈”的插入操作
因为这个“栈”的插入操作和普通的栈是一样的,经过上面的分析,我们直接用代码来实现“栈”的定义和插入。
typedef struct Queue
{
QueueNode* head; //头指针
QueueNode* tail; //尾指针
}Queue;
typedef struct {
Queue q1;
Queue q2;
}MyStack;
void QueuePush(Queue* pq,QDataType x)
{
assert(pq);
QueueNode* newnode = (QueueNode*)malloc(sizeof(QueueNode)); //创建一片空间为要插入的新的
//结点
newnode->next = NULL;
newnode->data = x;
if(pq->head == NULL) //判断这个队列是否为空
{
pq->head = pq->tail = newnode; //让头尾指针都指向队列中唯一的结点
}
else
}
pq->tail->next = newnode; //让插入之前的最后一个结点的next指针指向新插入的
//结点
pq->tail = newnode; //更新尾指针
}
}
MyStack* myStackCreate() //我们采用malloc的方式申请一片空间来创建队列。
{
MyStack* st = (MyStack*)malloc(sizeof(MyStack));
QueueInit(&st->q1); //因为在MyStack结构体里的q1和q2优势两个结构体,所以要对st进行解引用
QueueInit(&st->q2);
return st;
}
void myStackPush(MyStack* obj,int x)
{
if(QueueEmpty(&obj->q1))
{
QueuePush(obj->q1,x)
}
else
{
QueuePusu(&obj->q1.x)
}
}
int main()
{
MyStack* st = myStackCreate();
myStackPush(st,1);
myStackPush(st,2);
myStackPush(st,3);
return 0;
{
“栈”的删除操作
对于“栈”的删除操作,我们在实现的时候要先判断这两个队列哪一个是空哪一个不是空,我们先定义两个指针变量(empty和nonempty)empty指针指向的是没有没有数据的队列,nonempty指针指向的是有数据的队列。对于上述的q1队列和q2队列,我们可以先默认q1队列为空,q2不为空,然后把empty指针指向q1,nonempty指针指向q2。然后再if判断一下如果q1队列不为空,就把nonempty指针指向q1,empty指针指向q2。
当然也可以用if -- else来实现,我们使用(empty和nonempty)这两个指针用来储存有数据的队列和没有数据的队列,方便下面的实现操作。
当我们判断为空和不为空的队列之后,就要开始实现删除操作了。我们先把有数据队列的第一个数据储存到另一个队列里,然后再把原本有数据队列里的那个数据给删除掉,如此反复,一直操作到要删除的数据的位置就停止储存。然后把要删除的数据之间删除掉即可。
QDataType QueueFront(Qeueu* pq) //取这个队列中队头的数据
{
assert(pq);
assert(!QueueEmpty)); //判断队列尾空去情况,为空就报错
return pq->head->data;
}
int QueueSize (Queue*pq) //取队列中数据个数的函数
{
assert(pq);
int n = 0;
QueueNode* cur = pq->head;
while(cur)
{
++n;
cur = cur->next;
}
return n;
}
void QueuePop(Queue* pq)
{
assert(pq);
assert(!QueueEmpty(pq));
QueueNode* next = pq->head->next;
free(pq->head);
pq->head = next;
if(pq->head == NULL) //判断队列是否被删完
{
pq->tail = NULL; //将tail尾指针置空
}
}
void QueuePush(Queue* pq,QDataType x)
{
assert(pq);
QueueNode* newnode = (QueueNode*)malloc(sizeof(QueueNode)); //创建一片空间为要插入的新的
//结点
newnode->next = NULL;
newnode->data = x;
if(pq->head == NULL) //判断这个队列是否为空
{
pq->head = pq->tail = newnode; //让头尾指针都指向队列中唯一的结点
}
else
}
pq->tail->next = newnode; //让插入之前的最后一个结点的next指针指向新插入的
//结点
pq->tail = newnode; //更新尾指针
}
}
bool QueueEmpty(Queue* pq)
{
assert(pq);
return pq->head == NULL;
}
int myStackPop(MyStack* obj)
{
Queue* emptyQ = &obj->q1;
Queue* nonempty = &obj->q2;
if(!QueueEmpty(&obj->q1))
{
empty = &obj->q2;
nonemptyQ = &obj->q1;
}
//利用循环来把有数据队列里前面的数据存储到另一个空队列里
while(QueueSize(nonemptyQ) > 1) //当队列里面只有一个数据的时候就停止循环
{
QueuePush(emptyQ,QueueFront(nonemptyQ)); //先从队头把一个数据用另一个空队列储存
QueuePop(nonemptyQ); //再从队列的队头删除刚刚传入的那个数据
}
int top = QueueFront(nonemptyQ); //因为题目要求返回这个删除数据的值,所以在删除之前储存一下
QueuePop(nonemptyQ); //删除需要删除的那个数据
return top;
}
取“栈”顶的数据
对于取数据就不会用到第二个队列了,虽然按照我们之前对这个“栈”的定义,取“栈“顶的数据相当于就是取队列的队尾,我们只需要找到这个有数据的队列,然后然后利用这个队列的尾指针来寻找这个”栈“顶数据即可。
bool QueueEmpty(Queue* pq)
{
assert(pq);
return pq->head == NULL;
}
QueueType QueueBack(Queue* pq) //取队列队尾的数据
{
assert(pq);
assert(!QueueEmpty); //判断队列为空的情况
return pq->tail->data;
}
int myStackTop(MyStack* obj)
{
if(!QueueEmpty(&obj->q1) //判断(找出)不为空的队列
{
return QueueBack(&obj->q1); //取出队尾的数据
}
else
{
return QueueBack(&obj->q2);
}
}
判断“栈”为空的函数
我们之前在“栈”的删除操作里实现的判断队列为空的函数,而判断这个“栈”为不为空,其实就是看这个两个队列是否都为空,都为空这个“栈”就不是空,反之亦然。
bool QueueEmpty(Queue* pq)
{
assert(pq);
return pq->head == NULL;
}
bool mtStackEmpty(MyStack* obj)
{
return QueueEmpty(&obj->q1) && QueueEmpty(&obj->q2);
}
“栈”的整个删除
对于我们定义的“栈”的结构,是两个队列所组成的,我们有一个MyStack*类型的st指针指向这个结构,而这个结构里面有两个指针q1和q2,这两个指针分别指向两个队列,这两个队列有一个是NULL的,有一个是可能是有数据的,那么我们不能直接free(st),不能直接把这个“栈”的指针free掉,要先把里面的q1和q2指向的空间释放掉再去释放st指针。(防止出现野指针)
void QueueDestroy(Queue* pq) //对于整个队列的删除函数
{
assert(pq);
QueueNode* cur = pq->head;
while(cur != NULL) //如果while循环的条件使用(cur != pq->tail)会导致有一个数据删不完
{
QueueNode* next = cur->next; //先存储下一个结点位置
free(cur); //在释放这个结点
cur = next; //指向下一个结点
}
pq->next = pq->tail = NULL;
}
void myStackFree(MyStack* st)
{
QueueDestroy(&obj->q1); //先把q1和q2指向空间删除
QueueEestroy(&obj->q2);
free(obj);
}
用栈实现队列
请你只使用两个栈实现先入先出的队列,这个“队列”应当支持一般队列支持的所有操作
(push pop peek empty)
实现MyQueue类:
void push(int x) //将元素x推到队列的末尾
int pop() //从队列的开头移除并返回元素
int peek() //返回队列开头的元素
boolean empty() //如果队列为空,返回true ; 否则返回false
对于这个“队列”的实现,我们用两个栈(pushST和popST)来实现,首先我们来考虑插入数据的情况,我们假设把栈pushST的栈顶看做是“队列”的队尾,那么对于这个“队列”的插入操作就非常的简单了,直接插入就行。我们主要来看看删除操作,我们使用第二个栈来实现删除操作,当我们要删除数据的时候,就把栈pushST里的数据按照栈的规则传入到栈里面popST,当然传入到popST里面也是按栈的规则传入的,我们发现,数据的顺序就行了逆序。
我们之前在用两个队列实现栈的时候,我们在两个队列互相传数据的时候,数据的顺序是不会改变的。而两个栈在互相传数据的时候,数据的顺序发生了改变。
当我们把数据都传好之后,这时原本在push底端的数据就跑到了头上,我们就直接把栈popST的栈顶数据删除就行。这样就完成了对这个“队列”的数据进行删除的操作。
我们发现,我们用两个栈实现的队列,这个队列的插入操作和删除操作是互不影响的。而且我们定义的pushST插入栈,和popST删除栈,这两个栈也是不影响的。一个只管插入数据,一个只管删除数据,这用方便了我们对这些操作的实现。
这个“队列”的初始化及其定义
Typedef int STDataType;
Typedef struct Stack //栈的结构体定义
{
STDataType* a;
int top; //栈顶的位置
int capacity; //容量空间
}ST;
void StackInit(ST* ps) //栈的初始化函数
{
assert(ps);
ps->a = NULL;
ps->top = 0; //top的初始化
ps->capacity = 0; //capacity的初始化
}
typedef struct
{
ST pushST;
ST popST;
}MyQueue;
MyQueue* myQueueCreate() //创建两个栈
{
MyQueue* q = (MyQueue*)malloc(sizeof(MyQueue)); //开辟栈的空间
StackInit(&q->pushST); //栈pushST的初始化
StackInit(&q->popST); //栈popST的初始化
return q;
}
判断这个队列为不为空的函数
我们判断这个队列为不为空就要看这两个栈是否同时为空,同时为空及时曾队列为空
bool AstackEmpty(ST* ps)
{
assert(ps);
return ps->top == 0;
}
void myQueueEmpty(MyQueue* obj)
{
return StackEmpty(&obj->pushST && StackEmpty(&obj->popST);
}
插入数据操作
void StackPush(ST* ps,STDataType x) //在栈的栈顶插入数据
{
assert(ps);
if(ps->top == ps->capacity)
{
int newCapacity = ps->zapacity == ? 4 : ps->capacity * 2; //判断这个栈是否为空
STDataType* tmp = realloc(ps->a,sizeof(STDataType)*newCapacity);
if (tmp == NULL) //判断realloc函数是否使用成功
{
printf("realloc fail\n")
}
ps->a = tmp; //对开辟的空间进行分布
ps->capacity = newCapacity;
}
ps->a[ps->top] = x;
ps->top++;
}
void myQueuePush(MyQueue* obj,int x)
{
StackPush(&obj->pushST,x);
}
删除数据操作
对于出数据,当我们的popST栈里有数据,就直接出数据就行;如果popST栈里没有数据,就把pushST栈里的数据传入到popST栈里,然后再在popST栈里实现出数据的操作。
void StackPop(ST* ps) //取栈顶的数据
{
assert(ps);
assert(ps->top > 0); //判断栈是否为空
ps->top--;
}
STDataType StackTop(ST* ps) //栈的栈顶数据进行删除
{
assert(ps);
assert(ps->top > 0); //判断这个栈是否为空,为空就报错
return ps-a[ps->top - 1];
}
bool AstackEmpty(ST* ps) //判断栈为空的函数
{
assert(ps);
return ps->top == 0;
}
int myQueuePop(MyQueue* obj)
{
//如果popST栈中没有数据,就把pushST栈中的数据导入到popST里,这样就满足了先入先出的原则
if(StackEmpty(&obj->popST)) //判断popST栈是否为空
{ //如果是空 就把pushST栈里面的数据导入到popST栈里面
while (StackEmpty(&obj->pushST)
{
StackPush(&obj->popST,StackTop(&obj->pushST));
StackPop(&obj->pushST);
}
}
int front = StackTop(&obj->popST);
StackPop(&obj->popST);
return front:
}
取队列开头的元素
我们取队列开头的数据,要分两种情况,第一种是popST里面有数据,那就直接使用之前实现的StackTop()这个函数来寻找popST栈的栈顶的数据就行。第二种是popST里面没有数据,没有数据就把pushST里面的数据传入到popST栈里面,然后再用StackTop()函数就行查找返回就行。
STDataType StackTop(ST* ps)
{
assert(ps);
assert(ps->top > 0); //判断这个栈是否为空,为空就报错
return ps-a[ps->top - 1];
}
void StackPush(ST* ps,STDataType x)
{
assert(ps);
if(ps->top == ps->capacity)
{
int newCapacity = ps->zapacity == ? 4 : ps->capacity * 2; //判断这个栈是否为空
STDataType* tmp = realloc(ps->a,sizeof(STDataType)*newCapacity);
if (tmp == NULL) //判断realloc函数是否使用成功
{
printf("realloc fail\n")
}
ps->a = tmp; //对开辟的空间进行分布
ps->capacity = newCapacity;
}
ps->a[ps->top] = x;
ps->top++;
}
void StackPop(ST* ps)
{
assert(ps);
assert(ps->top > 0); //判断栈是否为空
ps->top--;
}
if(StackEmpty(&obj->popST)) //判断popST栈是否为空
{ //如果是空 就把pushST栈里面的数据导入到popST栈里面
while (StackEmpty(&obj->pushST)
{
StackPush(&obj->popST,StackTop(&obj->pushST));
StackPop(&obj->pushST);
}
}
return StackTop(&obj_>pushST);
整个“队列”的删除
对于这个指向队列的指针指向的空间,这个空间里面有两个指针,pushST和popST,这两个指针分别指向了pushST栈和popST栈,我们在删除整个队列的时候,不能直接把指向这个队列的指针的空间free掉,这个空间只是存储了指向pushST栈和popST栈的指针,如果直接free这些指针,那这两个指针指向的空间还没有free掉,造成了内存泄漏。
所以我们要先把这两个栈的空间free 掉在free 这个指向队列的“大指针”。
void StackDestroy(ST* ps) //将整个栈释放掉
{
assert(ps);
free(ps->a);
ps->a = NULL;
ps->capacity = ps->top = 0;
}
void myQueueFree(MyQueue* obj)
{
StackDestroy(&obj->pushST); //先把两个栈释放掉
StackDestroy(&obj->popST);
free(obj); //最后释放这个指针结构体
}
设计循环队列
设计你的循环队列实现。循环队列是一种特殊的线性数据结构,其操作表现基于FIFO(先进先出)原则并且队尾被连接在队首之后以形成一个循环。他也被称为“环形缓冲器”。
循环队列的一个好处是我们可以利用这个队列之前用过的空间。在一个普通的队列里,一旦一个队列满了,我们就不能插入下一个元素,即使在队列前面仍有空间。但是使用循环队列,我能使用这些空间取存储新的值。
你的实现应该支持如下操作:
MyCircularQueue(k) //构造器,设置队列长度为k
Front //从队首获取元素。如果队列为空,返回-1
Rear //获取队尾的元素。如果队列为空,返回1
enQueue(value) //向循环队列插入一个元素。如果成功插入则返回真
在实现之前,我们需要注意的是:
1.符合先进先出
2.空间大小固定
这里有两种实现方式,一种是用数组来实现,一种使用链表来实现。用链表来实现的话,其实我们之前咋学习单链表和双链表的时候学习过类似的结构,就是循环双链表 ,它在双链表的基础上,在第一个结点的prev指针(每个结点指向上一个结点的指针)是指向这个链表的最后一个结点的,而与之对应的最后一个结点的next指针(每个结点指向下一个结点的指针),是指向第一个结点的。由这样的链接方式,我们可以理解为,他们首位相连了。
所以,在用链表实现队列的时候,用这个方法就可以实现循环队列。
循环队列实现:
因为这个队列的大小是K,也就是说这个队列的空间要在一开始就开辟好,之后不能改变。
我们定义两个指针(tail和front),咋插入数据的时候,tail指向下一个结点,在下一个结点的位置插入数据就行。而front指针使用来删除数据的,front指针是一直指向这个队列的第一个有数据的结点的,我们先把front指针指向的结点的数据删除,然后再把front指向它删除数据结点的下一个结点。如此反复就用链表实现了循环队列。
需要注意的是,当tail指针和front指针指向同一个位置的时候,就表示这个队列为空。
数组实现:
对于数组的实现,也是用两个指针(tail和front),同样是插入数据时先把tail指向下一个结点,然后插入数据,咋删除数据的时候,先把front指针指向位置的数据删除然后在把front指针指向下一个结点。
当tail指针指向数组最后一个元素的下一个位置的时候,就把tail指针指向第一个元素。
注:
循环队列,无论使用数据实现还是使用链表实现,都要多开一个空间,也就意味着,要是一个存k个数据的循环队列,要开k+1空间。否则无法实现判空和判满。我们上面也说了,当front == tail的时候,是用来判定队列是否为空的,如果我们不多开一个空间的话,就有一种情况,如下所示:
这时候也是front = tail的情况,但是这个时候队列是满的状态,而不是空的状态。
数组和队列的判空条件都是 front和tail指向一个位置,但是判满的条件不太一样:
数组:
(tail + 1)%( k + 1) == front;
链式结构:
tail -> next == front;
因为链式的最后一个结点有直接指向第一个结点的next指针,所以这里判空很方便。
数组的代码实现:
实现流程之前已经说过了,这个还需要注意的是一种特殊情况,如下所示:
这个情况下,tail指向结点的下一个结点是空的,他的下一个结点应该是front指向的结点,所以在代码实现中,这个情况要把tail指向第一个结点。
typedef struct {
int * a;
int front;
int tail;
int k;
}MyCircularQueue;
MyCircularQueue* myCircularqueueCreate(int k)
{
MyCircularQueue* cq = (MyCircularQueue*)malloc(sizeof(MyCiecularQueue));
cq->a = (int*)malloc(sizeof(int)*(k+1));
cq->front = q->tail = 0;
cq->k = k;
return q;
}
判断队列是空和队列是满的两个的函数
判空:
我们之前说过了,当两个指针指向一个位置的时候,这个栈就为空了。
bool myCircularQueueIsEmpty(MyCircularQueue* obj)
{
return obj->front == obj->tail; //判断队列是否为空
}
判满:
对于数组的判满前面也说过了,满足 (tail + 1)%( k + 1) == front; 就认为这个队列已经满了。
这里还需要注意的是一种特殊情况,如下所示:
这个情况下,tail指向结点的下一个结点是空的,他的下一个结点应该是front指向的结点,所以在代码实现中,这个情况要把tail指向第一个结点。
bool myCircularQueueIsFull(MyCircularQueue* obj)
{
return (obj->tail+1) % (obj->k+1) == obj->front; //判断这个栈是否满了
}
插入数据
对于插入数据,有插入成功的情况和插入失败的情况,对于插入操作,就是当这个队列满了的时候这个插入操作就失败了。
而插入数据也是有特殊情况的:
这种情况是tail需要在第一个结点进行插入数据的操作,当我们在这个tail指向的位置插入数据之后,tail指向的是下一个NUll的位置,我们这个时候只要让tail %= k+1就行,而这个式子不需要加if判断,因为当这个tail在中间,图中所示位置的后一个位置(NULL)的时候,我们让tail %= k+1不影响tail的值。实现图如下所示:
bool myCircularQueueIsFull(MyCircularQueue* obj)
{
return (obj->tail+1) % (obj->k+1) == obj->front; //判断这个栈是否满了
}
bool myCircularQueue(MyCircularQueue* obj ,int value)
{
if(myCircularQueueIsFull(obj))
return false;
obj->a[obj->tail] = value;
++obj->tail;
obj->tail %= (obj->k+1);
return true;
}
删除数据操作
删除操作也同样有与插入操作类似的特殊情况:
对于上处的情况,当front在删除数据之后,指向了下一个位置,而下一个位置为空,那么他应该指向数据值为“6”的那个位置,我们同样是用front %= k+1的形式来实现front的换位。
bool myCircularQueueIsEmpty(MyCircularQueue* obj)
{
return obj->front == obj->tail; //判断队列是否为空
}
bool myCircularQueueDeQueue(MyCircularQueue* obj)
{
if(myCircularQueueIsEmpty(obj))
return false;
++obj->front;
obj->front %= (obj->k+1);
return true;
}
取队列队头的数据
题目规定这个函数如果队列为空返回-1,我们取队头的数据也就是取front指针指向的数据。
bool myCircularQueueIsEmpty(MyCircularQueue* obj)
{
return obj->front == obj->tail; //判断队列是否为空
}
int myCircularQueueFront(MyCircularQueue* obj)
{
if(myCircularQueueIsEmpty(obj))
return -1;
return obj->a[obj->front];
}
取队列队尾的数据
取队尾数据就是取tail的前一个结点的数据,但是有一个特殊情况,就是当tail指向第一个结点的时候,也就是当tail在最后一个结点插完数据,返回到第一个结点的时候,如下图所示:
这种情况下tail的前一个是数组-1的位置,这就访问错误了。这个时候我们访问的是最后一个结点的数据,也就是数组下标为k的位置。我们可以直接粗暴的if判断这个问题,如下所示:
if(obj->tail == 0)
return obj->a[obj->k];
else
return obj->a[obj->tail-1];
还可以利用“%”来实现:
int i = (obj->tail+obj->k) % (obj->k+1);
return obj->a[i];
我们拿这两种情况来理解一些这段代码。
第一种情况,tail = 2,这时候i = (2 + 4)% 5 = 1,而下标为1的数据就是tail之前的那个数据,
第二种情况,tail = 0,这时候i = (0 + 4) % 5 = 4,而下标为4的数据就是对应的最后的那个数据。
下面实现取队尾的数据的代码:
int myCirlarQueueRear(MyCircularQueue* obj)
{
if(myCircularQueueIsQmpty(obj)) //判断队列是否为空
return -1;
int i = (obj->tail+obj->k) % (obj->k+1);
return obj->a[i];
}