Python实战社群php
Java实战社群程序员
长按识别下方二维码,按需求添加web
扫码关注添加客服面试
进Python社群▲算法
扫码关注添加客服c#
进Java社群▲微信
做者丨labuladong
数据结构
来源丨labuladong编辑器
读完本文,你能够去力扣解决以下问题:
分布式
20.有效的括号(Easy)
921.使括号有效的最小插入(Medium)
1541.平衡括号串的最少插入(Medium)
判断合法括号串
对括号的合法性判断屡次在笔试中出现,现实中也很常见,好比说咱们写的代码,编辑器会检查括号是否正确闭合。并且咱们的代码可能会包含三种括号[](){}
,判断起来有一点难度。
来看一看力扣第 20 题「有效的括号」,输入一个字符串,其中包含[](){}
六种括号,请你判断这个字符串组成的括号是否合法。
举几个例子:
Input: "()[]{}" Output: true Input: "([)]" Output: false Input: "{[]}" Output: true
解决这个问题以前,咱们先下降难度,思考一下,如果只有一种括号()
,应该如何判断字符串组成的括号是否合法呢?
假设字符串中只有圆括号,若是想让括号字符串合法,那么必须作到:
每一个右括号)
的左边必须有一个左括号(
和它匹配。
好比说字符串()))((
中,中间的两个右括号左边就没有左括号匹配,因此这个括号组合是不合法的。
那么根据这个思路,咱们能够写出算法:
bool isValid(string str) { // 待匹配的左括号数量 int left = 0; for (int i = 0; i < str.size(); i++) { if (s[i] == '(') { left++; } else { // 遇到右括号 left--; } // 右括号太多 if (left == -1) return false; } // 是否全部的左括号都被匹配了 return left == 0; }
若是只有圆括号,这样就能正确判断合法性。对于三种括号的状况,我一开始想模仿这个思路,定义三个变量left1
,left2
,left3
分别处理每种括号,虽然要多写很多 if else 分支,可是彷佛能够解决问题。
但实际上直接照搬这种思路是不行的,好比说只有一个括号的状况下(())
是合法的,可是多种括号的状况下,[(])
显然是不合法的。
仅仅记录每种左括号出现的次数已经不能作出正确判断了,咱们要加大存储的信息量,能够利用栈来模仿相似的思路。栈是一种先进后出的数据结构,处理括号问题的时候尤为有用。
咱们这道题就用一个名为left
的栈代替以前思路中的left
变量,遇到左括号就入栈,遇到右括号就去栈中寻找最近的左括号,看是否匹配:
bool isValid(string str) { stack<char> left; for (char c : str) { if (c == '(' || c == '{' || c == '[') left.push(c); else { // 字符 c 是右括号 if (!left.empty() && leftOf(c) == left.top()) left.pop(); else // 和最近的左括号不匹配 return false; } } // 是否全部的左括号都被匹配了 return left.empty(); } char leftOf(char c) { if (c == '}') return '{'; if (c == ')') return '('; return '['; }
接下来说另外两个常见的问题,如何经过最小的插入次数将括号变成合法的?
平衡括号串(一)
先来个简单的,力扣第 921 题「使括号有效的最少添加」:
给你输入一个字符串s
,你能够在其中的任意位置插入左括号(
或者右括号)
,请问你最少须要几回插入才能使得s
变成一个合法的括号串?
好比说输入s = "())("
,算法应该返回 2,由于咱们至少须要插入两次把s
变成"(())()"
,这样每一个左括号都有一个右括号匹配,s
是一个合法的括号串。
这其实和前文的判断括号合法性很是相似,咱们直接看代码:
int minAddToMakeValid(string s) { // res 记录插入次数 int res = 0; // need 变量记录右括号的需求量 int need = 0; for (int i = 0; i < s.size(); i++) { if (s[i] == '(') { // 对右括号的需求 + 1 need++; } if (s[i] == ')') { // 对右括号的需求 - 1 need--; if (need == -1) { need = 0; // 需插入一个左括号 res++; } } } return res + need; }
这段代码就是最终解法,核心思路是以左括号为基准,经过维护对右括号的需求数need
,来计算最小的插入次数。须要注意两个地方:
一、当need == -1
的时候意味着什么?
由于只有遇到右括号)
的时候才会need--
,need == -1
意味着右括号太多了,因此须要插入左括号。
好比说s = "))"
这种状况,须要插入 2 个左括号,使得s
变成"()()"
,才是一个合法括号串。
二、算法为何返回res + need
?
由于res
记录的左括号的插入次数,need
记录了右括号的需求,当 for 循环结束后,若need
不为 0,那么就意味着右括号还不够,须要插入。
好比说s = "))("
这种状况,插入 2 个左括号以后,还要再插入 1 个右括号,使得s
变成"()()()"
,才是一个合法括号串。
以上就是这道题的思路,接下来咱们看一道进阶题目,若是左右括号不是 1:1 配对,会出现什么问题呢?
平衡括号串(二)
这是力扣第 1541 题「平衡括号字符串的最少插入次数」:
如今假设 1 个左括号须要匹配 2 个右括号才叫作合法的括号组合,那么给你输入一个括号串s
,请问你如何计算使得s
合法的最小插入次数呢?
核心思路仍是和刚才同样,经过一个need
变量记录对右括号的需求数,根据need
的变化来判断是否须要插入。
第一步,咱们按照刚才的思路正确维护need
变量:
int minInsertions(string s) { // need 记录需右括号的需求量 int res = 0, need = 0; for (int i = 0; i < s.size(); i++) { // 一个左括号对应两个右括号 if (s[i] == '(') { need += 2; } if (s[i] == ')') { need--; } } return res + need; }
如今想想,当need
为何值的时候,咱们能够肯定须要进行插入?
首先,相似第一题,当need == -1
时,意味着咱们遇到一个多余的右括号,显然须要插入一个左括号。
好比说当s = ")"
,咱们确定须要插入一个左括号让s = "()"
,可是因为一个左括号须要两个右括号,因此对右括号的需求量变为 1:
if (s[i] == ')') { need--; // 说明右括号太多了 if (need == -1) { // 须要插入一个左括号 res++; // 同时,对右括号的需求变为 1 need = 1; } }
另外,当遇到左括号时,若对右括号的需求量为奇数,须要插入 1 个右括号。由于一个左括号须要两个右括号嘛,右括号的需求必须是偶数,这一点也是本题的难点。
因此遇到左括号时要作以下判断:
if (s[i] == '(') { need += 2; if (need % 2 == 1) { // 插入一个右括号 res++; // 对右括号的需求减一 need--; } }
综上,咱们能够写出正确的代码:
int minInsertions(string s) { int res = 0, need = 0; for (int i = 0; i < s.size(); i++) { if (s[i] == '(') { need += 2; if (need % 2 == 1) { res++; need--; } } if (s[i] == ')') { need--; if (need == -1) { res++; need = 1; } } } return res + need; }
综上,三道括号相关的问题就解决了,其实咱们前文 合法括号生成算法 也是括号相关的问题,可是使用的回溯算法技巧,和本文的几道题差异仍是蛮大的,有兴趣的读者能够去看看。
程序员专栏 扫码关注填加客服 长按识别下方二维码进群
近期精彩内容推荐:
在看点这里好文分享给更多人↓↓