片头
嗨! 小伙伴们,大家好! 我们又见面啦! 今天我们来一起尝试一下这道题目---有效的括号,准备好了吗? 我们开始咯!
说实话,我刚开始做这道题的时候也是一脸懵,怎么进行括号匹配呢?
别慌,我们一起画个图,分析分析括号匹配的过程~
如下图所示,上方表示一个字符串数组,存放不同种类的左括号和右括号,用一个指针依次遍历字符串中的每一个元素
如果是左括号,则入栈,如果是右括号,则栈顶元素出栈和字符串数组中的元素进行匹配。
第一次:
左括号,则入栈
第二次:
左括号,则入栈
第三次:
左括号,则入栈
第四次:
此时指针指向的元素为右括号,应该出栈
第五次:
左括号,则入栈
第六次:
右括号,则出栈
第七次:
右括号,则出栈
第八次:
右括号,则出栈
第九次:
左括号,则入栈
第十次:
右括号,则出栈
第十一次:
左括号,则入栈
第十二次:
右括号,则出栈
好的,这道题的执行过程我们大致分析清楚了,怎么实现呢?
我们可以利用栈来解答这道题
先定义Stack.h文件
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int ElemType;
typedef struct Stack {
ElemType* arr; //数组
int top; //栈顶
int capacity; //栈的容量
}Stack;
//初始化栈
void StackInit(Stack* ps);
//入栈
void StackPush(Stack* ps,ElemType data);
//出栈
void StackPop(Stack* ps);
//获取栈顶元素
ElemType StackTop(Stack* ps);
//获取栈中有效元素的个数
int StackSize(Stack* ps);
//检测栈是否为空,如果为空返回非零结果,如果不为空返回0
int StackEmpty(Stack* ps);
//销毁栈
void StackDestroy(Stack* ps);
再定义Stack.c文件
#include"Stack.h"
//初始化栈
void StackInit(Stack* ps) {
assert(ps);
ps->arr = NULL;
ps->capacity = 0;
ps->top = 0;
}
//入栈
void StackPush(Stack* ps, ElemType data) {
assert(ps);
//扩容
if (ps->capacity == ps->top) {
int newCapacity = ps->capacity == 0 ? 4 : 2 * (ps->capacity);
ElemType* temp =(ElemType*) realloc(ps->arr, newCapacity * sizeof(ElemType));
if (temp == NULL) {
perror("realloc fail!\n");
exit(1);
}
ps->arr = temp;
ps->capacity = newCapacity;
}
ps->arr[ps->top] = data;
ps->top++;
}
//出栈
void StackPop(Stack* ps) {
assert(ps);
assert(!StackEmpty(ps));
ps->top--;
}
//获取栈顶元素
ElemType StackTop(Stack* ps) {
assert(ps);
assert(!StackEmpty(ps));
int ret = ps->arr[ps->top - 1];
return ret;
}
//获取栈中有效元素的个数
int StackSize(Stack* ps) {
assert(ps);
return ps->top;
}
//检测栈是否为空,如果为空返回非零结果,如果不为空返回0
int StackEmpty(Stack* ps) {
assert(ps);
return ps->top == 0; //如果top为0,说明栈为空,返回1
}
//销毁栈
void StackDestroy(Stack* ps) {
assert(ps);
if (ps->arr) {
free(ps->arr);
ps->arr = NULL;
}
ps->capacity = 0;
ps->top = 0;
}
最后我们再来测试一下,定义test.c文件
#include"Stack.h"
#include<stdbool.h>
int main() {
Stack st; //定义一个栈
StackInit(&st); //初始化这个栈
StackPush(&st, 1); //将"1"放入栈内
StackPush(&st, 2); //将"2"放入栈内
StackPush(&st, 3); //将"3"放入栈内
StackPush(&st, 4); //将"4"放入栈内
StackPush(&st, 5); //将"5"放入栈内
while (!StackEmpty(&st)) { //如果栈不为空,那么获取栈中的元素
int top = StackTop(&st); //获取栈顶元素
printf("%d ", top); //打印栈顶元素
StackPop(&st); //将栈顶元素删除
}
StackDestroy(&st); //销毁栈
return 0;
}
运行结果为:
5 4 3 2 1
OK啦,我们的栈就实现完毕! 接下来我们就可以将它套用到题目中去~
我们刚写了一个栈,直接把 Stack.h 和 Stack.c 复制粘贴过去,把头文件删掉, 再把 typedef int ElemType 改成 typedef char ElemType;
部分代码如下:
typedef char ElemType; //一定是 char, 不要弄错了,题目要求是字符串数组
typedef struct Stack {
ElemType* arr; //数组
int top; //栈顶
int capacity; //栈的容量
}Stack;
//初始化栈
void StackInit(Stack* ps);
//入栈
void StackPush(Stack* ps,ElemType data);
//出栈
void StackPop(Stack* ps);
//获取栈顶元素
ElemType StackTop(Stack* ps);
//获取栈中有效元素的个数
int StackSize(Stack* ps);
//检测栈是否为空,如果为空返回非零结果,如果不为空返回0
int StackEmpty(Stack* ps);
//销毁栈
void StackDestroy(Stack* ps);
//初始化栈
void StackInit(Stack* ps) {
ps->arr = NULL;
ps->capacity = 0;
ps->top = 0;
}
//入栈
void StackPush(Stack* ps, ElemType data) {
//扩容
if (ps->capacity == ps->top) {
int newCapacity = ps->capacity == 0 ? 4 : 2 * (ps->capacity);
ElemType* temp =(ElemType*) realloc(ps->arr, newCapacity * sizeof(ElemType));
if (temp == NULL) {
perror("realloc fail!\n");
exit(1);
}
ps->arr = temp;
ps->capacity = newCapacity;
}
ps->arr[ps->top] = data; //插入数据
ps->top++; //栈顶向上移动一个单位
}
//出栈
void StackPop(Stack* ps) {
assert(ps);
assert(!StackEmpty(ps)); //栈不能为空
ps->top--;
}
//获取栈顶元素
ElemType StackTop(Stack* ps) {
assert(ps);
assert(!StackEmpty(ps));
//不改变top指针,只是获取栈顶元素
int ret = ps->arr[ps->top - 1];
return ret;
}
//获取栈中有效元素的个数
int StackSize(Stack* ps) {
assert(ps);
return ps->top; //top恰好是元素的个数
}
//检测栈是否为空,如果为空返回非零结果,如果不为空返回0
int StackEmpty(Stack* ps) {
return ps->top == 0;
}
//销毁栈
void StackDestroy(Stack* ps) {
assert(ps);
if (ps->arr) {
free(ps->arr);
ps->arr = NULL;
}
ps->capacity = 0;
ps->top = 0;
}
接下来就是重头戏咯! 小伙伴们可要看仔细咯!
第一步,我们先定义一个栈,然后将这个栈初始化
Stack st; //定义一个栈
StackInit(&st); //初始化这个栈
第二步,我们用题目所给的条件: 该方法传递过来的形参s, 类型为char* ,意味着s是一个指向字符串数组的指针,可以遍历整个字符串数组, 因此,当s指针遍历到字符串数组的结束符'\0'的时候,退出循环。
和刚刚我们分析的思路一样,当s指针遍历到左括号时,则该元素入栈; 当s指针遍历到右括号时,将栈里面的元素出栈,看是否匹配; 如果匹配不成功,直接返回false; 接着,s指针继续指向下一个元素。
这里有一个特别容易被忽略的一个点: 当栈为空,且遇到右括号,栈里面没有左括号,销毁栈,返回false
这个部分的代码如下:
//当*s不等于'\0'的时候,进入while循环
while(*s){
if(*s == '{' || *s == '[' || *s == '('){
//将该元素放入栈内
StackPush(&st,*s);
}else{
//当栈为空,且遇到右括号,栈里面没有左括号,返回false
if(StackEmpty(&st)){
//销毁栈,防止内存泄漏
StackDestroy(&st);
return false;
}
int top = StackTop(&st); //获取栈顶元素
StackPop(&st); //将栈顶元素删除
//将栈顶元素和*s进行匹配
//如果出栈元素是'(',但是此时的*s不是')',说明不匹配,返回false
//如果出栈元素是'[',但是此时的*s不是']',说明不匹配,返回false
//如果出栈元素是'{',但是此时的*s不是'}',说明不匹配,返回false
if(top == '(' && *s != ')'
|| top == '[' && *s != ']'
|| top == '{' && *s != '}')
{
//销毁栈,防止内存泄漏
StackDestroy(&st);
return false;
}
}
s++; //s指针继续指向下一个元素
}
哈哈哈,屏幕前的你以为这样代码就写完了吗? 肯定还没有! 因为还有一种情况我们木有考虑到~
那就是当*s已经走到'\0',但是栈不是空,说明栈里面还有左括号,不合题意, 此时StackEmpty返回false ; 如果栈为空,返回true
部分代码如下:
bool flag = StackEmpty(&st); //用bool类型的flag变量来接受StackEmpty函数的返回值
StackDestroy(&st); //将栈销毁,归还给系统内存
return flag; //将结果返回
好啦! 这道题被我们解决啦,整体代码如下:
typedef char ElemType;
typedef struct Stack {
ElemType* arr; //数组
int top; //栈顶
int capacity; //栈的容量
}Stack;
//初始化栈
void StackInit(Stack* ps);
//入栈
void StackPush(Stack* ps,ElemType data);
//出栈
void StackPop(Stack* ps);
//获取栈顶元素
ElemType StackTop(Stack* ps);
//获取栈中有效元素的个数
int StackSize(Stack* ps);
//检测栈是否为空,如果为空返回非零结果,如果不为空返回0
int StackEmpty(Stack* ps);
//销毁栈
void StackDestroy(Stack* ps);
//初始化栈
void StackInit(Stack* ps) {
ps->arr = NULL;
ps->capacity = 0;
ps->top = 0;
}
//入栈
void StackPush(Stack* ps, ElemType data) {
//扩容
if (ps->capacity == ps->top) {
int newCapacity = ps->capacity == 0 ? 4 : 2 * (ps->capacity);
ElemType* temp =(ElemType*) realloc(ps->arr, newCapacity * sizeof(ElemType));
if (temp == NULL) {
perror("realloc fail!\n");
exit(1);
}
ps->arr = temp;
ps->capacity = newCapacity;
}
ps->arr[ps->top] = data; //插入数据
ps->top++; //栈顶向上移动一个单位
}
//出栈
void StackPop(Stack* ps) {
assert(ps);
assert(!StackEmpty(ps));
ps->top--;
}
//获取栈顶元素
ElemType StackTop(Stack* ps) {
assert(ps);
assert(!StackEmpty(ps));
//不改变top指针,只是获取栈顶元素
int ret = ps->arr[ps->top - 1];
return ret;
}
//获取栈中有效元素的个数
int StackSize(Stack* ps) {
assert(ps);
return ps->top; //top恰好是元素的个数
}
//检测栈是否为空,如果为空返回非零结果,如果不为空返回0
int StackEmpty(Stack* ps) {
return ps->top == 0;
}
//销毁栈
void StackDestroy(Stack* ps) {
assert(ps);
if (ps->arr) {
free(ps->arr);
ps->arr = NULL;
}
ps->capacity = 0;
ps->top = 0;
}
bool isValid(char* s) {
Stack st; //定义一个栈
StackInit(&st); //初始化这个栈
//当*s不等于'\0'的时候,进入while循环
while(*s){
if(*s == '{' || *s == '[' || *s == '('){
//将该元素放入栈内
StackPush(&st,*s);
}else{
//当栈为空,且遇到右括号,栈里面没有左括号,返回false
if(StackEmpty(&st)){
//销毁栈,防止内存泄漏
StackDestroy(&st);
return false;
}
int top = StackTop(&st); //获取栈顶元素
StackPop(&st); //将栈顶元素删除
//将栈顶元素和*s进行匹配
//如果出栈元素是'(',但是此时的*s不是')',说明不匹配,返回false
//如果出栈元素是'[',但是此时的*s不是']',说明不匹配,返回false
//如果出栈元素是'{',但是此时的*s不是'}',说明不匹配,返回false
if(top == '(' && *s != ')'
|| top == '[' && *s != ']'
|| top == '{' && *s != '}')
{
//销毁栈,防止内存泄漏
StackDestroy(&st);
return false;
}
}
s++; //s指针继续指向下一个元素
}
bool flag = StackEmpty(&st); //用bool类型的flag变量来接受StackEmpty函数的返回值
StackDestroy(&st); //将栈销毁,归还给系统内存
return flag; //将结果返回
}
片尾
今天我们学习了一道OJ题---有效的括号,里面包含了栈的一些基础知识,希望看完这篇文章能对友友们有所帮助 ! ! !
求点赞收藏加关注 ! ! !
谢谢大家 ! ! !