这是我参与更文挑战的第 8 天,活动详情查看: 更文挑战
栈的特性及简单使用
栈是我们在面试以及工作中常用到的一种数据结构。
它在计算机中也广泛的被应用着,如典型的方法调用上。
栈的特性--先进后出(LIFO)
经典题--判断括号的合法性
【题目】字符串中只有字符'('和')'。合法字符串需要括号可以配对。比如:
输入:"()"
输出:true
解释:(),()(),(())是合法的。)(,()(,(()是非法的。
请你实现一个函数,来判断给定的字符串是否合法。
分析题目的时候,我们可以通过以下四步来进行:
- 模拟:模拟正确的运行情况,从行为上完成。
- 找规律。
- 匹配:找到适合的数据结构与算法。
- 考虑边界,即特殊情况。
通过书写一个正确的例子,我们对其进行模拟,可以得到以下规律:
- 只有所有的括号都完成匹配,才是合法的。
- 配对的符号可以消除掉,直到最后没有任何字符,便是合法的。
匹配:我们可以发现,括号消除的这个行为跟栈的行为十分相似,因此可以用栈来进行模拟。
边界:
- 字符串为空
- 字符串个数为奇数个
可通过画图来辅助自己理解
boolean isValid(String s) {
// 当字符串本来就是空的时候,我们可以快速返回true
if (s == null || s.length() == 0) {
return true;
}
// 当字符串长度为奇数的时候,不可能是一个有效的合法字符串
if (s.length() % 2 == 1) {
return false;
}
// 消除法的主要核心逻辑:
Stack<Character> t = new Stack<Character>();
for (int i = 0; i < s.length(); i++) {
// 取出字符
char c = s.charAt(i);
if (c == '(') {
// 如果是'(',那么压栈
t.push(c);
} else if (c == ')') {
// 如果是')',那么就尝试弹栈
if (t.empty()) {
// 如果弹栈失败,那么返回false
return false;
}
t.pop();
}
return t.empty();
}
- 时间复杂度:O(n)
- 空间复杂度:O(n)
做完一道题,我们还要从2个角度进行深度思考:
- 深度:这种解法还可以怎么优化?
- 广度:这种解法具有普适性吗?可以推广吗?
深度扩展
如果仔细观察,你会发现,栈中存放的元素是一样的。全部都是左括号'(',除此之外,再也没有别的元素,优化方法如下。
栈中元素都相同时,实际上没有必要使用栈,只需要记录栈中元素个数。
public boolean isValid(String s) {
// 当字符串本来就是空的时候,我们可以快速返回true
if (s == null || s.length() == 0) {
return true;
}
// 当字符串长度为奇数的时候,不可能是一个有效的合法字符串
if (s.length() % 2 == 1) {
return false;
}
// 消除法的主要核心逻辑:
int leftBraceNumber = 0;
for (int i = 0; i < s.length(); i++) {
// 取出字符
char c = s.charAt(i);
if (c == '(') {
// 如果是'(',那么压栈
leftBraceNumber++;
} else if (c == ')') {
// 如果是')',那么就尝试弹栈
if (leftBraceNumber <= 0) {
// 如果弹栈失败,那么返回false
return false;
}
--leftBraceNumber;
}
}
return leftBraceNumber == 0;
}
- 时间复杂度:O(n)
- 空间复杂度:O(1)
广度扩展
观察题目可以发现,栈中只存放了一个维度的信息:左括号'('和右括号')'。如果栈中的内容变得更加丰富一点,就可以得到下面这道扩展题。
【题目扩展】给定一个只包括 '(',')','{','}','[',']' 的字符串,判断字符串是否有效。有效字符串需满足:
左括号必须用相同类型的右括号闭合
左括号必须以正确的顺序闭合
注意空字符串可被认为是有效字符串
class Solution {
public boolean isValid(String s) {
if (s == null || s.length() == 0) {
return true;
}
if (s.length() % 2 == 1) {
return false;
}
Stack<Character> t = new Stack<Character>();
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (c == '{' || c == '(' || c == '[') {
t.push(c);
} else if (c == '}') {
if (t.empty() || t.peek() != '{') {
return false;
}
t.pop();
} else if (c == ']') {
if (t.empty() || t.peek() != '[') {
return false;
}
t.pop();
} else if (c == ')') {
if (t.empty() || t.peek() != '(') {
return false;
}
t.pop();
} else {
return false;
}
}
return t.empty();
}
}
因此,以后你在看到题目中类似配对、消除之类的动作时,可以采用栈来操作。