数据结构与算法之深入解析“有效的括号”的求解思路与算法示例
Posted Serendipity·y
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据结构与算法之深入解析“有效的括号”的求解思路与算法示例相关的知识,希望对你有一定的参考价值。
一、题目要求
- 给定一个只包括 ‘(’,’)’,’’,’’,’[’,’]’ 的字符串 s ,判断字符串是否有效。
- 有效字符串需满足:
-
- 左括号必须用相同类型的右括号闭合;
-
- 左括号必须以正确的顺序闭合。
- 示例 1:
输入:s = "()"
输出:true
- 示例 2:
输入:s = "()[]"
输出:true
- 示例 3:
输入:s = "(]"
输出:false
- 示例 4:
输入:s = "([)]"
输出:false
- 示例 5:
输入:s = "[]"
输出:true
- 提示:
-
- 1 <= s.length <= 104;
-
- s 仅由括号 ‘()[]’ 组成。
二、求解算法
① 辅助栈
- 算法原理:
-
- 栈先入后出特点恰好与本题括号排序特点一致,即若遇到左括号入栈,遇到右括号时将对应栈顶左括号出栈,则遍历完所有括号后 stack 仍然为空;
-
- 建立哈希表 dic 构建左右括号对应关系:key 左括号,value 右括号;这样查询 2 个括号是否对应只需 O(1) 时间复杂度;建立栈 stack,遍历字符串 s 并按照算法流程一一判断。
- 算法流程:
-
- 如果 c 是左括号,则入栈 push;
-
- 否则通过哈希表判断括号对应关系,若 stack 栈顶出栈括号 stack.pop() 与当前遍历括号 c 不对应,则提前返回false。
- 提前返回 false
-
- 提前返回优点: 在迭代过程中,提前发现不符合的括号并且返回,提升算法效率。
-
- 解决边界问题:
-
-
- 栈 stack 为空: 此时 stack.pop() 操作会报错;因此,我们采用一个取巧方法,给 stack 赋初值 ? ,并在哈希表 dic 中建立 key: ′?′,value: ′?′ 的对应关系予以配合。此时当 stack 为空且 c 为右括号时,可以正常提前返回 false;
-
-
-
- 字符串 s 以左括号结尾: 此情况下可以正常遍历完整个 s,但 stack 中遗留未出栈的左括号;因此,最后需返回 len(stack) == 1,以判断是否是有效的括号组合。
-
- Java 示例:
class Solution
private static final Map<Character,Character> map = new HashMap<Character,Character>()
put('',''); put('[',']'); put('(',')'); put('?','?');
;
public boolean isValid(String s)
if(s.length() > 0 && !map.containsKey(s.charAt(0))) return false;
LinkedList<Character> stack = new LinkedList<Character>() add('?'); ;
for(Character c : s.toCharArray())
if(map.containsKey(c)) stack.addLast(c);
else if(map.get(stack.removeLast()) != c) return false;
return stack.size() == 1;
- 复杂度分析:
-
- 时间复杂度 O(N):正确的括号组合需要遍历一遍 s;
-
- 空间复杂度 O(N):哈希表和栈使用线性的空间大小。
② 栈(LeetCode 官方解法)
- 判断括号的有效性可以使用「栈」这一数据结构来解决。
- 我们遍历给定的字符串 s,当遇到一个左括号时,会期望在后续的遍历中,有一个相同类型的右括号将其闭合。由于后遇到的左括号要先闭合,因此可以将这个左括号放入栈顶。
- 当遇到一个右括号时,需要将一个相同类型的左括号闭合。此时,可以取出栈顶的左括号并判断它们是否是相同类型的括号。如果不是相同的类型,或者栈中并没有左括号,那么字符串 s 无效,返回 False。为了快速判断括号的类型,可以使用哈希表存储每一种括号。哈希表的键为右括号,值为相同类型的左括号。
- 在遍历结束后,如果栈中没有左括号,说明将字符串 s 中的所有左括号闭合,返回 True,否则返回 False。
- 注意到有效字符串的长度一定为偶数,因此如果字符串的长度为奇数,可以直接返回 False,省去后续的遍历判断过程。
- Java 示例:
class Solution
public boolean isValid(String s)
int n = s.length();
if (n % 2 == 1)
return false;
Map<Character, Character> pairs = new HashMap<Character, Character>()
put(')', '(');
put(']', '[');
put('', '');
;
Deque<Character> stack = new LinkedList<Character>();
for (int i = 0; i < n; i++)
char ch = s.charAt(i);
if (pairs.containsKey(ch))
if (stack.isEmpty() || stack.peek() != pairs.get(ch))
return false;
stack.pop();
else
stack.push(ch);
return stack.isEmpty();
- C++ 示例:
class Solution
public:
bool isValid(string s)
int n = s.size();
if (n % 2 == 1)
return false;
unordered_map<char, char> pairs =
')', '(',
']', '[',
'', ''
;
stack<char> stk;
for (char ch: s)
if (pairs.count(ch))
if (stk.empty() || stk.top() != pairs[ch])
return false;
stk.pop();
else
stk.push(ch);
return stk.empty();
;
- Swift 示例:
class Solution
func isValid(_ s: String) -> Bool
func isPair(_ a: Character, _ b: Character) -> Bool
return (a == "(" && b == ")") || (a == "[" && b == "]") || (a == "" && b == "")
enum Direction
case left
case right
static func getDirection(_ char: Character) -> Direction
switch char
case "(", "[", "":
return left
default:
return right
var unpaired: [Character] = []
for char in s
let direction = Direction.getDirection(char)
switch direction
case .left:
unpaired.append(char)
case .right:
if !isPair(unpaired.last ?? " ", char)
return false
else
unpaired.removeLast(1)
return unpaired.isEmpty
class Solution
func isValid(_ s: String) -> Bool
var stack:[String] = []
let paren_map = [")":"(", "]":"[", "":""]
for char in s // 遍历每个字符串
if paren_map.values.contains(char.description) // 判断是否为左括号
stack.append(char.description) // 入栈
else if paren_map[char.description] != stack.popLast() // 判断是否与栈顶元素相同,并出栈
return false // 如果不同,说明括号无效
return stack.count == 0
以上是关于数据结构与算法之深入解析“有效的括号”的求解思路与算法示例的主要内容,如果未能解决你的问题,请参考以下文章
数据结构与算法之深入解析“股票的最大利润”的求解思路与算法示例