20. 有效的括号
给定一个只包括 '(',')','{','}','[',']' 的字符串,判断字符串是否有效。
有效字符串需满足:
- 左括号必须用相同类型的右括号闭合。
- 左括号必须以正确的顺序闭合。
- 注意空字符串可被认为是有效字符串。
示例 1:
- 输入: "()"
- 输出: true
示例 2:
- 输入: "()[]{}"
- 输出: true
思路:这道题在以前编译原理课设制作编译器的时候遇到过
很明显这道题要用栈的思路先进后出
注意点:就是输入左边的符号的时候要把相应右边的符号压入栈,这样方便比较
#include <stack>
#include <string>
using namespace std;
class Solution {
public:
bool isValid(string 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(']'); // 如果是 '[',压入对应的右括号 ']'
} else {
// 如果是右括号,检查栈是否为空,或者栈顶是否匹配当前右括号
if (st.empty() || st.top() != s[i]) {
return false; // 不匹配直接返回 false
}
st.pop(); // 匹配成功,弹出栈顶元素
}
}
// 最后检查栈是否为空,若为空说明所有括号匹配成功
return st.empty();
}
};
这是我自己写的代码:
思路:
-
栈的特点:后进先出
栈的特点使得它可以快速判断当前字符是否与前一个字符相同,从而决定是否需要移除字符。 -
逐一遍历字符串
遍历字符串的每个字符,对每个字符进行如下操作:- 如果栈为空,直接将字符压入栈中。
- 如果栈非空,检查栈顶字符是否与当前字符相同:
- 若相同:表示找到一对相邻重复字符,将栈顶字符弹出,移除这对字符。
- 若不同:将当前字符压入栈中。
-
栈中的内容即为最终结果
- 遍历完成后,栈中的字符就是所有消除了相邻重复字符后剩下的字符。
- 注意:栈中的字符顺序是从栈底到栈顶,而实际字符串顺序是从左到右,因此需要反转栈中的内容以得到最终结果。
class Solution {
public:
string removeDuplicates(string s) {
stack<char> st; // 修正 "stacck" 为 "stack<char>"
for (int i = 0; i < s.size(); i++) {
if (st.empty() || st.top() != s[i]) { // 修正 "st==empty()" 为 "st.empty()",并且修正 "st.top" 为 "st.top()"
st.push(s[i]);
} else { // 如果字符与栈顶字符相同
st.pop();
}
}
// 将栈中的字符还原为字符串
string result = "";
while (!st.empty()) {
result += st.top();
st.pop();
}
reverse(result.begin(), result.end()); // 栈中字符顺序为倒序,需要反转
return result;
}
};
老师的做法的话就不用反转字符串
用字符串来模拟栈的话就可以直接找到字符串的顶和尾,并且弹出和压入
代码如下:
class Solution {
public:
string removeDuplicates(string s) {
string result = ""; // 用字符串模拟栈
for (char c : s) {
if (!result.empty() && result.back() == c) { // 如果栈顶与当前字符相同
result.pop_back(); // 弹出栈顶
} else {
result.push_back(c); // 压入当前字符
}
}
return result;
}
};
50. 逆波兰表达式求值
根据 逆波兰表示法,求表达式的值。
有效的运算符包括 + , - , * , / 。每个运算对象可以是整数,也可以是另一个逆波兰表达式。
说明:
整数除法只保留整数部分。 给定逆波兰表达式总是有效的。换句话说,表达式总会得出有效数值且不存在除数为 0 的情况。
示例 1:
- 输入: ["2", "1", "+", "3", " * "]
- 输出: 9
- 解释: 该算式转化为常见的中缀算术表达式为:((2 + 1) * 3) = 9
示例 2:
- 输入: ["4", "13", "5", "/", "+"]
- 输出: 6
- 解释: 该算式转化为常见的中缀算术表达式为:(4 + (13 / 5)) = 6
以下是我自己写的思路:
没想到的:
使用 std::stoi
将其转换为整数
-
逆波兰表达式的基本特点:
- 遇到数字时,将其压入栈中。
- 遇到操作符时,从栈中弹出两个数字,进行计算,将计算结果重新压入栈。
-
步骤:
- 遍历表达式列表
tokens
。 - 如果当前元素是数字:
- 使用
std::stoi
将其转换为整数,压入栈中。
- 使用
- 如果当前元素是操作符:
- 弹出栈顶的两个数字,假设为
b
和a
。 - 根据操作符执行计算:
a + b
,a - b
,a * b
, 或a / b
。 - 将计算结果压入栈中。
- 弹出栈顶的两个数字,假设为
- 遍历结束后,栈顶元素即为最终结果。
- 遍历表达式列表
-
注意事项:
- 运算顺序:对于减法和除法,弹出的第一个数字是右操作数,第二个数字是左操作数。
- 操作数和操作符的输入顺序必须符合逆波兰表达式的规则。
#include <stack>
#include <vector>
#include <string>
#include <cstdlib> // for stoi
using namespace std;
class Solution {
public:
int evalRPN(vector<string>& tokens) {
stack<int> st;
for (int i = 0; i < tokens.size(); i++) {
// 判断是否为操作符
if (tokens[i] != "+" && tokens[i] != "-" && tokens[i] != "*" && tokens[i] != "/") {
// 是数字,转换为整数并压入栈
st.push(stoi(tokens[i]));
} else {
// 是操作符,弹出两个操作数
int b = st.top(); st.pop();
int a = st.top(); st.pop();
// 执行相应的操作并将结果压入栈
if (tokens[i] == "+") st.push(a + b);
else if (tokens[i] == "-") st.push(a - b);
else if (tokens[i] == "*") st.push(a * b);
else if (tokens[i] == "/") st.push(a / b);
}
}
// 返回最终结果
return st.top();
}
};
239. 滑动窗口最大值
给你一个整数数组 nums
,有一个大小为 k
的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k
个数字。滑动窗口每次只向右移动一位。
返回 滑动窗口中的最大值 。
示例 1:
输入:nums = [1,3,-1,-3,5,3,6,7], k = 3 输出:[3,3,5,5,6,7] 解释: 滑动窗口的位置 最大值 --------------- ----- [1 3 -1] -3 5 3 6 7 3 1 [3 -1 -3] 5 3 6 7 3 1 3 [-1 -3 5] 3 6 7 5 1 3 -1 [-3 5 3] 6 7 5 1 3 -1 -3 [5 3 6] 7 6 1 3 -1 -3 5 [3 6 7] 7
示例 2:
输入:nums = [1], k = 1 输出:[1]
下面是我自己写的
这个用数组的代码,代码时间复杂度为 O(n×k)O(n \times k)O(n×k),会导致在大数据量情况下性能不佳。
#include <vector>
#include <algorithm>
using namespace std;
class Solution {
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
vector<int> result; // 存储结果
// 检查边界条件
if (nums.empty() || k <= 0) {
return result;
}
for (int i = 0; i <= nums.size() - k; i++) {
// 在每个滑动窗口中找到最大值
int temp = nums[i];
for (int j = 1; j < k; j++) {
temp = max(temp, nums[i + j]);
}
result.push_back(temp);
}
return result;
}
};
优化后的代码(使用双端队列提高效率):
如果你需要更高效的实现,可以用双端队列将复杂度优化到 O(n)O(n)O(n)。以下是推荐的高效实现:
#include <vector>
#include <deque>
using namespace std;
class Solution {
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
vector<int> result; // 存储结果
deque<int> dq; // 存储滑动窗口中可能的最大值下标
for (int i = 0; i < nums.size(); i++) {
// 移除窗口外的元素
if (!dq.empty() && dq.front() < i - k + 1) {
dq.pop_front();
}
// 移除队列中小于当前元素的所有元素
while (!dq.empty() && nums[dq.back()] < nums[i]) {
dq.pop_back();
}
// 添加当前元素下标
dq.push_back(i);
// 记录当前窗口的最大值
if (i >= k - 1) {
result.push_back(nums[dq.front()]);
}
}
return result;
}
};