【前端也得会算法】- 20. 有效的括号 [ 简单 ]

178 阅读3分钟

Offer 驾到,掘友接招!我正在参与2022春招系列活动-刷题打卡任务,点击查看活动详情

一、题目描述:

给定一个只包括 '(',')','{','}','[',']' 的字符串 s ,判断字符串是否有效。

有效字符串需满足:

左括号必须用相同类型的右括号闭合。 左括号必须以正确的顺序闭合。

示例

    示例 1:
    输入:s = "()"
    输出:true
    
    示例 2:
    输入:s = "()[]{}"
    输出:true
    
    示例 3:
    输入:s = "(]"
    输出:false
    
    示例 4:
    输入:s = "([)]"
    输出:false
    
    示例 5:
    输入:s = "{[]}"
    输出:true

提示:

  • 1 <= s.length <= 104
  • s 仅由括号 '()[]{}' 组成

二、题解:

方法一 暴力替换法

  • 方案。此题无非就是替换成对存在的三种括号,而且输入为字符串,所以想到了replacereplaceAll两种替换方案。

    tips: 最后实践出来两种方案效率几乎一致,replaceAll循环次数更少,但是单次循环耗时间更长,replace循环次数多,但是单次循环时间短
  • 思路。但由于不知道哪种括号在前,所以每次需要试着替换3种,不匹配也不会改变字符串。

    tips: replace不会改变原字符串,所以需要赋值改变字符串本身

    核心语句:

    s = s.replace('{}','').replace('[]','').replace('()','')
  • 循环。因为是替换的思路,所以循环也不知道具体次数,只知道大概小于等于字符串长度的一半,推荐使用while
  • 跳出循环。因为是替换的思路,所以可以根据替换前和替换后的长度是否一致决定可以跳出循环
  • 返回结果。根据跳出循环时字符串长度决定的,如果有长度,则返回false,否则返回true。

代码:

var isValid = function(s) {
    while(true){
        let _len = s.length
        s = s.replace('{}','').replace('[]','').replace('()','')
        let len = s.length
        if(_len === len){
            break
        }
    }
    if(s.length){
        return false
    }
    
    return true
};

image.png

优化

优化有几个思路。
  1. 更快出结果
  2. 少产生变量
  3. 提升计算效率

优化后代码

var isValid = function(s) {
    // 空字符串符合条件
    if (!s) {
        return true
    }
    if (s.length % 2 !== 0) {
        return false
    }
    while(true){
        if(!s.length){
            return true
        }
        let _len = s.length
        s = s.replaceAll('{}','').replaceAll('[]','').replaceAll('()','')
        let len = s.length
        if(_len === len){
            return len === 0
        }
    }
};

image.png

方法二 栈

  • 方案。本质上该题使用的是匹配对称问题,其实和堆栈一致,入栈元素和栈内元素是否匹配,匹配则出栈,否则则入栈
  • 思路。因为该题是匹配对应的括号而不是一致性,所以需要创建一个键值对对象或者二位数组的方式实现匹配

核心语句:

    const map = {
        '{':'}',
        '[':']',
        '(':')'
    }
    if(map[stack.pop()] !== item) return false
  • 原理。stack.pop() 是栈出栈的方案,如果该栈顶元素和遍历元素匹配,匹配一致则已经自动出栈,否则则不用循环了,因为已经不匹配
  • 循环。因为需要遍历每一个元素,对字符串的遍历,我选择了for...of循环
  • 结果。根据跳出循环时栈的长度决定的,如果有长度,则返回false,否则返回true。

结合方案一优化后的代码:

var isValid = function(s) {
    if (!s) {
        return true
    }
    if(s.length%2 !== 0){
        return false
    }
    let map = {
        '{':'}',
        '[':']',
        '(':')'
    }
    let stack = []
    for(let item of s){
        
        if(map[item]){
            stack.push(item)
        } else {
            if( !stack.length || map[stack.pop()] !== item){
                return false
            }
        }
    }
    return stack.length === 0
    
};

image.png

🤣基本也是战胜了暴力方案一

三、总结

  • 此题可以使用替换法栈解法两种方案,根据方案不同选择不同循环和判断是否可以跳出循环来解题。
  • 替换法使用replacereplaceAll替换成对存在的三种括号,加上while循环,判断字符串是否已经不改变了跳出循环
  • 栈解法使用栈思路,判断入栈元素和栈内元素是否匹配,匹配则出栈,否则则入栈,加上for...of循环,使用stack.pop() 取出栈顶元素,去和遍历元素匹配,匹配一致则已经自动出栈,否则则不用循环了,因为已经不匹配

文中如有错误,欢迎在评论区指正