算法学习笔记——数据结构:栈(处理括号表达式、四则运算计算器)

栈的应用场景:判断括号合法性、利用栈完成四则运算等
LeetCode 394. 字符串解码(处理括号表达式
LeetCode 224. 基本计算器(用栈实现加减运算+处理括号表达式)
LeetCode 227. 基本计算器 II(用栈实现乘除法的优先运算)

例题

LeetCode 394. 字符串解码
根据表达式写出字符串,例如s = "2[a2[c]]d",返回"accaccd"

思路:

  1. 最直接的想法:每次遇到左括号,都寻找与之匹配的右括号,整体计算表达式值
    每个[]括号内的部分视为一个表达式,solve函数每次处理一个表达式并返回结果,如果其内部还有[]表达式,则递归调用返回
    这样做的问题在于,每次要找出与'['匹配的那个']',需要用到栈,并且在递归解决内部子问题的时候,又一次的重复遍历了内部的[]表达式
  2. 如何遍历一次解决问题:
    函数定义变为:计算从当前指针i开始的第一个右括号内的值
    每次遇到[,直接调用函数解决
    每次遇到],就返回当前函数的结果
    最终,i只需遍历一次表达式,即可获得结果(i指针在所有函数调用中共享)
class Solution:
    def decodeString(self, s: str) -> str:
        """递归解法,优化效率:并不每次遇到左括号,都寻找与之匹配的右括号,整体计算值
            函数定义变为:计算从当前指针i开始的第一个右括号内的值,最终自动从最内侧的结果向上递归
            最终,i只需遍历一次表达式,即可获得结果(i指针在所有函数调用中共享)"""
        L = len(s)
        i = 0

        def solve():
            """计算从当前指针i开始的第一个右括号内的值"""
            nonlocal i
            res = ''
            while i < L:
                if s[i].isalpha():
                    # 普通字符子串,直接拼接
                    j = i
                    while j < L and s[j].isalpha():
                        j += 1
                    res += s[i:j]
                    i = j
                elif s[i].isdigit():
                    # 数字+左括号,递归计算子问题
                    j = s.find('[', i)
                    combo = int(s[i:j])
                    i = j + 1
                    res += combo * solve()
                elif s[i] == ']':
                    # 右括号,返回当前函数的stk中的值,这是某一个括号表达式的结果
                    i += 1
                    return res
                # print(stk)
            return res

        return solve()

LeetCode 726. 原子的数量
根据化学式统计其中的各个原子数量,例如"K4(ON(SO3)2)2",返回"K4N2O14S4"

思路:
类似上一题,但是这里的()后面如果为2,则原来化学式内部的所有原子数都要乘二,另外不同化学式的原子数量还要合并,因此使用计数器Counter()解决这题,合并操作用Counter() + Counter()即可方便的完成

from collections import Counter


class Solution:
    def countOfAtoms(self, formula: str) -> str:
        def solve() -> Counter:
            """处理一个表达式,例如K2,(H2O)2
            处理范围:当前的i位置到下一个与之配对的括号
            利用Counter的+运算合并多个表达式"""
            nonlocal i
            res = Counter()
            while i < L:
                if formula[i] == '(':
                    i += 1
                    res += solve()
                elif formula[i] == ')':
                    num = 0
                    while i + 1 < L and formula[i + 1].isdigit():
                        num *= 10
                        num += int(formula[i + 1])
                        i += 1
                    if num != 0:  # ()表达式后有数字下标
                        for k in res:
                            res[k] *= num
                    return res
                else:  # 单个字母
                    elem = formula[i]
                    while i + 1 < L and formula[i + 1].islower():
                        elem += formula[i + 1]
                        i += 1
                    num = 0
                    while i + 1 < L and formula[i + 1].isdigit():
                        num *= 10
                        num += int(formula[i + 1])
                        i += 1
                    res[elem] += (1 if num == 0 else num)  # 无数字下标,记一次,有下标,记录多次
                i += 1
            return res

        L = len(formula)
        i = 0
        cnt = solve()
        # print(cnt)
        ans = ''
        for k in sorted(cnt.keys()):
            ans += k
            if cnt[k] != 1:
                ans += str(cnt[k])
        return ans

LeetCode 227. 基本计算器 II
给出表达式"1+2*(2+1)",返回7

前两题不一定要用到栈,而这题的四则运算特性与栈完美切合:

  • 如何实现加减,使用栈,把数字拆分为一个个正、负数入栈,最后计算总和
  • 如何实现乘除法优先计算:遇到乘除法,直接用栈顶元素与之计算
  • 如何计算括号:每次遇到左括号,计算从当前指针i开始的第一个右括号内的值,最终从最内侧的结果向上递归(与上面两题的方式类似)
class Solution:
    def calculate(self, s: str) -> int:
        """实现四则运算和括号优先运算的计算器"""
        s = s.replace(" ", "")  # 取出所有空格
        i = 0
        L = len(s)

        def calculateSection(s):
            """1 如何实现加减,使用栈,把数字拆分为一个个正、负数入栈,最后计算总和
            2 如何实现乘除法优先计算:遇到乘除法,直接用栈顶元素与之计算
            3 如何计算括号:优化效率:并不每次遇到左括号,都寻找匹配的右括号,整体计算值
            函数定义变为:计算从当前指针i开始的第一个右括号内的值,最终自动从最内侧的结果向上递归
            最终,i只需遍历一次表达式,即可获得结果
            """
            nonlocal i, L

            stack = []  # 利用栈计算各项数字的和
            sign = '+'  # 第一个正数的符号默认为+号
            num = 0  # 当前要入栈的数字

            while i < L:
                # 下一个"数字"可能是整个括号表达式
                if s[i] == '(':
                    i += 1
                    num = calculateSection(s)  # 计算括号内的值,如果内部还有括号,递归计算
                # 正常情况的下一个数字
                while i < L and s[i].isdigit():
                    num = num * 10 + int(s[i])
                    i += 1

                # 当前数字入栈
                if sign == '+':
                    stack.append(num)
                elif sign == '-':
                    stack.append(-num)
                elif sign == '*':
                    stack.append(stack.pop() * num)
                elif sign == '/':  # 注意,3//2=1,而-3//2=-2
                    top = stack.pop()
                    result = top // num if top >= 0 else -(abs(top) // num)
                    stack.append(result)

                # 此时i位于非数字上/结尾处
                if i < L:
                    # 遇到右括号,直接返回括号内的计算结果
                    if s[i] == ')':
                        i += 1
                        break

                    # 准备计算下一个符号和数字
                    sign = s[i]
                    num = 0

                i += 1
            return sum(stack)

        return calculateSection(s)
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值