题目 20. 有效的括号
题目描述:
给定一个只包括 '(',')','{','}','[',']' 的字符串,判断字符串是否有效。
有效字符串需满足:
- 左括号必须用相同类型的右括号闭合。
- 左括号必须以正确的顺序闭合。
- 注意空字符串可被认为是有效字符串。
示例 1:
- 输入: "()"
- 输出: true
示例 2:
- 输入: "()[]{}"
- 输出: true
示例 3:
- 输入: "(]"
- 输出: false
示例 4:
- 输入: "([)]"
- 输出: false
示例 5:
- 输入: "{[]}"
- 输出: true
解题思路:
首先括号都是成对的,所以长度为奇数的肯定无效。
长度为偶数的情况下,用栈来做,把括号分为两种:
- 左括号 ( [ {
- 右括号 ) ] }
循环整个输入,当检测到为左括号时,往栈里面加对应的右括号。
检测到位右括号时,而且栈头元素为一样的右括号,则pop掉栈头元素。
最后检查栈,如果为空说明匹配完了,返回true;如果没有,说明匹配不合格。
代码:
class Solution:
def isValid(self, s: str) -> bool:
#define a stack to store the right part
stack=[]
#define a dict store the left and right
my_dict={'(':')','{':'}','[':']'}
if len(s)%2!=0:
return False
else:
for i in s:
if i in my_dict.keys():
stack.append(my_dict[i])
elif not stack or stack[-1] != i:
return False
else:
stack.pop()
return True if not stack else False
复杂度:
函数的时间和空间复杂度都是 O(n)。
题目1047. 删除字符串中的所有相邻重复项
题目描述:
给出由小写字母组成的字符串 S,重复项删除操作会选择两个相邻且相同的字母,并删除它们。
在 S 上反复执行重复项删除操作,直到无法继续删除。
在完成所有重复项删除操作后返回最终的字符串。答案保证唯一。
示例:
- 输入:"abbaca"
- 输出:"ca"
- 解释:例如,在 "abbaca" 中,我们可以删除 "bb" 由于两字母相邻且相同,这是此时唯一可以执行删除操作的重复项。之后我们得到字符串 "aaca",其中又只有 "aa" 可以执行重复项删除操作,所以最后的字符串为 "ca"。
提示:
- 1 <= S.length <= 20000
- S 仅由小写英文字母组成。
解题思路:
-
我们首先创建一个空的栈
stack
。 -
然后,我们遍历输入的字符串
s
。对于s
中的每一个字符i
,我们做以下处理:- 如果栈不为空,并且栈顶的字符(
stack[-1]
)与当前字符i
相同,我们就从栈顶弹出一个字符。这是因为我们发现了一对连续出现的重复字符。 - 否则,我们就把当前字符
i
压入栈中。
- 如果栈不为空,并且栈顶的字符(
-
遍历完
s
之后,栈中的字符就是我们想要的结果。我们只需要将栈中的字符连成一个字符串即可。在Python中,可以使用''.join(stack)
来实现这一点。
代码:
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)
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)
复杂度:
时间复杂度:我们需要遍历一次输入的字符串 s
,对于 s
中的每个字符,我们执行的操作(即比较和可能的压栈或出栈操作)都可以在常数时间内完成。因此,总的时间复杂度是 O(n)。
空间复杂度:我们用到了一个额外的栈 stack
来存储字符。在最坏的情况下(例如,当 s
中没有连续的重复字符时),我们需要把 s
中的所有字符都压入栈中。因此,总的空间复杂度是 O(n)。
题目 150. 逆波兰表达式求值
题目描述:
根据 逆波兰表示法,求表达式的值。
有效的运算符包括 + , - , * , / 。每个运算对象可以是整数,也可以是另一个逆波兰表达式。
说明:
整数除法只保留整数部分。 给定逆波兰表达式总是有效的。换句话说,表达式总会得出有效数值且不存在除数为 0 的情况。
示例 1:
- 输入: ["2", "1", "+", "3", " * "]
- 输出: 9
- 解释: 该算式转化为常见的中缀算术表达式为:((2 + 1) * 3) = 9
解题思路:
-
首先创建一个空栈。
-
遍历给定的逆波兰表达式(是一个字符串数组)。
-
对于遇到的每个元素:
- 如果它是一个操作符('+'、'-'、'*'、'/'),那么从栈中弹出两个元素作为操作数,进行相应的运算,并将结果再压入栈中。
- 如果它是一个数字,那么直接将其压入栈中。
-
遍历结束后,栈中应该只剩下一个元素,这就是整个表达式的计算结果。
代码:
注意eval函数的用法,
除法操作("/")在 Python 中的结果是浮点数,即使是两个整数相除。
因此,为了保持栈中元素的一致性(即都是字符串类型的整数),以及为了符合题目要求(即所有的除法运算结果都需要取整),这里用 int()
函数将 eval
的结果转化为整数,然后再将其以字符串形式存入栈中。
class Solution:
#eval: 将字符串str当成有效的表达式来求值并返回计算结果
def evalRPN(self, tokens: List[str]) -> int:
stack=[]
for item in tokens:
if item not in {"+", "-", "*", "/"}:
stack.append(item)
else:
first_num, second_num = stack.pop(), stack.pop()
stack.append(
int(eval(f'{second_num} {item} {first_num}')) # 第一个出来的在运算符后面
)
return int(stack.pop()) # 如果一开始只有一个数,那么会是字符串形式的,所以为了保险起见加一个int
复杂度:
时间复杂度:我们需要遍历一次输入的 tokens 数组,对于 tokens 中的每个元素,我们执行的操作(即比较和可能的压栈或出栈操作)都可以在常数时间内完成。因此,总的时间复杂度是 O(n)。
空间复杂度:我们用到了一个额外的栈 stack
来存储数字。在最坏的情况下(例如,当 tokens 中所有的元素都是数字时),我们需要把 tokens 中的所有元素都压入栈中。因此,总的空间复杂度是 O(n)。
额外知识点:
逆波兰表达式(Reverse Polish Notation, RPN)实际上是二叉树的后序遍历(Post-order Traversal)。
如果我们将一个算术表达式看作一个二叉树,其中叶节点是操作数(即数字),非叶节点是操作符(如 +、-、*、/),那么:
- 中序遍历(In-order Traversal)会得到中缀表达式,即常规的算术表达式,例如 "(2 + 3) * 4"。
- 前序遍历(Pre-order Traversal)会得到波兰表达式(Polish Notation,PN),例如 "* + 2 3 4"。
- 后序遍历(Post-order Traversal)则会得到逆波兰表达式(Reverse Polish Notation, RPN),例如 "2 3 + 4 *"。
所以,逆波兰表达式可以看作是算术表达式二叉树的后序遍历。