→→→→→→→→→→→→→→→→→哆啦A梦的任意门←←←←←←←←←←←←←←←←←←←
那么我们来看一下这道OJ题的具体要求叭!
如果我还没学习栈的时候,我可能基本思路就是去数左右括号的总个数了,这样肯定做不出题目的。当然今时不同往日了,我已经学会了栈这种数据结构,我们可以通过栈去实现这些括号的闭合匹配了
我们来分析一下如何运用栈:
由于第一个括号必须是左括号,所以我们就让左括号入栈,等遇到右括号,再进行出栈操作,进行匹配即可
我们再来分析一下括号不匹配的相关情况:
多出来了若干个左括号,无法完全匹配
不会多出多余的括号,但是左右括号不匹配
多出来了若干个右括号,无法完全匹配
那么以上三种情况,如何在代码中体现呢?
遍历一遍字符串,如果栈到最后还存在数据,那么说明有若干个左括号没有相对应的右括号进行匹配
遍历字符串的过程中,存在右括号与栈顶的左括号不匹配的状况
遍历字符串的过程中,遇到右括号时,我们的栈为空。
那么通过以上的分析,我们就很容易去实现我们括号的匹配了。
由于我们用C语言去写这道题,我们就需要手搓一个栈。
上一篇文章我们已经手搓过一个栈了,那么我们就传送一下叭!
→→→→→→→→→→→→→→→→→哆啦A梦的任意门←←←←←←←←←←←←←←←←←←←
相信我们都拿到了手搓的栈了,那么我们就开始进行下一步操作!
我们先创建出一个栈,并对它进行初始化。
stack stack;
StackInit(&stack);
我们去遍历整个字符串,遇到左括号,就入栈
if(*(s + i) == '('
|| *(s + i) == '['
|| *(s + i) == '{')
StackPush(&stack, *(s + i));
遇到右括号的时候就需要考虑一下了,如果我们的栈为空,那么不用说,肯定时匹配不上了,直接返回false,如果我们的栈不为空,我们就需要从栈顶出栈顶的一个左括号来和这个右括号进行匹配,如果匹配成功了就继续循环,如果匹配不上,我们就直接返回false
else
{
if(StackEmpty(&stack))
{
StackDestroy(&stack);
return false;
}
char tmp = StackTop(&stack);
StackPop(&stack);
if((tmp == '(' && *(s + i) == ')')
|| (tmp == '[' && *(s + i) == ']')
|| (tmp == '{' && *(s + i) == '}'))
{
continue;
}
else
{
StackDestroy(&stack);
return false;
}
}
那么就到了最后一步了,我们遍历完整个字符串,发现过程中没有发现任何的不匹配问题,也就是我们上面分析的三种情况的二、三种情况,那么我们还需要判断我们分析的第一种情况,第一种情况只需要检查一下我们的栈是否为空即可,如果我们的栈不是空的,那么就完美了,我们直接返回true,如果不是,我们就返回false
if(StackEmpty(&stack))
{
StackDestroy(&stack);
return true;
}
StackDestroy(&stack);
return false;
我们可能有同学也看出来了,每次返回值的时候,我们都会对栈进行销毁,这是个好习惯,有效的防止了我们的内存泄漏。
bool isValid(char * s)
{
stack stack;
StackInit(&stack);
for(int i = 0; *(s + i) != '\0'; i++)
{
if(*(s + i) == '('
|| *(s + i) == '['
|| *(s + i) == '{')
StackPush(&stack, *(s + i));
else
{
if(StackEmpty(&stack))
{
StackDestroy(&stack);
return false;
}
char tmp = StackTop(&stack);
StackPop(&stack);
if((tmp == '(' && *(s + i) == ')')
|| (tmp == '[' && *(s + i) == ']')
|| (tmp == '{' && *(s + i) == '}'))
{
continue;
}
else
{
StackDestroy(&stack);
return false;
}
}
}
if(StackEmpty(&stack))
{
StackDestroy(&stack);
return true;
}
StackDestroy(&stack);
return false;
}
那么我们来进行下一道OJ题
→→→→→→→→→→→→→→→→→哆啦A梦的任意门←←←←←←←←←←←←←←←←←←←
老样子,我们还是先来看一下我们题目的要求叭!
我们栈的数据是先进后出,后进先出,而我们队列是先进先出,后进后出。
假设我们的栈里面一次入栈了1-7的数字,我们需要一样按顺序去出栈,那么我们第一个数字就必须输出1,那么我们就必须如下图,先把1以上的其他数字入另一个栈。
那么我们就可以得到第一个数,之后我们可以发现,另一个栈所呈现数据的顺序就是我们需要的顺序,那么我们就遍历出栈即可。
我们还是需要手搓一个栈,我相信很多小伙伴都不想往上翻或退出去再找一次,那么,上任意门
→→→→→→→→→→→→→→→→→哆啦A梦的任意门←←←←←←←←←←←←←←←←←←←
因为我们需要创建两个栈,所以我是不是可以用一个结构体来表示我的两个栈?
typedef struct {
int stackInTop, stackOutTop;
int stackIn[100], stackOut[100];
} MyQueue;
该结构体存放了我们输出以及输入的两个栈,还有两个栈的栈顶。
当然,我们的栈还是需要初始化的
MyQueue* myQueueCreate()
{
MyQueue* queue = (MyQueue*)malloc(sizeof(MyQueue));
if(queue == NULL)
{
perror("malloc fail");
return;
}
queue->stackInTop = 0;
queue->stackOutTop = 0;
return queue;
}
一定要记得把两个栈顶置为0.
void myQueuePush(MyQueue* obj, int x)
{
obj->stackIn[(obj->stackInTop)++] = x;
}
先把元素压入第一个栈,然后让我们的栈顶+1
出栈!!!
int myQueuePop(MyQueue* obj) {
int stackInTop = obj->stackInTop;
int stackOutTop = obj->stackOutTop;
//若输出栈为空
if(stackOutTop == 0) {
while(stackInTop > 0) {
obj->stackOut[stackOutTop++] = obj->stackIn[--stackInTop];
}
}
int top = obj->stackOut[--stackOutTop];
while(stackOutTop > 0) {
obj->stackIn[stackInTop++] = obj->stackOut[--stackOutTop];
}
obj->stackInTop = stackInTop;
obj->stackOutTop = stackOutTop;
return top;
}
如果输出栈为空,而且第一个栈有元素,我们就需要将第一个栈的元素倒腾到第二个栈当中,
还要把栈顶元素进行保存。
返回栈底元素
int myQueuePeek(MyQueue* obj) {
return obj->stackIn[0];
}
判断队列是否为空
bool myQueueEmpty(MyQueue* obj) {
return obj->stackInTop == 0 && obj->stackOutTop == 0;
}
只需要判断两个栈顶是否为0就ok啦
将栈顶指针置为0
void myQueueFree(MyQueue* obj) {
obj->stackInTop = 0;
obj->stackOutTop = 0;
}
typedef struct {
int stackInTop, stackOutTop;
int stackIn[100], stackOut[100];
} MyQueue;
MyQueue* myQueueCreate()
{
MyQueue* queue = (MyQueue*)malloc(sizeof(MyQueue));
if(queue == NULL)
{
perror("malloc fail");
return;
}
queue->stackInTop = 0;
queue->stackOutTop = 0;
return queue;
}
void myQueuePush(MyQueue* obj, int x)
{
obj->stackIn[(obj->stackInTop)++] = x;
}
int myQueuePop(MyQueue* obj) {
int stackInTop = obj->stackInTop;
int stackOutTop = obj->stackOutTop;
//若输出栈为空
if(stackOutTop == 0) {
while(stackInTop > 0) {
obj->stackOut[stackOutTop++] = obj->stackIn[--stackInTop];
}
}
int top = obj->stackOut[--stackOutTop];
while(stackOutTop > 0) {
obj->stackIn[stackInTop++] = obj->stackOut[--stackOutTop];
}
obj->stackInTop = stackInTop;
obj->stackOutTop = stackOutTop;
return top;
}
int myQueuePeek(MyQueue* obj) {
return obj->stackIn[0];
}
bool myQueueEmpty(MyQueue* obj) {
return obj->stackInTop == 0 && obj->stackOutTop == 0;
}
void myQueueFree(MyQueue* obj) {
obj->stackInTop = 0;
obj->stackOutTop = 0;
}
→→→→→→→→→→→→→→→→→哆啦A梦的任意门←←←←←←←←←←←←←←←←←←←
还是先来看一下题目的要求叭!
还是先从我们两种数据结构的性质进行分析,栈是先进后出,后进先出,而队列是先进先出,后进后出。
根据以上性质,还有上一道用栈实现队列的思路,我们也大概能分析出来。
还是一样的思路,一个队列用于输出我们的元素,一个元素用来备份我们的元素。
来看下面的一组图:
我们发现了,这个思路虽然和我们上面的那题虽然像,但是,好像并没有那么简单,我们刚做完的那道题,只需要倒腾一次,这元素的排列就符合我们的预期了,但是这道题就不一样,我们需要倒腾一次才能输出一次,但是这并不影响我们的做题。
那么就马上到我们的代码实现叭!
当然,还是先把两个队列封装进一个结构体当中
typedef struct
{
Queue q1;
Queue q2;
} MyStack;
我们栈的创建,因为这个栈的创建是通过初始化两个队列,所以我们可以看到对两个队列进行初始化
MyStack* myStackCreate()
{
MyStack* mystack = (MyStack*)malloc(sizeof(MyStack));
assert(mystack);
QueueInit(&mystack->q1);
QueueInit(&mystack->q2);
return mystack;
}
那么来到元素的压栈
void myStackPush(MyStack* obj, int x)
{
assert(obj);
if(!QueueEmpty(&obj->q1))
{
QueuePush(&obj->q1, x);
}
else
{
QueuePush(&obj->q2, x);
}
}
我们也能分析的出,一个队列是用来备份我们的数据的,另一个用来对元素进行输出。
所以我们只对一个有数据的队列进行压栈。
到出栈啦!
int myStackPop(MyStack* obj)
{
assert(obj);
Queue* EmptyQueue = &obj->q1;
Queue* NonEmptyQueue = &obj->q2;
if(!QueueEmpty(EmptyQueue))
{
EmptyQueue = &obj->q2;
NonEmptyQueue = &obj->q1;
}
while(QueueSize(NonEmptyQueue) > 1)
{
QueuePush(EmptyQueue, QueueTop(NonEmptyQueue));
QueuePop(NonEmptyQueue);
}
int top = QueueTop(NonEmptyQueue);
QueuePop(NonEmptyQueue);
return top;
}
我们默认第一个队列是空的嘛,那么如果不是空的,那么另一个就是空的咯~
所以我们要找到那个空的队列,也就是上一次的输出队列,让它来当一次存储数据的队列,然后上一次用来存储数据的队列对最后一个元素进行输出即可!
获取我们的栈顶元素数据
int myStackTop(MyStack* obj)
{
assert(obj);
assert(!myStackEmpty(obj));
if(!QueueEmpty(&obj->q1))
{
return QueueBack(&obj->q1);
}
else
{
return QueueBack(&obj->q2);
}
}
当然,还是要先找到哪个不是空的队列
检查栈是否为空
bool myStackEmpty(MyStack* obj)
{
assert(obj);
return QueueEmpty(&obj->q1) && QueueEmpty(&obj->q2);
}
如果栈是空的,那么两个队列都是空的。
将该栈进行销毁
void myStackFree(MyStack* obj)
{
assert(obj);
QueueDestroy(&obj->q1);
QueueDestroy(&obj->q2);
free(obj);
obj = NULL;
}
养成编程的好习惯,先从释放内存做起,记得一定要先释放那两个队列,再去释放这个栈,如果先释放这个栈,我们再去释放那两个队列的时候是找不到那两个队列的
typedef struct QNode//队列节点
{
int data;
struct QNode* next;
}QNode;
typedef struct Queue//队列
{
QNode* front;
QNode* tail;
}Queue;
//栈是否为空
bool myStackEmpty(MyStack* obj);
//初始化
void QueueInit(Queue* queue);
//队列是否为空
bool QueueEmpty(Queue* queue);
//进队
void QueuePush(Queue* queue, int x);
//出队
void QueuePop(Queue* queue);
//队列头部元素
int QueueTop(Queue* queue);
//队列尾部元素
int QueueBack(Queue* queue);
//队列有效元素个数
int QueueSize(Queue* queue);
//销毁队列
void QueueDestroy(Queue* queue);
void QueueInit(Queue* queue)//初始化
{
assert(queue);
queue->front = NULL;
queue->tail = NULL;
}
//队列是否为空
bool QueueEmpty(Queue* queue)
{
assert(queue);
return queue->tail == NULL;
}
//进队
void QueuePush(Queue* queue, int x)
{
assert(queue);
QNode* newnode = (QNode*)malloc(sizeof(QNode));
assert(newnode);
newnode->data = x;
newnode->next = NULL;
if(queue->tail == NULL)
{
queue->front = queue->tail = newnode;
}
else
{
queue->tail->next = newnode;
queue->tail = newnode;
}
}
//出队
void QueuePop(Queue* queue)
{
assert(queue);
QNode* next = queue->front->next;
free(queue->front);
queue->front = next;
if(queue->front == NULL)
{
queue->tail = NULL;
}
}
//队列头部元素
int QueueTop(Queue* queue)
{
assert(queue);
assert(queue->front);
return queue->front->data;
}
//队列尾部元素
int QueueBack(Queue* queue)
{
assert(queue);
assert(queue->tail);
return queue->tail->data;
}
//队列有效元素个数
int QueueSize(Queue* queue)
{
assert(queue);
int size = 0;
QNode* cur = queue->front;
while(cur != NULL)
{
++size;
cur = cur->next;
}
return size;
}
//销毁队列
void QueueDestroy(Queue* queue)
{
assert(queue);
QNode* next = queue->front;
while(next != NULL)
{
next = queue->front->next;
free(queue->front);
queue->front = next;
}
queue->tail = NULL;
}
typedef struct
{
Queue q1;
Queue q2;
} MyStack;
//创建栈
MyStack* myStackCreate()
{
MyStack* mystack = (MyStack*)malloc(sizeof(MyStack));
assert(mystack);
QueueInit(&mystack->q1);
QueueInit(&mystack->q2);
return mystack;
}
//压栈
void myStackPush(MyStack* obj, int x)
{
assert(obj);
if(!QueueEmpty(&obj->q1))
{
QueuePush(&obj->q1, x);
}
else
{
QueuePush(&obj->q2, x);
}
}
//出栈
int myStackPop(MyStack* obj)
{
assert(obj);
Queue* EmptyQueue = &obj->q1;
Queue* NonEmptyQueue = &obj->q2;
if(!QueueEmpty(EmptyQueue))
{
EmptyQueue = &obj->q2;
NonEmptyQueue = &obj->q1;
}
while(QueueSize(NonEmptyQueue) > 1)
{
QueuePush(EmptyQueue, QueueTop(NonEmptyQueue));
QueuePop(NonEmptyQueue);
}
int top = QueueTop(NonEmptyQueue);
QueuePop(NonEmptyQueue);
return top;
}
//栈顶元素
int myStackTop(MyStack* obj)
{
assert(obj);
assert(!myStackEmpty(obj));
if(!QueueEmpty(&obj->q1))
{
return QueueBack(&obj->q1);
}
else
{
return QueueBack(&obj->q2);
}
}
//栈是否为空
bool myStackEmpty(MyStack* obj)
{
assert(obj);
return QueueEmpty(&obj->q1) && QueueEmpty(&obj->q2);
}
//销毁栈
void myStackFree(MyStack* obj)
{
assert(obj);
QueueDestroy(&obj->q1);
QueueDestroy(&obj->q2);
free(obj);
obj = NULL;
}