目录
一、有效的括号-LeetCode 20
Leecode链接: leetcode 20
文章链接: 代码随想录
视频链接: B站
给定一个只包括 ‘(’,‘)’,‘{’,‘}’,‘[’,‘]’ 的字符串 s ,判断字符串是否有效。
有效字符串需满足:
左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
每个右括号都有一个对应的相同类型的左括号。
示例:
输入:s = "()"
输出:true
输入:s = "(]"
输出:false
输入:s = "()[]{}"
输出:true
思路
使用堆栈,遇到左括号就插入它自己,遇到右括号就弹出栈顶元素对比,如果有一个不匹配就返回false,直到遍历结果且栈中没有元素时才能返回true,如果栈中有剩余元素表示整体括号数量是一个奇数,也就是不匹配。
实现代码
//cpp
class Solution {
public:
bool isValid(string s) {
int len = s.size();
if(len%2 == 1) return false;
stack<char> st;
for(int i = 0;i<len;i++){
if(s[i] == '('|| s[i] == '{' || s[i] == '['){
st.push(s[i]);
}
else{
if(st.empty()){
return false;
}
char temp = st.top();
st.pop();
if(temp == '[' && s[i] != ']'){
return false;
}
else if(temp == '{' && s[i] != '}'){
return false;
}
else if(temp == '(' && s[i] != ')'){
return false;
}
}
}
if(!st.empty()){
return false;
}
return true;
}
};
//cpp
class Solution {
public:
bool isValid(string s) {
if (s.size() % 2 != 0) return false; // 如果s的长度为奇数,一定不符合要求
stack<char> st;
for (int i = 0; i < s.size(); i++) {
if (s[i] == '(') st.push(')');
else if (s[i] == '{') st.push('}');
else if (s[i] == '[') st.push(']');
// 第三种情况:遍历字符串匹配的过程中,栈已经为空了,没有匹配的字符了,说明右括号没有找到对应的左括号 return false
// 第二种情况:遍历字符串匹配的过程中,发现栈里没有我们要匹配的字符。所以return false
else if (st.empty() || st.top() != s[i]) return false;
else st.pop(); // st.top() 与 s[i]相等,栈弹出元素
}
// 第一种情况:此时我们已经遍历完了字符串,但是栈不为空,说明有相应的左括号没有右括号来匹配,所以return false,否则就return true
return st.empty();
}
};
个人问题
我在这直接插入字符本身,当然卡哥那种插入对应的右括号然后直接对比是否相等也可以,需要注意的是,我这里有个想法与卡哥不一致,每道题例子数量有限,每个人不可能都想得到十全十美的代码解法,leecode上有很多你想不到的测试案例,只要能根据结果找到错误所在,并给出解决方案,这就是能力,谁都不可能一次ac所有题目代码,临场救急也是一种能力,所以不要有心理负担。
总结
整体比较简单,需要注意各个陷阱案例,在if分支中尽量考虑到可能的情况。算法时间复杂度为O(n),空间复杂度为O(n)。
二、删除字符串中的所有相邻重复项-LeetCode 1047
Leecode链接: LeetCode 1047
文章链接: 代码随想录
视频链接: B站
给出由小写字母组成的字符串 S,重复项删除操作会选择两个相邻且相同的字母,并删除它们。
在 S 上反复执行重复项删除操作,直到无法继续删除。
在完成所有重复项删除操作后返回最终的字符串。答案保证唯一。
示例:
输入:"abbaca"
输出:"ca"
解释:
例如,在 "abbaca" 中,我们可以删除 "bb" 由于两字母相邻且相同,这是此时唯一可以执行删除操作的重复项。之后我们得到字符串 "aaca",其中又只有 "aa" 可以执行重复项删除操作,所以最后的字符串为 "ca"。
思路
一边遍历字符串,一边把字符压栈,压栈规则如下:如果栈为空,直接压栈;如果不为空且栈顶元素与当前遍历元素不同,压栈;以上两个条件都不满足,则将栈顶元素弹出。但也可以不用栈,直接一边遍历一边插入,插入前对比是否相同再决定是否使用尾删法。
实现代码
//cpp
class Solution {
public:
string removeDuplicates(string s) {
stack<char> st;
string res = "";
for(char c : s){
if(st.empty()){
st.push(c);
}
else{
if(st.top() == c){
st.pop();
}
else{
st.push(c);
}
}
}
while(!st.empty()){
res += st.top();
st.pop();
}
reverse(res.begin(),res.end());
return res;
}
};
//cpp
class Solution {
public:
string removeDuplicates(string S) {
string result;
for(char s : S) {
if(result.empty() || result.back() != s) {
result.push_back(s);
}
else {
result.pop_back();
}
}
return result;
}
};
个人问题
在实现代码时,for循环那里我最初使用的形式为:for(int i = 0;i<s.size();i++),单个结果通过,但提交时出现内存超出限制,使用c++11标准后没有报错。
总结
题目需要删除的是相邻元素,类似于括号那题,需要配成一对的去考虑都可以用栈的思想,时间复杂度为O(n),空间复杂度为O(n),不使用栈情况下:时间复杂度为O(n),空间复杂度为O(1)。
三.逆波兰表达式求值-LeeCode 150
Leecode链接: LeetCode 150
文章链接: 代码随想录
视频链接: B站
给你一个字符串数组 tokens ,表示一个根据 逆波兰表示法 表示的算术表达式。
请你计算该表达式。返回一个表示表达式值的整数。
注意:
有效的算符为 ‘+’、‘-’、‘*’ 和 ‘/’ 。
每个操作数(运算对象)都可以是一个整数或者另一个表达式。
两个整数之间的除法总是 向零截断 。
表达式中不含除零运算。
输入是一个根据逆波兰表示法表示的算术表达式。
答案及所有中间计算结果可以用 32 位 整数表示。
示例:
输入:tokens = ["2","1","+","3","*"]
输出:9
解释:该算式转化为常见的中缀算术表达式为:((2 + 1) * 3) = 9
思路
与有效括号那题几乎一样,遍历字符串数组,遇到数字字符串就压栈,遇到符号就出栈两个元素,将计算好的结果再压栈进去,直到遍历结束,返回的栈顶元素一定是运算结果。
实现代码
//cpp
class Solution {
public:
long long strtonum(string s) {
bool isNegative = false;
long long sum = 0;
int i = 0;
if (s[0] == '-') {
isNegative = true;
i++;
}
for (i; i < s.size(); i++) {
if (s[i] >= '0' && s[i] <= '9') {
sum = sum * 10 + (s[i] - '0');
}
}
if (isNegative) {
return -sum;
}
else {
return sum;
}
}
int evalRPN(vector<string>& tokens) {
stack<long long> st;
for (int i = 0; i < tokens.size();i++) {
if (tokens[i] != "+" && tokens[i] != "-" && tokens[i] != "*" && tokens[i] != "/") {
st.push(strtonum(tokens[i]));
}
else {
long long temp1 = st.top();
st.pop();
long long temp2 = st.top();
st.pop();
if (tokens[i] == "+") {
st.push(temp1 + temp2);
}
else if (tokens[i] == "-") {
st.push(temp2 - temp1);
}
else if (tokens[i] == "*") {
st.push(temp1 * temp2);
}
else {
st.push(temp2 / temp1);
}
}
}
long long res = st.top();
return res;
}
};
个人问题
个人编写的代码在判断是否是符号那里出了问题,使用逻辑或 || 符号,导致遇到运算符时也压入栈。正确逻辑应该是只有当都不是运算符时才能被判定为数字,需要压栈处理。我自己把字符串转数字的函数也实现了一遍,实现时需要考虑负数问题。当然也可以直接用string头文件包含的库函数。
总结
考验对栈的应用。时间复杂度O(n),空间复杂度O(n)。