如何判断字符串是否括号匹配

1,025 阅读3分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第3天,点击查看活动详情

今天要学习的内容是如何判断字符串是否括号匹配。

1、题目分析

什么是括号匹配呢?让我们先了解一下括号,我们用到的有大括号{},小括号(),中括号[],这样的组合就是正确匹配的,但是如果不是上方列举的三种形式,那么就是不匹配的。
举例:
匹配:"{123[222(456)]}"
不匹配: "{([123)555]}"

2、解题思路

这道题目主要考察的是对栈的理解和使用,我们可以通过使用数组来模拟栈来进行解题。

1.jpeg

  • 延展:栈和数组的区别是什么呢?
  1. 栈是逻辑结构。它是一种理论模型,它是一种先进后出的模型,它不用考虑如何实现,不受语言的限制;
  2. 数组是一种物理结构。它受编程语言的限制,有真实的功能,具体的操作方法。
    数组可以实现栈,还有其他方式也可以实现栈,但栈无法实现数组。

我们可以通过使用数组模拟栈,通过将左括号压栈,右括号判断栈顶是否匹配,如果不匹配则返回false,如果匹配则出栈,继续向后查找。字符串查找完毕后,再判断栈的长度是否为0;

解题

function matchBrackets(str: string): boolean{
  const length = str.length;
  if(length === 0) return true; //如果字符串长度为0,那么其中不会存在括号,判断其符合括号匹配
  const stack = [];
  const leftBtackets = "{[(";
  const rightBtackets = ")]}";
  for(let i=0; i<length; i++){
    if(leftBtackets.includes(str[i])){//如果是左括号,进行入栈操作
    //if(str[i] === '{' || str[i] === '[' || str[i] === '(' ){
      stack.push(str[i])
    }else if(rightBtackets.includes(str[i])){//如果是右括号,判断其是否和栈顶的元素相匹配,
    //}else if(str[i] === '}' || str[i] === ']' || str[i] === ')'){
      const top = stack[stack.length - 1];//这里四栈顶元素
      if(ifMatch(top,str[i])){//如果匹配,那么则进行出栈操作,并继续向下进行。这里的判断可以再写一个方法
        stack.pop()
      }else{//如果不匹配,那么这个字符串就是括号不匹配的,返回false
        return false
      }
    }
  }
  if(stack.length === 0) return true;
  return false
}

function ifMatch(left:string, right:string): boolean{
  if(left === "{" && right === "}") return true;
  if(left === "[" && right === "]") return true;
  if(left === "(" && right === ")") return true;
  return false
}
功能测试
const str1 = "{44}[(12)44]"
const bol1 = matchBrackets(str1);
console.log(bol1)//true  符合预期

const str2 = "{44[(12})44]"
const bol2 = matchBrackets(str2);
console.log(bol2)//false  符合预期

const str3 = "{44[(12)}44]"
const bol3 = matchBrackets(str3);
console.log(bol3)//false  符合预期
复杂度分析

空间复杂度是O(n)

  • 这里通过改变输入变量str的增大,函数体内创建的变量stack的长度会变大(可能性比较大,不要考虑增长字符串却不添加括号),其占用的空间不是一成不变的,所以空间复杂度是O(n)
    时间复杂度是O(n)
  • 这里使用了includes,理论上includes的时间复杂度也是O(n),其外边右套了一层循环,为什么不是O(n^2)而是O(n)呢?这主要是因为虽然理论上includes的时间复杂度也是O(n),但是其遍历的内容是固定的,而且非常短leftBtackets = "{[(";rightBtackets = ")]}";所以可以将其在此处的时间复杂度默认为O(1);综合考虑其时间复杂度是O(n).如果实在不能理解,可以看看函数中注视掉的代码来替换inckudes,只是代码不如使用includes美观优雅而已。

3、总结

这道题重点在考察栈的理解与使用,这次没有做性能分析,主要是因为只提供了一种思路,而且其复杂度比较合适。另外虽然有些JS的内置方法时间复杂度可能会比较高,但也要考虑实际的使用情景。