每日一道 LeetCode (6):有效的括号
每天 3 分钟,走上算法的逆袭之路。
前文合集
代码仓库
GitHub: https://github.com/meteor1993/LeetCode
Gitee: https://gitee.com/inwsy/LeetCode
题目:有效的括号
题目来源:https://leetcode-cn.com/problems/valid-parentheses/
给定一个只包括 '(',')','{','}','[',']'
的字符串,判断字符串是否有效。
有效字符串需满足:
- 左括号必须用相同类型的右括号闭合。
- 左括号必须以正确的顺序闭合。
注意空字符串可被认为是有效字符串。
示例 1:
输入: "()"
输出: true
示例 2:
输入: "()[]{}"
输出: true
示例 3:
输入: "(]"
输出: false
示例 4:
输入: "([)]"
输出: false
示例 5:
输入: "{[]}"
输出: true
解题思路
解题思路?
啊呸,有个 P 的解题思路,我跟你们讲哦,这种题没见过,你就是想不出来。
我自己思索了几分钟以后,果断投降去看答案了,这玩意,不是我这种新手小白搞的定的。
结果,一看答案秒懂。
整个解题思路是借用了数据结构「栈」的「先进后出」的特性。
首先第一件事情还是先思考清楚极限情况,比如如果是空字符串,是可以返回 true ,如果字符串长度是奇数,那么显然是无法满足左右括号的对应关系。
接下来就是核心问题,一个左括号一个右括号,中间还可能隔着千山万水,如何处理?
使用「栈」结构:
- 遇到左括号就压入栈。
- 遇到右括号就和栈顶的左括号比对,匹配失败直接返回 false 。
- 如果匹配成功,则可能是嵌套在其它匹配括号中的,所以此时要将当前栈顶的左括号弹出。
- 如果最后最终,栈中没有剩余元素,也就是没有剩下左括号,说明刚好完成匹配,括号字符串有效。否则匹配失败,括号字符串无效。
如果上面这一段不好理解,可以借助下面这个动图(来源:LeetCode):
代码实现
有了上面的思路,写代码都是小事儿了,先看一个我自己写的,完全符合上面的逻辑:
public boolean isValid(String s) {
if (s.length() == 0) return true;
if (s.length() % 2 == 1) return false;
Stack<Character> stack = new Stack<> ();
for (int i = 0; i < s.length(); i++) {
char charAt = s.charAt(i);
// 如果是左括号,则把字符压入栈
if (charAt == '(' || charAt == '{' || charAt == '[')
stack.push(charAt);
else {
// 如果此时还有右括号而栈中已无左括号
if (stack.isEmpty()) return false;
// 获取栈顶的值
char top = stack.peek();
// 如果栈顶的值等于右括号,则出栈,否则返回 false
if ((top == '{' && charAt == '}') || (top == '(' && charAt == ')') || (top == '[' && charAt == ']'))
stack.pop();
else
return false;
}
}
return stack.isEmpty();
}
里面的注释已经比较清晰了,我就不多解释了。
接着我看了官方提供的答案,基本上思路和我的代码保持一致,只是把左右括号放入到了哈希表中,由哈希表来判断括号是否存在。
private Map<Character, Character> map;
// 初始化哈希表
public Solution() {
this.map = new HashMap<> ();
this.map.put(')', '(');
this.map.put('}', '{');
this.map.put(']', '[');
}
public boolean isValid_1(String s) {
if (s.length() == 0) return true;
if (s.length() % 2 == 1) return false;
Stack<Character> stack = new Stack<> ();
for (int i = 0; i < s.length(); i++) {
char charAt = s.charAt(i);
// 如果不是右括号,则把字符压入栈
if (!this.map.containsKey(charAt)) {
stack.push(charAt);
} else {
// 如果此时还有右括号而栈中已无左括号
if (stack.isEmpty()) return false;
// 获取栈顶的值
char top = stack.peek();
// 如果栈顶的值等于右括号,则出栈,否则返回 false
if (top == this.map.get(charAt))
stack.pop();
else
return false;
}
}
return stack.empty();
}
因为哈希表中的数据太少,而且寻址的次数也不够多,所以哈希表的方案看起来还比顺次匹配的的方案耗时高。
效率更高的方案有没有,当然有,直接使用数组模拟栈的入栈和出栈,这个时间是可以压缩在 1ms 以内的,执行时间可以在 Java 的提交中击败 100% 的用户,这个示例我就不写了,感兴趣的同学可以自己写写看。
在我翻答案的时候看到一个 1ms 的方案,这个方案是对我前面的那个 2ms 的代码的优化,有点意思,拿出来聊一下:
public boolean isValid_2(String s) {
char[] chs = s.toCharArray();
Stack<Character> stack = new Stack<>();
for (char c : chs) {
if (c == '{') {
stack.push('}');
} else if (c == '[') {
stack.push(']');
} else if (c == '(') {
stack.push(')');
} else if (stack.isEmpty() || stack.pop() != c) {
return false;
}
}
return stack.isEmpty();
}
这个方案就是在遇到左括号的时候往栈里面放一个对应的右括号,这么做的原因我猜测是为了好判断,只需要取出的时候和当前循环的右括号做一次 ==
判断,如果相等就继续循环,不相等就直接返回 false 了。
不得不说这个方案的构思很巧妙,在已有的方案上做了相当极致的优化,把耗时进一步降低。果然姜还是老的辣,你大爷还是你大爷。
果然是学无止境,希望各位刷 LeetCode 的同学,如果时间充足,也可以多看看不同的答案,对开阔思路和视野真的是相当有帮助。
顺便吐槽下:想到这些方案的大神太 TM 变态了,这个脑回路和常人差的太远了,能用数组把耗时压到 1ms 以内我能接受,但是能把栈这种数据结构的方案优化到 2ms 以内,这个我是真的服。
每日一道 LeetCode (6):有效的括号的更多相关文章
- 每日一道 LeetCode (3):回文数
前文合集 每日一道 LeetCode 文章合集 题目:回文数 题目来源:https://leetcode-cn.com/problems/palindrome-number/ 判断一个整数是否是回文数 ...
- 每日一道 LeetCode (5):最长公共前缀
前文合集 每日一道 LeetCode 前文合集 代码仓库 GitHub: https://github.com/meteor1993/LeetCode Gitee: https://gitee.com ...
- 每日一道 LeetCode (8):删除排序数组中的重复项和移除元素
每天 3 分钟,走上算法的逆袭之路. 前文合集 每日一道 LeetCode 前文合集 代码仓库 GitHub: https://github.com/meteor1993/LeetCode Gitee ...
- 每日一道 LeetCode (9):实现 strStr()
每天 3 分钟,走上算法的逆袭之路. 前文合集 每日一道 LeetCode 前文合集 代码仓库 GitHub: https://github.com/meteor1993/LeetCode Gitee ...
- 每日一道 LeetCode (10):搜索插入位置
每天 3 分钟,走上算法的逆袭之路. 前文合集 每日一道 LeetCode 前文合集 代码仓库 GitHub: https://github.com/meteor1993/LeetCode Gitee ...
- 每日一道 LeetCode (14):数组加一
每天 3 分钟,走上算法的逆袭之路. 前文合集 每日一道 LeetCode 前文合集 代码仓库 GitHub: https://github.com/meteor1993/LeetCode Gitee ...
- 每日一道 LeetCode (15):二进制求和
每天 3 分钟,走上算法的逆袭之路. 前文合集 每日一道 LeetCode 前文合集 代码仓库 GitHub: https://github.com/meteor1993/LeetCode Gitee ...
- 每日一道 LeetCode (19):合并两个有序数组
每天 3 分钟,走上算法的逆袭之路. 前文合集 每日一道 LeetCode 前文合集 代码仓库 GitHub: https://github.com/meteor1993/LeetCode Gitee ...
- 每日一道 LeetCode (41):阶乘后的零
每天 3 分钟,走上算法的逆袭之路. 前文合集 每日一道 LeetCode 前文合集 代码仓库 GitHub: https://github.com/meteor1993/LeetCode Gitee ...
随机推荐
- Python比较操作符、变量赋值、对象拷贝
Python比较操作符.变量赋值.对象拷贝 目录 Python比较操作符.变量赋值.对象拷贝 1. 比较操作符 == 和 is 1.1 区别 1.2 实例 2. 变量及其赋值 2.1 概念和逻辑关系 ...
- python 面向对象专题(四):封装、多态、鸭子类型、类的约束、super
https://www.cnblogs.com/liubing8/p/11321099.html 目录 Python面向对象04 /封装.多态.鸭子类型.类的约束.super 1. 封装 2. 多态 ...
- 微信小程序支付、小程序支付功能、小程序支付方法、微信小程序支付方法
相信大家在做小程序的时候不可避免的会碰到支付功能小程序的支付和pc的是有区别的小程序的支付方法为 wx.requestPayment wx.requestPayment({ timeStamp: '' ...
- Idea 自定义快捷代码输入 如syso => System.out.println()
前言 之前一直用的Eclipse System.out.println()的快捷代码输入 是 syso,但是在Idea 不好使用了,后来搜索了一番才知道,在Idea中的快捷输入是 sout,这里我就想 ...
- 美国数学家维纳(N.Wiener)智力早熟,11岁就上了大学。他曾在1935~1936年应邀来中国清华大学讲学。 一次,他参加某个重要会议,年轻的脸孔引人注目。于是有人询问他的年龄,他回答说:我年龄的立方是个4位数。 我年龄的4次方是个6位数。这10个数字正好包含了从0到9这10个数字,每个都恰好出现1次。” 请你推算一下,他当时到底有多年轻。 结果只有一个数。
#include<stdio.h>int main(){ int age=1; int san=0; int si=0; int sum=0; while(age>0) { san= ...
- 媳妇儿喜欢玩某音中的动漫特效,那我就用python做一个图片转化软件。
最近某音上的动漫特效特别火,很多人都玩着动漫肖像,我媳妇儿也不例外.看着她这么喜欢这个特效,我决定做一个图片处理工具,这样媳妇儿的动漫头像就有着落了.编码 为了快速实现我们的目标,我们 ...
- Host是什么?如何设置host文件?
前言 前几天我在使用一些软件和网站时,出了一些小问题,然后我在网上搜解决问题的方法,搜着搜着就看到频繁出现的Host这个词.以前还没有注意到这个东西,因为总觉得它是系统文件,没必要去乱动:但是经过这次 ...
- T4 字符串的修改 题解
有 A=a1a2a3„am,B=b1b2b3„bn 两个字符串(均为小写字母)现在要通过以下操作将 A 或 A 的一个后缀修改为 B: 1. 删除 删除掉 A 中的某一个字符. 2. 添加 将某一个字 ...
- 集训作业 洛谷P1469 找筷子
这个题的代码真的是短的不得了呢. 有个神奇的东西叫异或,写起来是这个样子的^. 这个东西可以查看2个数的二进制某位是否相同,相同取0,不同取1.虽然我用的不熟,但我可以想出来,如果2个相同的数异或,答 ...
- antd4 源码学习 :表单
Evernote Export 首先.vue 的数据流是双向的,而 react 的数据流是单向的. 这意味着什么? 这意味着,vue 中,子组件可以用 emit 把数据更新传给父组件.而 react ...