【图解算法】彻底搞懂(括号匹配)——图解带你直击本质
括号匹配专题
你可否记得,当年被【括号匹配】支配的恐惧?
>>> 本文先讨论最基本的括号匹配及其经典的【栈】思路
>>> 再分析其变体
>>> 最后是究极变体,从【栈】【动态规划】【括号计数(追赶法)】三种方法切入,彻底理解括号匹配问题
>>>
>>> 记住,所有的方法,归根结底还是从括号字符串的自身特性出发的
>>> 再记住两句话:
>>> 1.【尝试用')'去消去前面的'('】
>>> 2.【当右括号数量超过左括号时,必然串无效】
经典的栈
/*
* 经典的【栈】思路
*
* 遇到'(',就入栈; 遇到')',就出栈
* 扫描过程中,当出栈时发现栈空时,串无效
* 扫描结束后。若栈空,则串有效;如果栈不空,则串有效
*/
public boolean isValid(String s) {Deque<Character> stack = new ArrayDeque<>();for(char c : s.toCharArray()) {if(c == '(') {stack.push(c);}else {if(stack.isEmpty()) {return false;}else {stack.pop();}}}return stack.isEmpty();
}
变体1——不止是小括号
思路变体:【经典的栈】 + 【Map】
▊【Q20】(ez) 有效的括号(括号匹配问题)
给定一个只包括 ‘(’,’)’,’{’,’}’,’[’,’]'的字符串,判断字符串是否有效。
有效字符串需满足:
左括号必须用相同类型的右括号闭合。 左括号必须以正确的顺序闭合。 注意空字符串可被认为是有效字符串。
示例 1:
输入: “([{}])”
输出: true
示例 2:
输入: “([)]”
输出: false
class Solution {public boolean isValid(String s) {/** 思路:* 如果是左括号,就毫不犹豫的入栈;* 如果是右括号,不仅要先判断是否栈空,还要判断弹出的左括号是否对应此时的右括号* * 注:* 1.使用HashMap进行匹配,使得代码和逻辑都更具美感* 2.典型的栈结构问题。由于java 8,9有移除Stack的趋势,因此这里使用双端队列Deque,同样有PushPop特性*/if(s == null || s.length() == 0) return true; // leetcode传统艺能,null和0值不说就是trueMap<Character, Character> map = new HashMap<>();map.put(')', '(');map.put(']', '[');map.put('}', '{');Deque<Character> dq = new ArrayDeque<>(); for(char c : s.toCharArray()){if(c == '(' || c == '[' || c == '{'){dq.push(c);}else{if(dq.size() == 0 || map.get(c) != dq.pop()) return false;}}return dq.size() == 0;}// 另外:栈中的字符串必定是'(',因此在这个简单的括号匹配问题中,栈中是什么根本无所谓
}
变体2——自己生成括号
思路变体:【回溯】 + 【左右括号计数】
这里利用了一个括号字符串的性质进行【剪枝】:当右括号数量超过左括号时,必然串无效——这个性质之后还会再派上大用场
▊【Q22】(md) 括号生成
数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。示例:
输入:
n = 3
输出:[
“((()))”,
“(()())”,
“(())()”,
“()(())”,
“()()()”
]
class Solution {/** 【回溯算法】* 思路 :其实这是很典型的需要减枝的回溯算法,应该不难想到* * String tempString是暂存串* int L, int R 记录左右括号的个数,是用于减枝的(右括号不可能多于左括号)* * 其实,剪枝的条件(右括号不可能多于左括号)也是再给出现一个有效串做铺垫:* 如果出现left==right,则这串不一定有效* 当限制了右括号不可能多于左括号时,如果出现left==right则一定是right追上了left,则这串一定有效* * 这就是【左右括号计数】解决括号匹配问题的思路————抛开栈的一个新思路*/public List<String> generateParenthesis(int n) {traceBack(n, "", 0, 0);return resList; }public List<String> resList = new ArrayList<>();public void traceBack(int n, String tempString, int L, int R) {if(R + L == 2*n) {if( R == n && L == n) {resList.add(tempString.toString());return;}else {return;// 减枝}}if(L < R) {return;// 减枝}traceBack(n, tempString + "(", L+1, R);traceBack(n, tempString + ")", L, R+1);}
}
究极变体,由定性到定量——寻找最长有效括号的长度
我们将从【栈】【动态规划】【左右括号计数】三个角度去攻克这个问题
【Q32】(hd) 最长有效括号
给定一个只包含 ‘(’ 和 ‘)’ 的字符串,找出最长的包含有效括号的子串的长度。
示例 1:输入: “(()”
输出: 2
示例 2:
输入: “)()())”
输出: 4
示例 3:
输入: “()(())”
输出: 4
class Solution {public int longestValidParentheses(String s) {/** 【栈】* * 我们维护一个栈,值为下标* 每次遇到'('时,毫不犹豫的将其下标入栈,等待被配对弹出;* 每次遇到')'时,就执行一次弹出操作:* 如果这时栈空了,说明没有'('能与其配对,它的下标入栈(除非遇到下一个无法配对的')',它将永远垫底)* 如果栈不空,则尝试更新maxLen,此时的有效括号长度为此时的小标,与弹出后此时的栈顶的下标之差* * 理解:* 1.每次'('下标入栈,都是【等待被配对弹出】的; * 每次')'下标入栈,都说明此时有效串断了,它的下标现在垫底用的,作为【新串开始的前一个字符位置】* 2.一个重要的技巧:先入栈一个-1。这相当于在原串的最前面加了个')'———这不影响结果,而且解决了第一个字符为')'的情况下,pop出错的问题* 3.解决【括号字符串】问题有一个看似奇怪但绝对安全的思路: ')'去配对它左边的第一个'(',然后消掉 ,准没错!!!!!* 4.还是那个重要思路: ')'是有可能作为结束一个有效串的标志的* 5.再理解一下-1吧!它是一个有效串结束的标志,作为参照下标,等价于多出的')'。当下一个')'将其顶替后,它的使命也就结束了*/Stack<Integer> stack = new Stack<>();stack.add(-1);int maxLen = 0;for(int i = 0; i < s.length(); i++) {if(s.charAt(i) == '(') {stack.add(i);}else {stack.pop();if(stack.isEmpty()) {stack.add(i);}else {maxLen = Math.max(maxLen, i - stack.peek());}}}return maxLen;}
}
class Solution {/** 【动态规划】* * 举个例子帮助理解:* 下标:0 1 2 3 4 5* ( ) ( ( ) )* dp: 0 2 0 0 2 6* dp表的值表示以当前括号做为结尾的最长有效括号长度————以'('结尾,必然填0,不用管; 以')'结尾,长度由三部分组成(★☆★):* * 以index=5处的')'为例:* 先找到左边未被匹配的第一个'('。index=3处的'('已经是已经匹配的有效字符串的一部分,所以应该看这个有效字符串之前的第一个是否为'(',* 即s.charAt(i - dp[i-1] - 1)是否为'('* 如果是'('的话,则匹配上,原先的串被【加长】。因此第一部分是原先的串长dp[i-1];第二部分是刚刚匹配上从而加长的2* 即如果此时的')'能和 i - dp[i-1] - 1 处的'('匹配上,则 dp[i] = dp[i - 1] + 2* 千万别忘了,当这括号匹配上时(index=5,2),就有可能接上之前的有效串(在index=5,2匹配之前,index=3,4显然和index=0,1是独立的,但现在接上了)* 即再尝试加上刚匹配上的'('之前1个位置的dp值,即再加上dp[i - dp[i-1] - 2]* * 三部分呼之欲出:dp[i] = dp[i - dp[i-1] - 2] + dp[i-1] + 2* 但是:一来 i - dp[i-1] - 1 ,即有效串左边的第一个位置有可能不存在需要提前判断:i - dp[i-1] - 1 >= 0 && ...* 二来 一次匹配后,在尝试接上再之前的有效串的时候,也可能不存在:i - dp[i-1] - 2 >= 0 ...* */public int longestValidParentheses(String s) {int len = s.length();int[] dp = new int[len];int maxLen = 0;for(int i = 1; i < len; i++) {if(s.charAt(i) == ')') {if(i - dp[i-1] - 1 >= 0 && s.charAt(i - dp[i-1] - 1) == '(') {dp[i] = (i - dp[i-1] - 2 >= 0 ? dp[i - dp[i-1] - 2] : 0) + dp[i-1] + 2;}maxLen = Math.max(maxLen, dp[i]);}}return maxLen;}
}
class Solution {/** 【括号计数】* * 之前在[回溯法生成有效括号]时,使用的就是这种思路:从左向右遍历,如果右括号')'的个数多于左括号'('的个数,那就没有继续的必要了* 这与【')'和'('相消】的括号的性质是相契合的* * 我们从左向右遍历,是'('就left计数器加一,是')'就给right计数器加一* 每次加一后都检查一遍:* 如果right > left,则这个串已经无效的,将计数器都置0重新开始* 如果right < left,说明右括号在【追赶】左括号,这个有效串的长度一直在加长* 如果right = left,说明恰好匹配,有效串长度为2*right 或 2*left* * 理解(!!!):* 你可能会有疑问,当左右括号数量相等,就一定有效?很容易举出这样的反例啊:"))(("* 其实,"数量相等"在计数时,当右括号多于左括号时,就清零一次,重新开始:* "))(("的前两个括号,并没有计数,也不可能出现left==right的情况* * 这个"相等",并不是left和right齐头并进,而是right对left的单方面追赶————这就是')'尝试消去'('的过程* 当right超过left,这段"赛跑"就结束了(这个有效串的长度终止了);需要让left先跑几步,right再去追它(需要先有若干'(',再让')'取匹配它)* * 还有一个致命的问题:当right一直没追上left时,就不会出现2*right 或 2*left的有效串长度更新,比如"(()"这种情况* 如何解决呢?* 从另一个方向开始跑,让left去追right————让'('去匹配')'* * * 通过这样的"两个方向的追赶",也就保证得出了最终的正确答案*/public int longestValidParentheses(String s) {int len = s.length();int left = 0;int right = 0;int maxLen = 0;for(int i = 0; i < len; i++) {if(s.charAt(i) == '(') {left++;}else {right++;}if(right == left) {maxLen = Math.max(maxLen, 2 * right);}else if(right > left) {left = 0;right = 0;}}right = 0;left = 0;for(int i = len - 1; i >= 0; i--) {if(s.charAt(i) == ')') {right++;}else {left++;}if(left == right) {maxLen = Math.max(maxLen, 2 * left);}else if(left > right) {right = 0;left = 0;}}return maxLen;}
}
部分题目来源
【Leetcode Q20】有效的括号
【Leetcode Q22】括号生成
【Leetcode Q32】最长有效括号
End ♬ By a Lolicon
【图解算法】彻底搞懂(括号匹配)——图解带你直击本质相关推荐
- C++两个函数可以相互递归吗_[算法系列] 搞懂递归, 看这篇就够了 !! 递归设计思路 + 经典例题层层递进
[算法系列] 搞懂递归, 看这篇就够了 !! 递归设计思路 + 经典例题层层递进 从学习写代码伊始, 总有个坎不好迈过去, 那就是遇上一些有关递归的东西时, 看着简短的代码, 怎么稀里糊涂就出来了. ...
- [算法系列] 搞懂递归, 看这篇就够了 !! 递归设计思路 + 经典例题层层递进
[算法系列] 搞懂递归, 看这篇就够了 !! 递归设计思路 + 经典例题层层递进 从学习写代码伊始, 总有个坎不好迈过去, 那就是遇上一些有关递归的东西时, 看着简短的代码, 怎么稀里糊涂就出来了. ...
- C语言的指针\数组用图解一次搞懂
目录 1.指针 1.1指针相关标识符 1.1.1 取地址符号 & 1.1.2 解引用符号 * 1.1.3 const 符号 1.1.4 算数运算(偏移概念) 1.2野指针 1.3多级指针 1. ...
- 算法之路(5)--括号匹配问题(Valid Parentheses)[leetcode]
题目: Given a string containing just the characters '(', ')', '{', '}', '[' and ']', determine if the ...
- C++paranthesis matching括号匹配的算法(附完整源码)
C++paranthesis matching括号匹配的算法 C++paranthesis matching括号匹配的算法完整源码(定义,实现,main函数测试) C++paranthesis mat ...
- 转载 硬核图解!30张图带你搞懂!路由器,集线器,交换机,网桥,光猫有啥区别?
硬核图解!30张图带你搞懂!路由器,集线器,交换机,网桥,光猫有啥区别?... 煎鱼(EDDYCJY) 于 2022-01-17 08:51:00 发布 1219 已收藏 22 文章标签: 交换机 网 ...
- 帮你彻底搞懂JS中的prototype、__proto__与constructor(图解)
帮你彻底搞懂JS中的prototype.__proto__与constructor(图解) 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明. 本文 ...
- 五分钟搞懂什么是红黑树(全程图解)
红黑树(又叫平衡二叉树),对很多童鞋来说,是既熟悉又陌生.熟悉是因为在校学习期间,准备面试时,这是重点.然后经过多年的荒废,如今已经忘记的差不多了.如果正在看文章的你,马上快要毕业,面临着找工作的压力 ...
- (转)帮你彻底搞懂JS中的prototype、__proto__与constructor(图解)
文章目录 1. 前言 2. _ _ proto _ _ 属性 3. prototype属性 4. constructor属性 5. 总结 提示:不要排斥,静下心来,认真读完,你就搞懂了!(可以先看一下 ...
- css股票曲线图图解,如何看懂股票曲线图,一文搞懂这些曲线所代表的含义!
原标题:如何看懂股票曲线图,一文搞懂这些曲线所代表的含义! 在股票技术分析中,我们经常要分析K线图,分时图等等,有些新手在入门的时候常常把两者弄混,所以统称它们为曲线图,实际它们就是K线图和分时图了, ...
最新文章
- 关于一些Linux SVN的安装使用
- tableview的两个重用cell方法
- pcap文件解析工具_【免费毕设】PHP网络数据包分析工具的设计与开发(源代码+论文)...
- SQLite VC环境搭建
- scala学习 之 环境搭建(一)
- git clone 报错:error: RPC failed; curl 18 transfer closed with outstanding read
- 菜鸟入门【ASP.NET Core】6:配置的热更新、配置的框架设计
- Objective-C基础之基本数据类型
- 更适合私有云的网络部署模式-动态路由
- pyodbc 连接informix
- mysql图书管理系统设计答辩_基于微信的图书管理系统毕业论文+任务书+开题报告+答辩PPT+前后台(Java+Mysql)源码及数据库文件...
- vue axios常用写法
- java openCV调用摄像头并以窗体显示出来
- 国产M0核风机量产程序开发方案… FOC电机控制开发方案…3电阻采样
- CS224n_2019_Assignment1: Exploring Word Vectors Coding Solution
- 词频统计,中文分词FMM,BMM博客
- 疫情过后,35岁老程序员年后第一天上班被公司劝退,该何去何从?
- Windows docker 安装 home assistant
- kkt条件里面的松弛互补条件推导
- Linux下用户的密码
热门文章
- 多任务学习(Multi-Task Learning)
- Linux CentOS 学习笔记
- 查看服务器ip配置信息,怎么查看服务器ip地址,怎么查看ip地址和端口
- Springboot配置suffix指定mvc视图的后缀
- 团队管理19--团队分工
- php安装失败,PHP编译安装时常见错误解决办法
- 【2021 年终总结】一年涨粉100倍,有规划始执行~成功一半
- 第十五周博客作业西北师范大学|李晓婷
- android icloud照片恢复软件,iCloud照片怎么恢复到手机 iCloud照片恢复到手机方法【详细步骤】...
- 郑州计算机安全协会安全员考试成绩,安全员C证考试结果查询