先看简单的例题1:如果序列中只有小括号‘(’和‘)’,那么怎么判断序列是否合法?
解法1:
step1:用一个指针遍历这个序列。
已知的结论:若序列是合法的,它需要满足下面2个条件。
1)指针遍历在序列的任何一个位置上,已遍历的部分,左括号数量>=右括号数量。
2)在指针遍历到序列的最后一个位置上,就是说整个序列,左括号数量==右括号数量。
示例:((()可能是合法的。 ((())))一定不合法。
step2:程序运行中只需要记录左括号的数量和右括号的数量即可。
代码:
bool isValid(char* s){ //bool返回序列s是否合法,序列s其实是一个字符串。
int lnum = 0,rnum = 0; //lnum是左括号的数量,rnum是右括号的数量。
int len = strlen(s); //len记录字符串s序列有多长。
for(int i=0; i<len; i++){ //step1:用i这个索引来遍历序列。索引i就相当于指针。
switch(s[i]){ //判断索引i指向的字符s[i]是不是左括号或右括号。
case '(': ++lnum; break; //若s[i]是左括号,则左括号计数+1。
case ')': ++rnum; break; //若s[i]是右括号,则右括号计数+1。
default : return false; //这个序列s,除了小括号以外还有别的字符,不合法。
}
if(lnum >= rnum) continue; //step2:在序列s已遍历的部分,左括号数量>=右括号数量,才合法
else return false; //左括号数量<右括号数量,如序列(())),不合法的。
}
//程序走到这里,意味着已经遍历完了序列s。这里已经剔除掉所有左括号数量<右括号数量的情况。
//遍历完序列s的所有元素,若左括号数量==右括号数量,就合法,返回true。
//若左括号数量>右括号数量,依然不合法。
return lnum == rnum;
}
上面的程序还有一点点优化的余地,就是可以不用lnum和rnum这两个变量去分别记录左右括号的数量,而是可以只设置一个变量num来记录左右括号数量的差值即可。
若 左括号数量>=右括号数量,则左右括号的插值 num = lnum-rnum >= 0 。
遍历序列,遇到左括号,num+1,遇到右括号,num-1。
中途如果num>=0就合理,最后num==0就合理。
bool isValid(char* s){ //bool返回序列s是否合法,序列s其实是一个字符串。
int num = 0; //num是左括号数量和右括号数量的差值。num = 左括号数量-右括号数量
int len = strlen(s); //len记录字符串s序列有多长。
for(int i=0; i<len; i++){ //step1:用i这个索引来遍历序列。索引i就相当于指针。
switch(s[i]){ //判断索引i指向的字符s[i]是不是左括号或右括号。
case '(': ++num; break; //若s[i]是左括号,则num+1。
case ')': --num; break; //若s[i]是右括号,则num-1。
default : return false; //这个序列s,除了小括号以外还有别的字符,不合法。
}
if(num >= 0) continue;
//step2:在序列s已遍历的部分,左括号数量>=右括号数量(差值 num>=0),才合法。
else return false;
//左括号数量<右括号数量(差值 num<0),如序列(())),不合法的。
}
//遍历完序列s的所有元素,若左括号数量==右括号数量,即差值num==0,就合法,返回true。
//若左括号数量>右括号数量,即差值>0,依然不合法,返回false。
return num == 0;
}
接下来要利用栈来解决上面的问题。
解法2:
遍历序列的时候,遇到了左括号‘(’,将其入栈。
若遇到右括号‘)’,这个右括号‘)’能和栈中的一个左括号‘(’凑成一对‘()’。则将栈中的一个左括号出栈与此右括号抵消。
正确的思维:
将一对括号‘()‘看成是一个完整的事件。(())看作是一个大事件嵌套了一个小事件。
这里的23和45就是小事件,16是大事件。想解决16这个大事件必须得先把23和45解决了。
把一对括号‘()’,这一个完整的事件,看成是一个函数funcA()。
则上面序列“(()())”可写成下面代码:
最外层的funcA就是大事件16,里面的funcA是需要解决小事件23和45。
栈这个数据结构,正好适合用来处理这种大事件里面套着小事件之类的问题。
这里比较特殊,这还是一个递归问题。故栈还可以用来处理递归问题。
一般情况:把序列"([]{()}())"可理解成下面代码:
把"()"作为funcA,"[]"作为funcB,"{}"作为funcC。
上面代码就是大事件嵌套小事件,而并非递归。这适用于用栈来解决问题。上面的递归只是特殊情况,当然因为是特殊情况,栈也能够解决递归问题了。
括号匹配:leetcode 20
请看题目:
题目的中文版(力扣官网原文):
简要概括题目:给你一个只有括号的序列,看看它合不合法。
解题方法,利用栈即可。
这道题具体代码实现,详情见:https://mp.csdn.net/mp_blog/creation/editor/13073726