Q26-code224-基本计算器 && Q27-code20- 有效的括号

50 阅读3分钟

Q26-code224-基本计算器

实现思路

1 双栈实现

1 双栈设计:

  • 数字栈:存储所有数字
  • 操作符栈:存储运算符和括号

2 核心思想:

  • 遇到数字直接入栈
  • 遇到运算符时,比较优先级,决定是否计算
  • 遇到括号时特殊处理
  • 保持较高优先级的运算符先计算

3 优先级处理:

  • 括号具有最高优先级
  • 乘除 > 加减
  • 同级运算符从左到右计算

4 统一处理负数

  • 将负数统一转换为减法运算
  • -n 转换为 0-n

参考文档

01- 方法1直接参考文档

代码实现

1 方法1: 双栈实现 时间复杂度 O(n) 空间复杂度:O(n)

function calculate(s: string): number {
  const opes = [], nums = [0];
  // 隐藏的优选级:()括号 的优先级最高
  const opePriority = { "+": 1, "-": 1, "*": 2, "/": 2 };
  let lastChar = "";

  // 替换掉所有空格
  s = s.replace(/\s/g, "");
  // 遍历字符串
  for (let char of s) {
    // 是左括号
    if (char === "(") {
      opes.push(char);
    } else if (/\d/.test(char)) {
      // 易错点1: 是数字类型的字符串,如果上一个也是数字,那么需要和上一个数字拼接起来
      if (/\d/.test(lastChar)) {
        nums[nums.length - 1] = nums.at(-1) * 10 + +char;
      } else {
        nums.push(+char);
      }
    } else if (char === ")") {
      // 是右括号:优先级最高,说明需要进行运算,直到运行到左括号为止
      while (opes.at(-1) !== "(") getCal(nums, opes);
      // 运算完成后,让左括号出栈
      opes.pop();
    } else {
      // 说明是负号 或者 运算操作符
      // 先处理是负号的情况:前面的一位是 (、+、-
      // 不包含 * 和 /,是因为数学规范里, * 和 / 后面的负数需要用括号包起来,所以这里无需考虑
      if (["(", "+", "-"].includes(lastChar)) nums.push(0);

      // 易错点2: 如果目前操作栈顶的优先级 >= 当前的运算符,那么就需要先进行运算
      // 这里需要注意可能之前有多个运算符优先级都 >= 当前的运算符,所以需要用 while循环
      while ((opePriority[opes.at(-1)] ?? -1) >= opePriority[char]) {
        getCal(nums, opes);
      }
      // 存入当前运算符
      opes.push(char);
    }
    lastChar = char;
  }
  // 易错点4: 当遍历完字符串后,可能栈中还有运算符,此时 需要进行剩余值计算
  while (opes.length) getCal(nums, opes);
  return nums.at(-1);
};

function getCal(nums, opes) {
  const [v2, v1] = [nums.pop(), nums.pop()];
  const ope = opes.pop();
  const fnMap = {
    "+": (a, b) => a + b,
    "-": (a, b) => a - b,
    "*": (a, b) => a * b,
    "/": (a, b) => ~~(a / b),
  };
  const fn = fnMap[ope];
  nums.push(fn(v1, v2));
}

code20- 有效的括号

实现思路

方法1: map + 栈

  • 需要注意 只有遇到false才能提前返回,为true还得继续查看后续字符
  • 注意 最后还需要检查栈内是否有 多余的不成对的左字符

参考文档

01-官方实现

代码实现

1 方法1: map + 栈 时间复杂度: O(n); 空间复杂度(n)

function isValid(s: string): boolean {
  let st = [];
  const pairs = new Map([
    ['{', '}'],
    ['(', ')'],
    ['[', ']'],
  ]);
  for (let char of s) {
    if (pairs.has(char)) {
      st.push(char);
    } else {
      // 说明当前字符char是右字符串,判断closeTag和char是否相等
      const closeTag = pairs.get(st.pop());
       // 易错点1: 不相等时可以提前返回,但是相等时需要继续处理下一个字符
      if (char !== closeTag) return false;
    }
  }
  // 易错点2: 防止有多余的左字符
  return st.length === 0;
}