括号匹配这个问题,说难好难,但是说简单好像也挺简单,主要就是看我们的思路是否清晰,条例是否清楚。
基本问题是:给定一串字符,可能包括括号、数字、字母、标点符号、空格,检查这一串字符中的( ) ,[ ],{ }是否匹配,匹配输出yes,反之输出no。
我们可以先确定最基本的逻辑,就是对输入的数一一判断,如果是左括号就存起来,等到有有括号的时候进行配对,配对成功继续输入,错了就可以退出了。
于是,数据结构自然而然就是用栈了,而我用的是顺序栈。
先说说我最开始的想法啦。
1. 由于首先要读入一串字符,而且包括空格,但是判断的时候只能一个一个判断,所以就先给个数组存这串字符,用下标表示单个字符。
2. 然后为了方便地控制循环,我用strlen取得了字符的长度。
3. 设置了一个result参数,用于表示括号是否匹配的状态,1表示匹配成功,0表示不匹配。并将其初始化为1,这样可以避免额外讨论字符串中没有括号的情况。(没有括号也算匹配成功)
4. 接下来就是匹配环节了。遍历我们之前的数组,遇到左括号入栈;遇到右括号,把这个右括号和出栈的值尝试匹配,成功就继续扫描,失败不用扫描了,直接输出no吧。但是由于我用result表示是否匹配,所以我选择吧result的值变为0,然后退出循环。
5. 遍历的循环结束后,判断是否匹配,即判断result为0还是为1。
看起来没啥问题,但接着考虑一下,由于我的result初值为1,即默认匹配,会不会存在一种情况使括号即使不匹配,但是由于没有改变result的值而导致错误呢?
那么来看看什么时候result会改变。仅当扫描到右括号才会进行匹配尝试,而仅当匹配失败才会使result为0。也就是说,不扫描到右括号,result是没机会匹配的。
于是我们会想,只有左括号呢?如果没有对应的右括号,即使不匹配,result仍然为1。
那么就必须额外加一个条件来排除上述情况。显然这种情况下,栈中一定有左括号,即栈一定非空。那么反过来说,如果括号匹配,栈就一定为空。于是想到最后判断匹配的条件是:result=1且栈为空。
关键代码如下:
1 cin.getline(a, 100); 2 length = strlen(a); 3 for (int i = 0; i <= length; i++) { // 4 if (a[i] == '(' || a[i] == '[' || a[i] == '{') { //左括号入栈 5 Push(stack, a[i]); 6 } 7 else if (a[i] == ')' || a[i] == ']' || a[i] == '}') { //右括号,将其与出栈的字符尝试匹配 8 temp = Pop(stack); 9 if (!((a[i] == ')'&&temp =='(' )|| (a[i] == ']'&&temp == '[') || (a[i] == '}'&&temp == '{'))) { 10 result = 0; //如果不匹配就将result赋0 11 break; 12 } 13 } 14 } 15 if (result == 1&&stack.base==stack.top) { //匹配一定栈空,排除无右括号匹配,只有左括号 16 cout << "yes"; 17 } 18 else { 19 cout << "no"; 20 }
需要注意的是,栈顶指针所指的一直是顶元素的上一个。因此入栈的时候,现赋值再让栈顶指针+1;而出栈要反过来,先-1再赋值。一开始我先输出头指针的内容再让头指针减一,这就导致运行时错误。因为我忘了头指针为栈顶+1,因此其所指没有确切的值,导致非法访问。