LeetCode20. 有效的括号
-
思路:
- 有效的括号,什么是有效的括号?肯定是成双成对的才行,比如(),[],{} 这 3 种括号各自成双成对。也正因为要成双成对,所以括号的总个数,一定就不能是奇数,这可以排除一种情况。
- 这题并没有涉及具体的算法思想,只是要按照它的规则去实现就好了。
- 括号无效的情况有3种。
- 第一种情况,字符串里左方向的括号多了 ,所以不匹配。
- 第二种情况,括号没有多余,但是 括号的类型没有匹配上。
- 第三种情况,字符串里右方向的括号多了,所以不匹配。
- 实现思路:
- 遍历到左边的符号时,在栈里压入一个其对应的右方向括号。遍历到右方向的括号的不压入。
- 为什么呢?
- 因为在括号有效时,遇到左方向的括号,字符串中肯定会有右方向的括号与其对应,所以在遍历到左方向的括号时压入一个其对应的右方向的括号。如果之后在字符串中遍历到的这个括号是右方向的括号,就取出栈顶处的右方向的括号与当前遍历到的右方向括号 进行比较。比对不上,说明括号无效,比对上了,就继续遍历字符串的下一个字符。
-
代码:
class Solution { public: bool isValid(string s) { //长度为奇数,直接排除 if(s.size() % 2) {return false;} //正常偶数个 stack<char>st; for(int i = 0; i < s.size(); i++) { //遍历到3种左括号时,push其对应的右括号入栈 if(s[i] == '(' ) {st.push(')');} else if (s[i] == '[' ) {st.push(']');} else if (s[i] == '{') {st.push('}'); } // 第三种情况:原字符串中,右括号多。栈中压入的是右方向的括号。如果在遍历过程中栈已经为空了,当前的遍历的右方向的括号 ,说明右方向的括号多了。无效,return false // 第二种情况:没匹配上。 遍历到字符串中的右方向的括号时,栈顶不是其匹配的,也无效 else if (st.empty() || st.top() != s[i]) { return false; } // 右方向括号比对成功,弹出栈顶的这个右方向括号 else st.pop(); } // 第一种情况:字符串中左方向括号多,这种情况是遍历完了字符串,但是栈不为空。又因为遍历到一个左方向括号就压入一个右方向括号,遍历完,栈不为空,就是左方向括号多了,无效return false,如果遍历完,栈空的,说明有效都匹配了return true return st.empty(); } };
-
时间复杂度: O(n)
- 一层for循环的遍历。
-
总结:
括号匹配的,只在遍历到左方向的括号时,才压入其对应的右方向括号。遍历到有方向的括号,不压入,而是取出栈顶的右方向括号进行比较。这个思路确实新奇也有效。
LeetCode1047. 删除字符串中的所有相邻重复项
这也是一道栈的经典题目。
-
思路:
- 给我们一条字符串,然后我们要删除相邻相同的元素,删除此次相邻相同元素后,如果又有新的相邻相同元素就接着删除。直到没有相邻相同元素为止。
- 因为我们想要删除相邻重复项的时候,那我们是不是要在遍历这一项的时候,还要知道上一项是什么,并跟它比较。考虑的这个思路,我们可以用到 栈 来完成。
- 栈的目的,就是存放我们遍历过的元素。遍历当前元素时,去栈里看一下栈顶这个元素是不是相同元素,是的话把这个元素pop掉,不是的话,把这个元素放进栈里。
- 最终字符串的元素遍历结束后,从栈里pop出来的元素是倒序的,反转一下,就得到了我们想要的结果。
- 考虑到用 栈 存放之后,还要考虑弹出,反转,有些繁琐,也可以试试 用 字符串来模拟 栈的操作。 用字符串的尾部进入, 有元素要进入就push_back() 从尾部进入,有相邻相同元素要弹出就pop_back(),最终返回这个字符串就好了。
-
代码:
class Solution { public: string removeDuplicates(string s) { string result; for(int i = 0 ; i < s.size(); i++) { //刚开始时,result为空,要放进去;之后的比对不上也要放进去 if(result.empty() || s[i] != result.back()) {result.push_back(s[i]);} //比对成功,是相邻相同元素,就弹出 else {result.pop_back();} } return result; } };
-
时间复杂度: O(n)
- 一层for循环,自然是O(n)
-
总结: 这题有点“消消乐”的感觉。用 栈 的思想最合适了。栈就适合处理 符号匹配,消消乐的问题。
LeetCode150. 逆波兰表达式求值
-
思路:
- 题目要求根据 逆波兰表达式 来求表达式的值
- 什么是逆波兰表达式?
- 逆波兰表达式是一种后缀表达式,后缀其实就是指运算符写在后面。而我们从小到大用的都是中缀表达式,就是把运算符写中间。例如 1 + 2 ,8 * 9 这些,他们的运算符都在中间。
- 这题也是用到 栈 的一道经典题。
- 做题思路:遇到数字就入栈;遇到运算符就取出栈顶的两个数字进行计算,再将结果压入栈中。
-
代码:
class Solution { public: int evalRPN(vector<string>& tokens) { stack<long long> st; long long nums1,nums2; //遍历字符串token for(int i =0; i<tokens.size(); i++) { //遇见运算符就取出2个元素进行计算 if(tokens[i]=="+" || tokens[i]=="-" || tokens[i]=="*" || tokens[i]=="/") { //nums1取的是表达式里算符右边的数,nums2取的是左边的 nums1 = st.top(); st.pop(); nums2 = st.top(); st.pop(); if (tokens[i] == "+") st.push(nums2 + nums1); if (tokens[i] == "-") st.push(nums2 - nums1); if (tokens[i] == "*") st.push(nums2 * nums1); if (tokens[i] == "/") st.push(nums2 / nums1); } //否则,遇见数字,把数字压进去 //stoll函数把字符串转换为long long int 类型的整数 else{ st.push(stoll(tokens[i]));} } int result = st.top(); st.pop(); return result; } };
-
时间复杂度:O(n)
-
总结:
- 这题也是栈的经典题目。到现在我们已经接触过 使用 栈 来解决的3道题了。它们都有一种消消乐的感觉,比如这题 遇到运算符,就消掉栈顶的2个数字,并压入计算的结果。LeetCode1047. 删除字符串中的所有相邻重复项 是 遇到相邻相同的就消。LeetCode20. 有效的括号 是利用栈 “消掉” 括号。