题目描述:
给定一个只包括 ‘(’,’)’,’{’,’}’,’[’,’]’ 的字符串,判断字符串是否有效。
有效字符串需满足:
左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
注意空字符串可被认为是有效字符串。
示例 1:
输入: “()”
输出: true
示例 2:
输入: “()[]{}”
输出: true
示例 3:
输入: “(]”
输出: false
示例 4:
输入: “([)]”
输出: false
示例 5:
输入: “{[]}”
输出: true
“踩得坑”(错误code)
class Solution(object):
def Reverse(self, lst):
return [ele for ele in reversed(lst)]
def isValid(self, s):
"""
:type s: str
:rtype: bool
"""
left_list = ['(', '{', '[']
right_list = [')', '}', ']']
correct_list = ['()', '{}', '[]'] #参照
n = 0
test_list = list(s) #将字符串转换为列表
test_right_part = test_list[(len(test_list)/2):]
test_left_part = test_list[0:(len(test_list)/2)]
test_left_part = self.Reverse(test_left_part) #翻转左切片
if s == '':
return 1
if len(test_list) % 2 != 0:
return 0
if test_list[-1] in left_list:
return 0
if test_list[0] in right_list:
return 0
for i in range(len(test_right_part)):
if test_right_part[i] not in left_list:
tmp = test_left_part[i] + test_right_part[i]
if tmp in correct_list:
n += 1
if test_right_part[i] in left_list:
tmp = test_right_part[i] + test_right_part[i+1]
if tmp in correct_list:
n += 1
if test_left_part[i] in right_list:
tmp = test_left_part[i+1] + test_left_part[i]
if tmp in correct_list:
n += 1
if n == len(test_list)/2:
return 1
这段code题中给出的示例都可以过去,但是,会卡在这样的例子中:
原因:上面code的基本逻辑是
- 如果是奇数,一定是非有效的括号
- 如果最后一位是左括号,或第一位是右括号,这两种情况也都一定是非有效的括号
- 从中位数开始,分"左切片"和"右切片",然后把左右切片合并,判断是否在"正确列表"中
这种列表的逻辑没有抓到本质,有很多种情况需要一一实现,如上述图片中的示例就很难套用这套逻辑!因此得采用数据结构的思想!
我们先看原问题的一个简化问题:
我们试着用一个简单的算法来解决这一问题。
我们从表达式的左侧开始,每次只处理一个括号。
假设我们遇到一个开括号"( 即 ( )",表达式是否无效取决于在该表达式的其余部分的某处是否有相匹配的闭括号"( 即 ) )"。
此时,我们只是增加计数器的值保持跟踪现在为止开括号的数目。left += 1
如果我们遇到一个闭括号,这可能意味着这样两种情况:
- 此闭括号没有与与之对应的开括号,在这种情况下,我们的表达式无效。当 left == 0,也就是没有未配对的左括号可用时就是这种情况。
- 我们有一些未配对的开括号可以与该闭括号配对。当 left > 0,也就是有未配对的左括号可用时就是这种情况。 如果我们在 left == 0 时遇到一个闭括号(例如 )),那么当前的表达式无效。否则,我们会减少 left 的值,也就是减少了可用的未配对的左括号的数量。
继续处理字符串,直到处理完所有括号。
如果最后我们仍然有未配对的左括号,这意味着表达式无效。
简言之,就是从最左端开始,加一个计数器,只要遇到左括号,计数器+1,遇到右括号,计数器-1;直到末尾判断计数器是否等于0;如果等于0,则为有效括号;若不等于0,则无效括号。
这是一个简化问题,简化原因在于所有的括号都是圆括号(即 ( 或 ));而这里我们遇到的括号有三种,因此计数器的思想行不通;我们可以采取栈的思想---------------后进先出。
具体:
- 栈中存储左括号,和右括号匹配,只要配对成功就删掉,继续往下匹配;
- 如果匹配不成功,则直接返回0,因为”最近原则“,左右匹配(如果一左一右的话)
代码实现:
class Solution(object):
def isValid(self, s):
"""
:type s: str
:rtype: bool
"""
# The stack to keep track of opening brackets.
stack = []
# Hash map for keeping track of mappings. This keeps the code very clean.
# Also makes adding more types of parenthesis easier
mapping = {")": "(", "}": "{", "]": "["}
# For every bracket in the expression.
for char in s:
# If the character is an closing bracket
if char in mapping:
# Pop the topmost element from the stack, if it is non empty
# Otherwise assign a dummy value of '#' to the top_element variable
top_element = stack.pop() if stack else '#'
# The mapping for the opening bracket in our hash and the top
# element of the stack don't match, return False
if mapping[char] != top_element:
return False
else:
# We have an opening bracket, simply push it onto the stack.
stack.append(char)
# In the end, if the stack is empty, then we have a valid expression.
# The stack won't be empty for cases like ((()
return not stack
代码解释:
if char in mapping:
字典这么写是判断它的”键“是否在mapping中;
如果在,并且栈中还有左括号,就可以删掉,并且返回赋值给top_element;原因是:下一句如果不是匹配的,就直接返回false;否则继续往下!
另一解法?:
class Solution:
def isValid(self, s):
while '{}' in s or '()' in s or '[]' in s:
s = s.replace('{}', '')
s = s.replace('[]', '')
s = s.replace('()', '')
return s == ''