括号匹配(栈)

题目

有效括号序列_牛客题霸_牛客网 (nowcoder.com)icon-default.png?t=N7T8https://www.nowcoder.com/practice/37548e94a270412c8b9fb85643c8ccc2?tpId=196&tqId=37083&ru=/exam/oj

c++有栈 但是C语言没有

到那时我们可以自己造

这里的代码是直接调用栈,然后调用

等于三个左括号的任意一个 我们就入栈

左括号(入栈)

右括号

取出栈顶数据,出栈并且进行匹配,这里匹配的是不匹配的情况

这里进行方向的判断

因为不匹配可以直接拿结果

栈里面还有数据意味着数量不相等

‘也就是此时进行判断 此时栈是不是为空

图解

  1. 因为每次都是左括号入栈,然后然后才有右括号的,所以我们可以来一个判断,如果入栈半天只有左括号,那么说明也就是第四种情况,直接返回fash就可以
  2. 根据栈的性质,先进先出,我们可以把输入数值的左括号入栈,因为匹配的时候,我们不能去寻找与之匹配的右括号,因为这样可能存在漏掉某一个数组里面的数组就像第三个,所以我们需要进行遍历,所有的左括号入栈,因为的对称的,所以我们可以判定哪些不是与之匹配的右括号,不是就false(这期间记得每次匹配之后,我们需要进行出栈,也就是让栈的第一个数值进行更换,因为我们已经参与匹配并且成功了)
  3. 循环结束之后此时我们进行一个判空,如果是空的说明是,满足条件的,不是空的说明是不满足条件的

代码的实现

#pragma once
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
typedef char STDataType;
typedef struct Stack {
    STDataType* _a; // 首元素的地址
    int _top; // 栈顶,初始化为0,也就是等同于size,初始化为-1,等同于下标
    int _capacity; // 容量
} Stack;
// 初始化栈
void StackInit(Stack* ps);
// 销毁栈
void StackDestroy(Stack* ps);
// 入栈
void StackPush(Stack* ps, STDataType data);
// 出栈
void StackPop(Stack* ps);
// 获取栈顶元素
STDataType StackTop(Stack* ps);
// 获取栈中有效元素个数
int StackSize(Stack* ps);
// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0
int StackEmpty(Stack* ps);
// 初始化栈
void StackInit(Stack* ps) {
    ps->_a = NULL;

    // 这里栈顶初始化为-1和初始化为0是不一样的
    //    0 0 0 0 0 0 0 0 数组
    // -1 0 0 0 0 0 0 0 0 初始化为-1
    //    0 0 0 0 0 0 0 0 初始化为0
    // 初始化为0,也就是等同于size,初始化为-1,等同于下标
    ps->_capacity = ps->_top = 0;
}
// 销毁栈
void StackDestroy(Stack* ps) {
    // 判断为不为空,判断里面有没有数值
    assert(ps && ps->_top);
    free(ps->_a);
    ps->_capacity = ps->_top = 0;
}
// 入栈
void StackPush(Stack* ps, STDataType data) {
    // 首先判断容量是多少,然后进行扩容
    if (ps->_capacity == ps->_top) {
        // 扩容
        int newapacity = ps->_capacity == 0 ? 4 : 2 * ps->_capacity;
        STDataType* tmp =
            (STDataType*)realloc(ps->_a, newapacity * sizeof(STDataType));
        if (tmp == NULL) {
            perror("StackPush:tmp:error:");
            exit(1);
        }
        // 改变容量大小,指向新空间的头第一个元素位置的地址
        ps->_capacity = newapacity;
        ps->_a = tmp;
    }
    // 插入数值
    ps->_a[ps->_top] = data;
    ps->_top++;
}
// 出栈
void StackPop(Stack* ps) {
    // 判断为不为空,判断里面有没有数值
    assert(ps && ps->_top);
    ps->_top--;
}
// 获取栈顶元素
STDataType StackTop(Stack* ps) {
    assert(ps && ps->_top);
    return ps->_a[ps->_top - 1];
}
// 获取栈中有效元素个数
int StackSize(Stack* ps) {
    assert(ps && ps->_top);
    return ps->_top - 1;
}
// 检测栈是否为空,如果为空返回非零结果(1),如果不为空返回0
int StackEmpty(Stack* ps) {
    assert(ps);
    return ps->_top == 0;
    // 所以,ps->_top == 0 这个表达式的作用是比较栈顶位置 ps->_top 是否等于 0。
    // 如果相等,说明栈为空,表达式结果为 true(在C语言中用 1 表示);
    // 如果不相等,说明栈不为空,表达式结果为 false(在C语言中用 0 表示)
}
bool isValid(char* s) {

    // 创建变量
    Stack ps;
    // 初始化变量
    StackInit(&ps);
    while (*s) {
        // 给出入栈条件,左边括号进行入栈
        if (*s == '[' || *s == '{' || *s == '(') {
            StackPush(&ps, *s);
        } else {
            if (StackEmpty(&ps)) {
                //StackDestroy(&ps);
                return false;
            }
            // 取栈顶
            STDataType ch = StackTop(&ps);
            // 判断不匹配的条件
            if (ch == '[' && *s != ']' || ch == '(' && *s != ')' ||
                ch == '{' && *s != '}') {
                //StackDestroy(&ps);
                return false;
            }
            // 出栈
            StackPop(&ps);
        }
        ++s;
    }
    // 检测栈是否为空,如果为空返回非零结果ture,如果不为空返回false
    bool ret = StackEmpty(&ps);
    return ret;
}

解释:

  1. 栈结构体定义

    • STDataType* _a:指向栈数组的指针,用于存储栈中的元素。
    • int _top:表示栈顶的位置。数组索引从0开始,栈顶位置 _top 初始化为0,表示栈为空。
    • int _capacity:栈的容量,用于存储栈中最多可以存放的元素数量。
  2. 栈操作函数

    • StackInit:初始化栈,将数组指针设置为 NULL,栈顶 _top 和容量 _capacity 都设置为0。
    • StackDestroy:释放栈数组的内存,并将栈顶和容量重置为0。
    • StackPush:入栈操作,将元素添加到栈顶,如果栈已满则先进行扩容。
    • StackPop:出栈操作,移除栈顶的元素。
    • StackTop:获取栈顶元素的值。
    • StackSize:获取栈中元素的数量。
    • StackEmpty:检查栈是否为空。
  3. 括号验证函数 isValid

    • 该函数接收一个字符数组 s 作为参数,用于验证字符串中的括号是否正确配对。
    • 在函数内部,首先创建并初始化了一个 Stack 类型的变量 ps
    • 然后,使用 while 循环遍历字符串 s
      • 如果当前字符是 [{ 或 ((左括号),则将其入栈。
      • 如果当前字符是 ]} 或 )(右括号):
        • 首先检查栈是否为空。如果栈为空,说明没有对应的左括号与之配对,返回 false
        • 否则,获取栈顶元素并与当前字符配对检查。如果配对不成功,返回 false
        • 如果配对成功,执行出栈操作。
    • 在循环结束后,检查栈是否为空。如果栈为空,则说明所有括号都正确配对,返回 true;否则返回 false
  4. 括号配对规则

    • 每种左括号([{()必须与其对应的右括号(]}))配对。
    • 括号的配对必须遵守正确的嵌套顺序。
  5. 注意事项

    • 在 StackPush 函数中,使用了 realloc 进行内存扩容。如果 realloc 失败,程序将输出错误信息并退出。
    • 在 StackDestroy 函数中,使用 assert 宏确保传入的栈指针 ps 不是 NULL,并且栈是非空的。
    • 在 isValid 函数中,没有释放局部栈 ps 的内存,因为 ps 是自动变量,其内存会在函数结束时自动释放。如果 ps 是动态分配的,那么需要在函数末尾调用 StackDestroy 并传递 ps 的地址。

检验

 换一种写法 检验能不能看懂,那就是学会了

/**
 * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
 *
 *
 * @param s string字符串
 * @return bool布尔型
 */

#pragma once
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
typedef char STDataType;
typedef struct Stack {
    STDataType* _a; // 首元素的地址
    int _top; // 栈顶,初始化为0,也就是等同于size,初始化为-1,等同于下标
    int _capacity; // 容量
} Stack;
// 初始化栈
void StackInit(Stack* ps);
// 销毁栈
void StackDestroy(Stack* ps);
// 入栈
void StackPush(Stack* ps, STDataType data);
// 出栈
void StackPop(Stack* ps);
// 获取栈顶元素
STDataType StackTop(Stack* ps);
// 获取栈中有效元素个数
int StackSize(Stack* ps);
// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0
int StackEmpty(Stack* ps);
// 初始化栈
void StackInit(Stack* ps) {
    ps->_a = NULL;

    // 这里栈顶初始化为-1和初始化为0是不一样的
    //    0 0 0 0 0 0 0 0 数组
    // -1 0 0 0 0 0 0 0 0 初始化为-1
    //    0 0 0 0 0 0 0 0 初始化为0
    // 初始化为0,也就是等同于size,初始化为-1,等同于下标
    ps->_capacity = ps->_top = 0;
}
// 销毁栈
void StackDestroy(Stack* ps) {
    // 判断为不为空,判断里面有没有数值
    assert(ps && ps->_top);
    free(ps->_a);
    ps->_capacity = ps->_top = 0;
}
// 入栈
void StackPush(Stack* ps, STDataType data) {
    // 首先判断容量是多少,然后进行扩容
    if (ps->_capacity == ps->_top) {
        // 扩容
        int newapacity = ps->_capacity == 0 ? 4 : 2 * ps->_capacity;
        STDataType* tmp =
            (STDataType*)realloc(ps->_a, newapacity * sizeof(STDataType));
        if (tmp == NULL) {
            perror("StackPush:tmp:error:");
            exit(1);
        }
        // 改变容量大小,指向新空间的头第一个元素位置的地址
        ps->_capacity = newapacity;
        ps->_a = tmp;
    }
    // 插入数值
    ps->_a[ps->_top] = data;
    ps->_top++;
}
// 出栈
void StackPop(Stack* ps) {
    // 判断为不为空,判断里面有没有数值
    assert(ps && ps->_top);
    ps->_top--;
}
// 获取栈顶元素
STDataType StackTop(Stack* ps) {
    assert(ps && ps->_top);
    return ps->_a[ps->_top - 1];
}
// 获取栈中有效元素个数
int StackSize(Stack* ps) {
    assert(ps && ps->_top);
    return ps->_top - 1;
}
// 检测栈是否为空,如果为空返回非零结果(1),如果不为空返回0
int StackEmpty(Stack* ps) {
    assert(ps);
    return ps->_top == 0;
    // 所以,ps->_top == 0 这个表达式的作用是比较栈顶位置 ps->_top 是否等于 0。
    // 如果相等,说明栈为空,表达式结果为 true(在C语言中用 1 表示);
    // 如果不相等,说明栈不为空,表达式结果为 false(在C语言中用 0 表示)
}
bool isValid(char* s ) {
    Stack ps;
    StackInit(&ps);
    while (*s) {
        //左括号入栈
        if (*s == '[' || *s == '(' || *s == '{') {
            StackPush(&ps, *s);
        }
        if (*s == ']' || *s == ')' || *s == '}') {
            //当栈为空的时候(也就是左括号没有了),但是右括号还存在的时候
            if (StackEmpty(&ps)) {
                return false;
            }
            int ret = StackTop(&ps);
            StackPop(&ps);
            if (*s == ']' && ret != '[' || *s == ')' && ret != '(' || *s == '}' && ret != '{') {
                return false;
            }

        }
        s++;
    }
    //这里是,左括号栈里面的元素没有匹配完毕,但是右括号已经匹配完毕
    if (!StackEmpty(&ps)) {
        return false;
    }
    return true;
}

解释:

栈结构和操作函数

首先,代码定义了一个 Stack 结构体和几个操作栈的函数:

  • StackInit:初始化栈,将栈顶指针 _top 设置为0,表示栈为空。
  • StackDestroy:销毁栈,释放栈内存。
  • StackPush:入栈操作,将元素压入栈顶。
  • StackPop:出栈操作,移除栈顶元素。
  • StackTop:获取栈顶元素,不移除它。
  • StackSize:获取栈中元素的数量。
  • StackEmpty:检查栈是否为空。

括号匹配函数 isValid

函数 isValid 接收一个字符串 s 作为参数,检查其中的括号是否正确匹配。它返回一个布尔值,如果括号匹配正确则返回 true,否则返回 false

  1. 初始化栈:使用 StackInit 函数初始化一个栈。

  2. 遍历字符串:使用 while 循环遍历字符串 s 中的每个字符。

  3. 处理左括号:如果当前字符是左括号('[', '(', 或 '{'),则使用 StackPush 将其压入栈。

  4. 处理右括号:如果当前字符是右括号(']', ')', 或 '}'):

    • 首先检查栈是否为空。如果为空,并且还存在右括号,说明没有对应的左括号,返回 false
    • 如果栈不为空,使用 StackTop 获取栈顶元素,并使用 StackPop 将其从栈中移除。
    • 检查栈顶元素与当前右括号是否匹配。如果不匹配,返回 false
  5. 检查栈是否为空:在字符串遍历结束后,如果栈不为空,说明存在没有匹配的左括号,返回 false

  6. 括号匹配:如果所有字符都已遍历,并且栈为空,则说明所有括号正确匹配,返回 true

代码实现的关键点

  • 使用栈来跟踪最近遇到的左括号。
  • 右括号出现时,检查栈顶的左括号是否与之匹配。
  • 使用 assert 来确保栈在使用前已正确初始化,并且在尝试访问或修改栈元素时栈不为空。

示例

对于输入字符串 "{[()]}"

  • 遇到 '{',入栈。
  • 遇到 '[',入栈。
  • 遇到 '(',入栈。
  • 遇到 ')',出栈(匹配 '(')。
  • 遇到 ']',出栈(匹配 '[')。
  • 遇到 '}', 出栈(匹配 '{')。

栈为空,所有括号正确匹配,返回 true

对于输入字符串 "{[(])}"

  • 遇到 '{',入栈。
  • 遇到 '[',入栈。
  • 遇到 '(',入栈。
  • 遇到 ']',尝试出栈,但栈顶是 '(',不匹配,返回 false

这个函数可以处理包括圆括号、方括号和花括号在内的多种括号类型。

  • 24
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值