目录
232. 用栈实现队列
- 使用两个栈实现,一个为输入栈,一个为输出栈
- 输入正常push,输出时需要将输入栈中的元素push进输出栈pop操作
class MyQueue {
public:
stack<int> stIn;
stack<int> stOut;
MyQueue() {
}
void push(int x) {
stIn.push(x);
}
int pop() {
if (stOut.empty()) {
while (!stIn.empty()) {
stOut.push(stIn.top());
stIn.pop();
}
}
int res = stOut.top();
stOut.pop();
return res;
}
int peek() {
if (stOut.empty()) {
while (!stIn.empty()) {
stOut.push(stIn.top());
stIn.pop();
}
}
return stOut.top();
}
bool empty() {
if (!stIn.empty() || !stOut.empty()) return false;
else return true;
}
};
225. 用队列实现栈
- 感觉比上一题难一点(ಥ_ಥ)
- 使用两个队列q1 q2 ,弹出时将q1所有元素存入q2,只剩下最后一个元素并将其弹出,然后更新q1,将q2设置为空
class MyStack {
public:
queue<int> q1;
queue<int> q2;
MyStack() {
}
void push(int x) {
q1.push(x);
}
int pop() {
int size = q1.size();
size--;
while (size--) {
q2.push(q1.front());
q1.pop();
}
int res = q1.front();
q1.pop();
q1 = q2;
while (!q2.empty()) {
q2.pop();
}
return res;
}
int top() {
return q1.back();
}
bool empty() {
return q1.empty();
}
};
20. 有效的括号
- 根据左括号种类在栈中存入对应种类的右括号,这样就可以很容易的和后面字符串中的右括号进行比对
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('}');
// s[i]为右括号,栈中没有元素或栈顶元素不匹配
else if (st.empty() || st.top() != s[i]) return false;
else st.pop();
}
return st.empty();
}
};
1047. 删除字符串中的所有相邻重复项
- 没仔细看题,题干中说删除相邻两个,还以为是删除所有的相邻元素
- 思路和括号匹配相似
class Solution {
public:
string removeDuplicates(string s) {
stack<char> st;
for (int i = 0; i < s.size(); i++ ) {
if (st.empty() || s[i] != st.top()) st.push(s[i]);
else st.pop();
}
string res = "";
while (!st.empty()) {
res.push_back(st.top());
st.pop();
}
reverse(res.begin(), res.end());
return res;
}
};
150. 逆波兰表达式求值
- 首先是将string转为longlong
int转string:to_string()
string转int、long、longlong:stoi(), stol(), stoll()
- 主要的思路是遇到运算符号利用其之前的两个元素运算,结果push进栈中
- 要注意的是 - 和 / 运算的被减数和被除数
class Solution {
public:
int evalRPN(vector<string>& tokens) {
stack<long long> st;
int sum = 0;
for (int i = 0; i < tokens.size(); i++) {
if (tokens[i] == "+") {
int a = st.top();
st.pop();
int b = st.top();
st.pop();
st.push(a + b);
} else if (tokens[i] == "-") {
int a = st.top();
st.pop();
int b = st.top();
st.pop();
st.push(b - a);
} else if (tokens[i] == "*") {
int a = st.top();
st.pop();
int b = st.top();
st.pop();
st.push((long long)a * b);
} else if (tokens[i] == "/") {
int a = st.top();
st.pop();
int b = st.top();
st.pop();
st.push(b / a);
} else {
st.push(stoi(tokens[i]));
}
}
return st.top();
}
};
239. 滑动窗口最大值
- 这题最大的难点就是要根据滑动窗口构造出一个对应的单调递减队列
- 队列中不需要维护窗口中的所有元素,只需要维护那些有可能成为最大值的元素即可
设计单调队列的时候,pop,和push操作要保持如下规则:
- pop(value):如果窗口移除的元素value等于单调队列的出口元素,那么队列弹出元素,否则不用任何操作
- push(value):如果push的元素value大于入口元素的数值,那么就将队列入口的元素弹出,直到push元素的数值小于等于队列入口元素的数值为止
保持如上规则,每次窗口移动的时候,只要问que.front()就可以返回当前窗口的最大值。
class Solution {
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
MyQueue que;
vector<int> res;
for (int i = 0; i < k; i++) {
que.push(nums[i]);
}
res.push_back(que.front());
for (int i = k; i < nums.size(); i++) {
que.pop(nums[i - k]); // 移除滑动窗口头元素
que.push(nums[i]); // 加入滑动窗口尾元素
res.push_back(que.front());
}
return res;
};
class MyQueue {
public:
deque<int> q;
void pop(int value) {
if (!q.empty() && value == q.front()) q.pop_front();
}
void push(int value) {
while (!q.empty() && q.back() < value) {
q.pop_back();
}
q.push_back(value);
}
int front() {
return q.front();
}
};
};
347. 前 K 个高频元素
- 这里用到了priority_queue(优先级队列),关于优先级队列的用法可以参考
- 首先我们用一个unoredered_map将数组元素频率全部保存
- 然后重载比较函数,将存的数据放入小顶堆(堆头是最小元素,也最先被弹出)
- 学到了优先级队列的使用o(╥﹏╥)o
class Solution {
public:
class cmp{
public:
bool operator()(const pair<int,int> &a, const pair<int,int> &b) {
return a.second > b.second;
}
};
vector<int> topKFrequent(vector<int>& nums, int k) {
unordered_map<int,int> m;
priority_queue<pair<int,int>, vector<pair<int,int>>, cmp> pri_que;
for (int i = 0; i < nums.size(); i++) {
m[nums[i]]++;
}
for (auto &i:m) {
pri_que.push(i);
if (pri_que.size() > k) pri_que.pop();
}
vector<int> res(k);
for (int i = 0; i < k; i++) {
res[k - i - 1] = pri_que.top().first;
pri_que.pop();
}
return res;
}
};
- 关于重载的解释
大家对这个比较运算在建堆时是如何应用的,为什么左大于右就会建立小顶堆,反而建立大顶堆比较困惑。
确实 例如我们在写快排的cmp函数的时候,
return left>right
就是从大到小,return left<right
就是从小到大。优先级队列的定义正好反过来了,可能和优先级队列的源码实现有关(我没有仔细研究),我估计是底层实现上优先队列队首指向后面,队尾指向最前面的缘故!