问题
给定一个只包括 ‘(’,’)’,’{’,’}’,’[’,’]’ 的字符串,判断字符串是否有效。
有效字符串需满足:
- 左括号必须用相同类型的右括号闭合。
- 左括号必须以正确的顺序闭合。
注意空字符串可被认为是有效字符串。
示例:
输入: "()"
输出: true
输入: "(]"
输出: false
输入: "([)]"
输出: false
输入: "{[]}"
输出: true
分析
一个不完善的方案
类似于编译器对程序中出现的括号的匹配性进行检查。
很容易想出一个简单的方法来解决这个问题:
- 从表达式的左侧开始,每次只处理一个括号。
- 假设我们遇到一个开括号(即
(
),表达式是否无效取决于在该表达式的其余部分的某处是否有相匹配的闭括号(即)
)。此时,我们只是增加计数器的值保持跟踪现在为止开括号的数目。left += 1
- 如果我们遇到一个闭括号,这可能意味着这样两种情况:
- 此闭括号没有与与之对应的开括号,在这种情况下,我们的表达式无效。当
left == 0
,也就是没有未配对的左括号可用时就是这种情况。 - 我们有一些未配对的开括号可以与该闭括号配对。当
left > 0
,也就是有未配对的左括号可用时就是这种情况。
- 此闭括号没有与与之对应的开括号,在这种情况下,我们的表达式无效。当
- 如果我们在 left == 0 时遇到一个闭括号(例如
)
),那么当前的表达式无效。否则,我们会减少left
的值,也就是减少了可用的未配对的左括号的数量。 - 继续处理字符串,直到处理完所有括号。
- 如果最后我们仍然有未配对的左括号,这意味着表达式无效。
代码的实现也很简单:
bool isValid(char* s) {
int a[3] = {0};
while (*s != '\0') {
switch (*s) {
case '(':
a[0]++;
break;
case '[':
a[1]++;
break;
case '{':
a[2]++;
break;
case ')':
a[0]--;
break;
case ']':
a[1]--;
break;
case '}':
a[2]--;
break;
default:
break;
}
if (a[0] < 0 || a[1] < 0 || a[2] < 0) {
return false;
}
s++;
}
if (a[0] != 0 || a[1] != 0 || a[2] != 0) {
return false;
}
return true;
}
但是,这只对简单的括号有效,比如出现的是同一种类型的括号。
当多种类型的括号同时出现时,该方法仅记录了最后的结果,而没有记录过程,会导致诸如([)]
类型的表达为true
。
因为这时括号的相对位置起到了重要作用,而简单计数方法就无法应对这种情况了。
使用栈解决问题
这让我想到了实现算术运算时的一个案例。如2 + 5 * 3
,计算时应该先运算乘法而不是加法。先计算的不是前面的而是后面的。
于是,后进先出的结构栈的概念便派上用场了。
具体过程如下:
- 初始化栈 S。
- 一次处理表达式的每个括号。
- 如果遇到开括号,我们只需将其推到栈上即可。这意味着我们将稍后处理它,让我们简单地转到前面的 子表达式。
- 如果遇到一个闭括号,那么我们检查栈顶的元素。如果栈顶的元素是一个 相同类型的 左括号,那么我们将它从栈中弹出并继续处理。否则,这意味着表达式无效。
- 如果到最后剩下的栈中仍然有元素,那么这意味着表达式无效。
代码
bool isValid(char* s) {
int len = strlen(s);
char *stack = NULL;
int cur = 0;
if (len % 2) {
return false;
}
stack = (char *)calloc(1, len + 1);
if (!stack) {
printf("calloc error: %m\n");
return false;
}
while (*s != NULL) {
if (*s == '(' || *s == '[' || *s == '{') {
stack[cur] = *s;
cur++;
} else if (cur < 1) {
free(stack);
return false;
} else if (*s == ')') {
if (stack[cur-1] == '(') {
stack[cur-1] = '\0';
cur--;
} else {
free(stack);
return false;
}
} else if (*s == ']') {
if (stack[cur-1] == '[') {
stack[cur-1] = '\0';
cur--;
} else {
free(stack);
return false;
}
} else if (*s == '}') {
if (stack[cur-1] == '{') {
stack[cur-1] = '\0';
cur--;
} else {
free(stack);
return false;
}
}
s++;
}
if (stack[0] != '\0') {
return false;
}
free(stack);
return true;
}