目录
[七、前 K 个高频元素](#七、前 K 个高频元素)
一、用栈实现队列
232. 用栈实现队列 - 力扣(LeetCode)https://leetcode.cn/problems/implement-queue-using-stacks/description/
请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作(
push
、pop
、peek
、empty
):实现
MyQueue
类:
void push(int x)
将元素 x 推到队列的末尾int pop()
从队列的开头移除并返回元素int peek()
返回队列开头的元素boolean empty()
如果队列为空,返回true
;否则,返回false
说明:
- 你 只能 使用标准的栈操作 ------ 也就是只有
push to top
,peek/pop from top
,size
, 和is empty
操作是合法的。- 你所使用的语言也许不支持栈。你可以使用 list 或者 deque(双端队列)来模拟一个栈,只要是标准的栈操作即可。
bash
示例 1:
输入:
["MyQueue", "push", "push", "peek", "pop", "empty"]
[[], [1], [2], [], [], []]
输出:
[null, null, null, 1, 1, false]
解释:
MyQueue myQueue = new MyQueue();
myQueue.push(1); // queue is: [1]
myQueue.push(2); // queue is: [1, 2] (leftmost is front of the queue)
myQueue.peek(); // return 1
myQueue.pop(); // return 1, queue is [2]
myQueue.empty(); // return false
cpp
class MyQueue {
public:
// 定义栈
stack<int> Instack;
stack<int> Outstack;
MyQueue() {
}
void push(int x) {
Instack.push(x);
}
int pop() {
if (Outstack.empty()) {
// 输出栈为空
while (!Instack.empty()) {
Outstack.push(Instack.top());
Instack.pop();
}
}
int re = Outstack.top();
Outstack.pop();
return re;
}
int peek() {
// 使用已有函数
int re = this->pop();
Outstack.push(re);
return re;
}
bool empty() {
return (Instack.empty() && Outstack.empty());
}
};
/**
* Your MyQueue object will be instantiated and called as such:
* MyQueue* obj = new MyQueue();
* obj->push(x);
* int param_2 = obj->pop();
* int param_3 = obj->peek();
* bool param_4 = obj->empty();
*/
二、用队列实现栈
225. 用队列实现栈 - 力扣(LeetCode)https://leetcode.cn/problems/implement-stack-using-queues/description/
请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作(
push
、top
、pop
和empty
)。实现
MyStack
类:
void push(int x)
将元素 x 压入栈顶。int pop()
移除并返回栈顶元素。int top()
返回栈顶元素。boolean empty()
如果栈是空的,返回true
;否则,返回false
。注意:
- 你只能使用队列的标准操作 ------ 也就是
push to back
、peek/pop from front
、size
和is empty
这些操作。- 你所使用的语言也许不支持队列。 你可以使用 list (列表)或者 deque(双端队列)来模拟一个队列 , 只要是标准的队列操作即可。
bash
示例:
输入:
["MyStack", "push", "push", "top", "pop", "empty"]
[[], [1], [2], [], [], []]
输出:
[null, null, null, 2, 2, false]
解释:
MyStack myStack = new MyStack();
myStack.push(1);
myStack.push(2);
myStack.top(); // 返回 2
myStack.pop(); // 返回 2
myStack.empty(); // 返回 False
cpp
class MyStack {
public:
queue<int> Inqueue; // 单队列
MyStack() {
}
void push(int x) {
Inqueue.push(x);
}
int pop() {
for (int i = 0; i < Inqueue.size() - 1; i++) {
Inqueue.push(Inqueue.front());
Inqueue.pop();
}
int res = Inqueue.front();
Inqueue.pop();
return res;
}
int top() {
return Inqueue.back();
}
bool empty() {
return Inqueue.empty();
}
};
/**
* Your MyStack object will be instantiated and called as such:
* MyStack* obj = new MyStack();
* obj->push(x);
* int param_2 = obj->pop();
* int param_3 = obj->top();
* bool param_4 = obj->empty();
*/
三、有效的括号
20. 有效的括号 - 力扣(LeetCode)https://leetcode.cn/problems/valid-parentheses/description/
给定一个只包括
'('
,')'
,'{'
,'}'
,'['
,']'
的字符串s
,判断字符串是否有效。有效字符串需满足:
- 左括号必须用相同类型的右括号闭合。
- 左括号必须以正确的顺序闭合。
- 每个右括号都有一个对应的相同类型的左括号。
bash
示例 1:
输入: "()"
输出: true
示例 2:
输入: "()[]{}"
输出: true
示例 3:
输入: "(]"
输出: false
示例 4:
输入: "([)]"
输出: false
示例 5:
输入: "{[]}"
输出: true
思路:
本题遍历时存在三种情况:
第一种情况:已经遍历完了字符串,但是栈不为空,说明有相应的左括号没有右括号来匹配,所以 return false
第二种情况:遍历字符串匹配的过程中,发现栈里没有要匹配的字符。所以 return false
第三种情况:遍历字符串匹配的过程中,栈已经为空了,没有匹配的字符了,说明右括号没有找到对应的左括号 return false
cpp
class Solution {
public:
bool isValid(string s) {
stack<char> str;
for (int i = 0; i < s.length(); i++) {
if (s[i] == '(') str.push(')');
else if (s[i] == '{') str.push('}');
else if (s[i] == '[') str.push(']');
// 遍历的过程中栈已经为空,或者不匹配
else if (str.empty() || s[i] != str.top()) return false;
else str.pop();
}
return str.empty();
}
};
四、删除字符串中的所有相邻重复项
给出由小写字母组成的字符串
s
,重复项删除操作会选择两个相邻且相同的字母,并删除它们。在
s
上反复执行重复项删除操作,直到无法继续删除。在完成所有重复项删除操作后返回最终的字符串。答案保证唯一。
cpp
示例:
输入:"abbaca"
输出:"ca"
解释:例如,在 "abbaca" 中,我们可以删除 "bb" 由于两字母相邻且相同,这是此时唯一可以执行删除操作的重复项。之后我们得到字符串 "aaca",其中又只有 "aa" 可以执行重复项删除操作,所以最后的字符串为 "ca"。
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;
}
};
五、逆波兰表达式求值
给你一个字符串数组
tokens
,表示一个根据 逆波兰表示法 表示的算术表达式。请你计算该表达式。返回一个表示表达式值的整数。
注意:
- 有效的算符为
'+'
、'-'
、'*'
和'/'
。- 每个操作数(运算对象)都可以是一个整数或者另一个表达式。
- 两个整数之间的除法总是 向零截断 。
- 表达式中不含除零运算。
- 输入是一个根据逆波兰表示法表示的算术表达式。
- 答案及所有中间计算结果可以用 32 位 整数表示。
bash
示例 1:
输入:tokens = ["2","1","+","3","*"]
输出:9
解释:该算式转化为常见的中缀算术表达式为:((2 + 1) * 3) = 9
示例 2:
输入:tokens = ["4","13","5","/","+"]
输出:6
解释:该算式转化为常见的中缀算术表达式为:(4 + (13 / 5)) = 6
示例 3:
输入:tokens = ["10","6","9","3","+","-11","*","/","*","17","+","5","+"]
输出:22
解释:该算式转化为常见的中缀算术表达式为:
((10 * (6 / ((9 + 3) * -11))) + 17) + 5
= ((10 * (6 / (12 * -11))) + 17) + 5
= ((10 * (6 / -132)) + 17) + 5
= ((10 * 0) + 17) + 5
= (0 + 17) + 5
= 17 + 5
= 22
cpp
class Solution {
public:
int evalRPN(vector<string>& tokens) {
stack<long long> record;
for (int i = 0; i < tokens.size(); i++) {
if (tokens[i] == "+" || tokens[i] == "-" || tokens[i] == "*" || tokens[i] == "/") {
long long num1 = record.top(); record.pop();
long long num2 = record.top(); record.pop();
if (tokens[i] == "+") record.push(num2 + num1);
if (tokens[i] == "-") record.push(num2 - num1);
if (tokens[i] == "*") record.push(num2 * num1);
if (tokens[i] == "/") record.push(num2 / num1);
}
else {
record.push(stoll(tokens[i]));
}
}
return record.top();
}
};
六、滑动窗口最大值
239. 滑动窗口最大值 - 力扣(LeetCode)https://leetcode.cn/problems/sliding-window-maximum/description/
给你一个整数数组
nums
,有一个大小为k
的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的k
个数字。滑动窗口每次只向右移动一位。返回 滑动窗口中的最大值 。
bash
示例 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]
思路:
这是使用单调队列的经典题目。
队列没有必要维护窗口里的所有元素,只需要维护有可能成为窗口里最大值的元素就可以了,同时保证队列里的元素数值是由大到小的。C++中没有直接支持单调队列,需要我们自己来实现一个单调队列。
设计单调队列的时候,pop 和 push操作要保持如下规则:
- pop(value):如果窗口移除的元素value等于单调队列的出口元素,那么队列弹出元素,否则不用任何操作
- push(value):如果push的元素value大于入口元素的数值,那么就将队列入口的元素弹出,直到push元素的数值小于等于队列入口元素的数值为止
保持如上规则,每次窗口移动的时候,只要问 que.front() 就可以返回当前窗口的最大值。
具体的请查看网站: 代码随想录 (programmercarl.com)
cpp
class Solution {
private:
class MyQueue { //单调队列(从大到小)
public:
deque<int> que; // 使用deque来实现单调队列
void pop(int value) {
// 如果弹出的数值等于队列出口元素的数值,则弹出
if (!que.empty() && value == que.front()) {
que.pop_front();
}
}
void push(int value) {
// 如果push的数值大于入口元素的数值,那么就将队列后端的数值弹出
// 这样就保持了队列里的数值是单调从大到小的了
while (!que.empty() && value > que.back()) {
que.pop_back();
}
que.push_back(value);
}
int front() {
// front为当前队列里的最大值
return que.front();
}
};
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
MyQueue que;
vector<int> result;
// 先将前 k 的元素放进队列
for (int i = 0; i < k; i++) {
que.push(nums[i]);
}
// result 记录前 k 的元素的最大值
result.push_back(que.front());
for (int i = k; i < nums.size(); i++) {
que.pop(nums[i - k]); // 滑动窗口移除最前面元素
que.push(nums[i]); // 滑动窗口前加入最后面的元素
result.push_back(que.front()); // 记录对应的最大值
}
return result;
}
};
七、前 K 个高频元素
347. 前 K 个高频元素 - 力扣(LeetCode)https://leetcode.cn/problems/top-k-frequent-elements/description/
给你一个整数数组
nums
和一个整数k
,请你返回其中出现频率前k
高的元素。你可以按 任意顺序 返回答案。
bash
示例 1:
输入: nums = [1,1,1,2,2,3], k = 2
输出: [1,2]
示例 2:
输入: nums = [1], k = 1
输出: [1]
思路:
这道题目主要涉及到如下三块内容:
- 要统计元素出现频率
- 对频率排序
- 找出前K个高频元素
首先统计元素出现的频率,这一类的问题可以使用map来进行统计。
然后是对频率进行排序,这里我们可以使用一种 容器适配器就是优先级队列。
缺省情况下priority_queue利用max-heap(大顶堆)完成对元素的排序,这个大顶堆是以vector为表现形式的complete binary tree(完全二叉树)。
这种场景下,我们只需要维护 k 个 有序的序列 就可以了,所以 使用优先级队列是最优的。
要用小顶堆,因为要统计最大前k个元素,只有小顶堆每次将最小的元素弹出,最后小顶堆里积累的才是前k个最大元素。
cpp
class Solution {
public:
struct sortmap {
bool operator() (pair<int, int> x, pair<int, int> y) {
return x.second > y.second;
}
};
vector<int> topKFrequent(vector<int>& nums, int k) {
// 记录频数
unordered_map<int, int> map;
for (int i = 0; i < nums.size(); i++) {
map[nums[i]]++;
}
// 排序,小顶堆维护前 k 个 map
priority_queue<pair<int, int>, vector<pair<int, int>>, sortmap> record;
for (unordered_map<int, int>::iterator it = map.begin(); it != map.end(); it++) {
record.push(*it);
if (record.size() > k) {
record.pop();
}
}
vector<int> result(k);
for (int i = k - 1; i >= 0; i--) {
result[i] = record.top().first;
record.pop();
}
return result;
}
};