代码随想录算法训练营第十一天|20. 有效的括号、1047. 删除字符串中的所有相邻重复项、150. 逆波兰表达式求值

文章讲述了如何使用栈数据结构解决有效括号匹配问题,以及将逆波兰表达式转换为后缀表达式进行计算。主要涉及栈的使用、匹配逻辑和两种不同场景下的字符串处理算法。
摘要由CSDN通过智能技术生成

20. 有效的括号

思路:

由于栈结构的特殊性,非常适合做对称匹配类的题目。

首先要弄清楚,字符串里的括号不匹配有几种情况:

1.第一种情况,字符串里左方向的括号多余了 ,所以不匹配。

2.第二种情况,括号没有多余,但是 括号的类型没有匹配上。

3.第三种情况,字符串里右方向的括号多余了,所以不匹配。

第一种情况:已经遍历完了字符串,但是栈不为空,说明有相应的左括号没有右括号来匹配,所以return false

第二种情况:遍历字符串匹配的过程中,发现栈里没有要匹配的字符。所以return false

第三种情况:遍历字符串匹配的过程中,栈已经为空了,没有匹配的字符了,说明右括号没有找到对应的左括号return false

那么什么时候说明左括号和右括号全都匹配了呢,就是字符串遍历完之后,栈是空的,就说明全都匹配了。

代码:

class Solution:
    def isValid(self, s: str) -> bool:
        stack=[] # 定义一个栈

        if len(s)%2==1: # 剪枝,如果字符串长度为奇数,直接返回False
            return False

        for i in s:
            if i=='(': # 如果遇到左括号,就把对应的右括号添加到栈中
                stack.append(')')
            elif i == '[':
                stack.append(']')
            elif i == '{':
                stack.append('}')
            elif not stack: # 遇到右括号,如果栈为空,说明右括号没有对应的左括号,返回False
                return False    
            elif stack[-1] != i: # 遇到右括号,如果栈顶元素不等于当前遍历的括号,返回False
                return False
            else: # 遇到右括号,如果栈顶元素等于当前遍历的括号,弹出栈顶元素
                stack.pop()

        if stack: # 遍历完字符串,如果栈不为空返回False,说明有左括号没有对应的右括号
            return False 
        else:
            return True
  • 时间复杂度: O(n)
  • 空间复杂度: O(n)

1047. 删除字符串中的所有相邻重复项

思路:

本题要删除相邻相同元素,相对于20. 有效的括号来说其实也是匹配问题,20. 有效的括号是匹配左右括号,本题是匹配相邻元素,最后都是做消除的操作。

本题也是用栈来解决的经典题目。

我们在删除相邻重复项的时候,其实就是要知道当前遍历的这个元素,我们在前一位是不是遍历过一样数值的元素,那么如何记录前面遍历过的元素呢?

所以就是用栈来存放,那么栈的目的,就是存放遍历过的元素,当遍历当前的这个元素的时候,去栈里看一下我们是不是遍历过相同数值的相邻元素。然后再去做对应的消除操作。

代码:

class Solution:
    def removeDuplicates(self, s: str) -> str:
        stack=[]
        for i in s:
            if stack and i==stack[-1]:
                stack.pop()
            else:
                stack.append(i)

        return "".join(stack)
  • 时间复杂度: O(n)
  • 空间复杂度: O(n)

150. 逆波兰表达式求值

逆波兰表达式概念:

我们日常的运算表达式通常是如下形式,这种成为中缀表达式,也就是运算符在运算数的中间。这种表达式人类很容易识别,并根据其进行计算,但计算机识别这种表达式非常困难。

a + b * (c - d) + e/f

因此,1920年,波兰科学家扬·武卡谢维奇(Jan ukasiewicz)发明了一种不需要括号的计算表达式的表示法将操作符号写在操作数之前,也就是前缀表达式,即波兰式(Polish Notation, PN)。上述中缀表达式转换为波兰表达式的格式如下: 

+a+*b-cd/ef

从上面表达式可以看出,运算符在2个运算数的前面,因此波兰表达式也称为前缀表达式。为了便于理解,我们给出一个具体的实例,这个实例将上面的字母换成具体的数字(1 + 2 * (4 - 3) + 6/2),这个结果很容易看出来,也就是1+2*1+3 = 6。然后我们看一下波兰表达式的表示形式及运算过程。 

+1+*2-4 3/ 6 2 // 从右向左扫描,当遇到运算符时计算其最近的右侧2个运算数
+1+*2-4 3 3    // 先计算最右侧的数据,也就是 6/2=3
+1+*2 1 3      // 同理,4-3 = 1
+1+2 3         // 同理,2*1= 2
+1+5  
6

波兰表达式也成为前缀表达式,而逆波兰表达式则成为后缀表达式,对比可以猜出来运算符在运算数后面的表达式就是逆波兰表达式。上述表达式如果采用逆波兰表达式则如下:

1 2 4 3 -*+ 6 2 / + // 计算方式正好相反,也就是从左向右扫描
1 2 1 *+ 6 2 / +
1 2 + 6 2 / +
3 6 2 / +
3 3 +
6

思路:

其实逆波兰表达式相当于是二叉树中的后序遍历。 大家可以把运算符作为中间节点,按照后序遍历的规则画出一个二叉树。但我们没有必要从二叉树的角度去解决这个问题,只要知道逆波兰表达式是用后序遍历的方式把二叉树序列化了,就可以了。

在进一步看,本题中每一个子表达式要得出一个结果,然后拿这个结果再进行运算,那么这岂不就是一个相邻字符串消除的过程,和1047.删除字符串中的所有相邻重复项 中的对对碰游戏是不是就非常像了。只不过本题不要相邻元素做消除了,而是做运算!

代码:

版本一

class Solution:
    def evalRPN(self, tokens: List[str]) -> int:
        stack=[]
        for i in tokens:
            if i == "+":
                num1=stack.pop()
                num2=stack.pop()
                stack.append(int(num2+num1))
            elif i == "-":
                num1=stack.pop()
                num2=stack.pop()
                stack.append(int(num2-num1))
            elif i == "*":
                num1=stack.pop()
                num2=stack.pop()
                stack.append(int(num2*num1))
            elif i == "/":
                num1=stack.pop()
                num2=stack.pop()
                stack.append(int(num2/num1))
            else:
                stack.append(int(i))
        
        return int(stack.pop())
  • 时间复杂度: O(n)
  • 空间复杂度: O(n)

版本二(使用eval()函数

class Solution:
    def evalRPN(self, tokens: List[str]) -> int:
        stack=[]
        for i in tokens:
            if i in ["+","-","*","/"]:
                num1=stack.pop()
                num2=stack.pop()
                stack.append(int(eval(f'{num2} {i} {num1}')))
            else:
                stack.append(int(i))
        
        return int(stack.pop())

 补充:

eval是Python的一个内置函数,功能十分强大,这个函数的作用是,返回传入字符串的表达式的结果。就是说:将字符串当成有效的表达式来求值并返回计算结果。(就是去掉"")

eval函数实现list、dict、tuple与str之间的转化,同样str函数把list,dict,tuple转为为字符串

a = 1
b = 2
c = eval("a+b")
print(c)

#输出为3

  • 53
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
第二十二天的算法训练主要涵盖了Leetcode题目的三道题目,分别是Leetcode 28 "Find the Index of the First Occurrence in a String",Leetcode 977 "有序数组的平方",和Leetcode 209 "长度最小的子数组"。 首先是Leetcode 28题,题目要在给定的字符串找到第一个出现的字符的索引。思路是使用双指针来遍历字符串,一个指向字符串的开头,另一个指向字符串的结尾。通过比较两个指针所指向的字符是否相等来判断是否找到了第一个出现的字符。具体实现的代码如下: ```python def findIndex(self, s: str) -> int: left = 0 right = len(s) - 1 while left <= right: if s[left == s[right]: return left left += 1 right -= 1 return -1 ``` 接下来是Leetcode 977题,题目要对给定的有序数组的元素进行平方,并按照非递减的顺序返回结果。这里由于数组已经是有序的,所以可以使用双指针的方法来解决问题。一个指针指向数组的开头,另一个指针指向数组的末尾。通过比较两个指针所指向的元素的绝对的大小来确定哪个元素的平方应该放在结果数组的末尾。具体实现的代码如下: ```python def sortedSquares(self, nums: List[int]) -> List[int]: left = 0 right = len(nums) - 1 ans = [] while left <= right: if abs(nums[left]) >= abs(nums[right]): ans.append(nums[left ** 2) left += 1 else: ans.append(nums[right ** 2) right -= 1 return ans[::-1] ``` 最后是Leetcode 209题,题目要在给定的数组找到长度最小的子数组,

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值