参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!

> 数据结构与算法应用往往隐藏在我们看不到的地方 # 20. 有效的括号 [力扣题目链接](https://leetcode.cn/problems/valid-parentheses/) 给定一个只包括 '(',')','{','}','[',']' 的字符串,判断字符串是否有效。 有效字符串需满足: * 左括号必须用相同类型的右括号闭合。 * 左括号必须以正确的顺序闭合。 * 注意空字符串可被认为是有效字符串。 示例 1: * 输入: "()" * 输出: true 示例 2: * 输入: "()[]{}" * 输出: true 示例 3: * 输入: "(]" * 输出: false 示例 4: * 输入: "([)]" * 输出: false 示例 5: * 输入: "{[]}" * 输出: true # 思路 《代码随想录》算法视频公开课:[栈的拿手好戏!| LeetCode:20. 有效的括号](https://www.bilibili.com/video/BV1AF411w78g),相信结合视频在看本篇题解,更有助于大家对链表的理解。 ## 题外话 **括号匹配是使用栈解决的经典问题。** 题意其实就像我们在写代码的过程中,要求括号的顺序是一样的,有左括号,相应的位置必须要有右括号。 如果还记得编译原理的话,编译器在 词法分析的过程中处理括号、花括号等这个符号的逻辑,也是使用了栈这种数据结构。 再举个例子,linux系统中,cd这个进入目录的命令我们应该再熟悉不过了。 ``` cd a/b/c/../../ ``` 这个命令最后进入a目录,系统是如何知道进入了a目录呢 ,这就是栈的应用(其实可以出一道相应的面试题了) 所以栈在计算机领域中应用是非常广泛的。 有的同学经常会想学的这些数据结构有什么用,也开发不了什么软件,大多数同学说的软件应该都是可视化的软件例如APP、网站之类的,那都是非常上层的应用了,底层很多功能的实现都是基础的数据结构和算法。 **所以数据结构与算法的应用往往隐藏在我们看不到的地方!** 这里我就不过多展开了,先来看题。 ## 进入正题 由于栈结构的特殊性,非常适合做对称匹配类的题目。 首先要弄清楚,字符串里的括号不匹配有几种情况。 **一些同学,在面试中看到这种题目上来就开始写代码,然后就越写越乱。** 建议在写代码之前要分析好有哪几种不匹配的情况,如果不在动手之前分析好,写出的代码也会有很多问题。 先来分析一下 这里有三种不匹配的情况, 1. 第一种情况,字符串里左方向的括号多余了 ,所以不匹配。 ![括号匹配1](https://code-thinking-1253855093.file.myqcloud.com/pics/2020080915505387.png) 2. 第二种情况,括号没有多余,但是 括号的类型没有匹配上。 ![括号匹配2](https://code-thinking-1253855093.file.myqcloud.com/pics/20200809155107397.png) 3. 第三种情况,字符串里右方向的括号多余了,所以不匹配。 ![括号匹配3](https://code-thinking-1253855093.file.myqcloud.com/pics/20200809155115779.png) 我们的代码只要覆盖了这三种不匹配的情况,就不会出问题,可以看出 动手之前分析好题目的重要性。 动画如下: ![20.有效括号](https://code-thinking.cdn.bcebos.com/gifs/20.有效括号.gif) 第一种情况:已经遍历完了字符串,但是栈不为空,说明有相应的左括号没有右括号来匹配,所以return false 第二种情况:遍历字符串匹配的过程中,发现栈里没有要匹配的字符。所以return false 第三种情况:遍历字符串匹配的过程中,栈已经为空了,没有匹配的字符了,说明右括号没有找到对应的左括号return false 那么什么时候说明左括号和右括号全都匹配了呢,就是字符串遍历完之后,栈是空的,就说明全都匹配了。 分析完之后,代码其实就比较好写了, 但还有一些技巧,在匹配左括号的时候,右括号先入栈,就只需要比较当前元素和栈顶相不相等就可以了,比左括号先入栈代码实现要简单的多了! 实现C++代码如下: ```CPP class Solution { public: bool isValid(string s) { if (s.size() % 2 != 0) return false; // 如果s的长度为奇数,一定不符合要求 stack st; for (int i = 0; i < s.size(); i++) { if (s[i] == '(') st.push(')'); else if (s[i] == '{') st.push('}'); else if (s[i] == '[') st.push(']'); // 第三种情况:遍历字符串匹配的过程中,栈已经为空了,没有匹配的字符了,说明右括号没有找到对应的左括号 return false // 第二种情况:遍历字符串匹配的过程中,发现栈里没有我们要匹配的字符。所以return false else if (st.empty() || st.top() != s[i]) return false; else st.pop(); // st.top() 与 s[i]相等,栈弹出元素 } // 第一种情况:此时我们已经遍历完了字符串,但是栈不为空,说明有相应的左括号没有右括号来匹配,所以return false,否则就return true return st.empty(); } }; ``` * 时间复杂度: O(n) * 空间复杂度: O(n) 技巧性的东西没有固定的学习方法,还是要多看多练,自己灵活运用了。 ## 其他语言版本 Java: ```Java class Solution { public boolean isValid(String s) { Deque deque = new LinkedList<>(); char ch; for (int i = 0; i < s.length(); i++) { ch = s.charAt(i); //碰到左括号,就把相应的右括号入栈 if (ch == '(') { deque.push(')'); }else if (ch == '{') { deque.push('}'); }else if (ch == '[') { deque.push(']'); } else if (deque.isEmpty() || deque.peek() != ch) { return false; }else {//如果是右括号判断是否和栈顶元素匹配 deque.pop(); } } //最后判断栈中元素是否匹配 return deque.isEmpty(); } } ``` Python: ```python # 方法一,仅使用栈,更省空间 class Solution: def isValid(self, s: str) -> bool: stack = [] for item in s: if item == '(': stack.append(')') elif item == '[': stack.append(']') elif item == '{': stack.append('}') elif not stack or stack[-1] != item: return False else: stack.pop() return True if not stack else False ``` ```python # 方法二,使用字典 class Solution: def isValid(self, s: str) -> bool: stack = [] mapping = { '(': ')', '[': ']', '{': '}' } for item in s: if item in mapping.keys(): stack.append(mapping[item]) elif not stack or stack[-1] != item: return False else: stack.pop() return True if not stack else False ``` Go: ```Go func isValid(s string) bool { hash := map[byte]byte{')':'(', ']':'[', '}':'{'} stack := make([]byte, 0) if s == "" { return true } for i := 0; i < len(s); i++ { if s[i] == '(' || s[i] == '[' || s[i] == '{' { stack = append(stack, s[i]) } else if len(stack) > 0 && stack[len(stack)-1] == hash[s[i]] { stack = stack[:len(stack)-1] } else { return false } } return len(stack) == 0 } ``` Ruby: ```ruby def is_valid(strs) symbol_map = {')' => '(', '}' => '{', ']' => '['} stack = [] strs.size.times {|i| c = strs[i] if symbol_map.has_key?(c) top_e = stack.shift return false if symbol_map[c] != top_e else stack.unshift(c) end } stack.empty? end ``` Javascript: ```javascript var isValid = function (s) { const stack = []; for (let i = 0; i < s.length; i++) { let c = s[i]; switch (c) { case '(': stack.push(')'); break; case '[': stack.push(']'); break; case '{': stack.push('}'); break; default: if (c !== stack.pop()) { return false; } } } return stack.length === 0; }; // 简化版本 var isValid = function(s) { const stack = [], map = { "(":")", "{":"}", "[":"]" }; for(const x of s) { if(x in map) { stack.push(x); continue; }; if(map[stack.pop()] !== x) return false; } return !stack.length; }; ``` TypeScript: 版本一:普通版 ```typescript function isValid(s: string): boolean { let helperStack: string[] = []; for (let i = 0, length = s.length; i < length; i++) { let x: string = s[i]; switch (x) { case '(': helperStack.push(')'); break; case '[': helperStack.push(']'); break; case '{': helperStack.push('}'); break; default: if (helperStack.pop() !== x) return false; break; } } return helperStack.length === 0; }; ``` 版本二:优化版 ```typescript function isValid(s: string): boolean { type BracketMap = { [index: string]: string; } let helperStack: string[] = []; let bracketMap: BracketMap = { '(': ')', '[': ']', '{': '}' } for (let i of s) { if (bracketMap.hasOwnProperty(i)) { helperStack.push(bracketMap[i]); } else if (i !== helperStack.pop()) { return false; } } return helperStack.length === 0; }; ``` Swift ```swift func isValid(_ s: String) -> Bool { var stack = [String.Element]() for ch in s { if ch == "(" { stack.append(")") } else if ch == "{" { stack.append("}") } else if ch == "[" { stack.append("]") } else { let top = stack.last if ch == top { stack.removeLast() } else { return false } } } return stack.isEmpty } ``` C: ```C //辅助函数:判断栈顶元素与输入的括号是否为一对。若不是,则返回False int notMatch(char par, char* stack, int stackTop) { switch(par) { case ']': return stack[stackTop - 1] != '['; case ')': return stack[stackTop - 1] != '('; case '}': return stack[stackTop - 1] != '{'; } return 0; } bool isValid(char * s){ int strLen = strlen(s); //开辟栈空间 char stack[5000]; int stackTop = 0; //遍历字符串 int i; for(i = 0; i < strLen; i++) { //取出当前下标所对应字符 char tempChar = s[i]; //若当前字符为左括号,则入栈 if(tempChar == '(' || tempChar == '[' || tempChar == '{') stack[stackTop++] = tempChar; //若当前字符为右括号,且栈中无元素或右括号与栈顶元素不符,返回False else if(stackTop == 0 || notMatch(tempChar, stack, stackTop)) return 0; //当前字符与栈顶元素为一对括号,将栈顶元素出栈 else stackTop--; } //若栈中有元素,返回False。若没有元素(stackTop为0),返回True return !stackTop; } ``` C#: ```csharp public class Solution { public bool IsValid(string s) { var len = s.Length; if(len % 2 == 1) return false; // 字符串长度为单数,直接返回 false // 初始化栈 var stack = new Stack(); // 遍历字符串 for(int i = 0; i < len; i++){ // 当字符串为左括号时,进栈对应的右括号 if(s[i] == '('){ stack.Push(')'); }else if(s[i] == '['){ stack.Push(']'); }else if(s[i] == '{'){ stack.Push('}'); } // 当字符串为右括号时,当栈为空(无左括号) 或者 出栈字符不是当前的字符 else if(stack.Count == 0 || stack.Pop() != s[i]) return false; } // 如果栈不为空,例如“((()”,右括号少于左括号,返回false if (stack.Count > 0) return false; // 上面的校验都满足,则返回true else return true; } } ``` PHP: ```php // https://www.php.net/manual/zh/class.splstack.php class Solution { function isValid($s){ $stack = new SplStack(); for ($i = 0; $i < strlen($s); $i++) { if ($s[$i] == "(") { $stack->push(')'); } else if ($s[$i] == "{") { $stack->push('}'); } else if ($s[$i] == "[") { $stack->push(']'); // 2、遍历匹配过程中,发现栈内没有要匹配的字符 return false // 3、遍历匹配过程中,栈已为空,没有匹配的字符了,说明右括号没有找到对应的左括号 return false } else if ($stack->isEmpty() || $stack->top() != $s[$i]) { return false; } else {//$stack->top() == $s[$i] $stack->pop(); } } // 1、遍历完,但是栈不为空,说明有相应的括号没有被匹配,return false return $stack->isEmpty(); } } ``` Scala: ```scala object Solution { import scala.collection.mutable def isValid(s: String): Boolean = { if(s.length % 2 != 0) return false // 如果字符串长度是奇数直接返回false val stack = mutable.Stack[Char]() // 循环遍历字符串 for (i <- s.indices) { val c = s(i) if (c == '(' || c == '[' || c == '{') stack.push(c) else if(stack.isEmpty) return false // 如果没有(、[、{则直接返回false // 以下三种情况,不满足则直接返回false else if(c==')' && stack.pop() != '(') return false else if(c==']' && stack.pop() != '[') return false else if(c=='}' && stack.pop() != '{') return false } // 如果为空则正确匹配,否则还有余孽就不匹配 stack.isEmpty } } ``` rust: ```rust impl Solution { pub fn is_valid(s: String) -> bool { if s.len() % 2 == 1 { return false; } let mut stack = vec![]; let mut chars: Vec = s.chars().collect(); while let Some(s) = chars.pop() { match s { ')' => stack.push('('), ']' => stack.push('['), '}' => stack.push('{'), _ => { if stack.is_empty() || stack.pop().unwrap() != s { return false; } } } } stack.is_empty() } } ```