总结:
用栈实现队列
题目链接:232.用栈实现队列
思路
设置两个栈,分别为stack1,stack2。
入队:当采用push操作时,将元素推到stack1中去。
出队:当采用pop或者peek操作时,如果是第一次请求或此时stack2为空,将stack1中所有元素全部pop出来,推入stack2中。
此时stack2的栈顶就是所实现队列的队列头。而如果此时stack2中还有元素,则只需要pop出stack2当前栈顶元素即可。
判断是否为空,只需要判断当前两个栈stack1和stack2是否都为空即可。
实现:
class MyQueue {
private:
stack<int> stack1;
stack<int> stack2;
public:
MyQueue() {
}
void push(int x) {
stack1.push(x);
}
int pop() {
if(stack2.empty()){
while(!stack1.empty()){
int tmp = stack1.top();
stack1.pop();
stack2.push(tmp);
}
}
int value = stack2.top();
stack2.pop();
return value;
}
int peek() {
if(stack2.empty()){
while(!stack1.empty()){
int tmp = stack1.top();
stack1.pop();
stack2.push(tmp);
}
}
int value = stack2.top();
return value;
}
bool empty() {
return stack1.empty() && stack2.empty();
}
};
用队列实现栈
题目链接: 用队列实现栈
题目: 请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作(push、top、pop 和 empty)。
思路
可以仅采用一个队列来实现。
定义一个队列queue1,实现push操作,只需要将元素置入队尾即可。
实现pop操作时,可以先记录当前队列的长度length,然后不断地将元素从队首排出,再推入队列尾部,这个循环持续length - 1次。
此时队列首部的元素正是构建的栈的栈顶,将该元素从队首排出即可。
对于top操作来说,前面的循环过程于pop类似,只是当获得该元素的值后,再将该元素从队首排出,推入队列尾部。
判断实现的栈是否为空,只需要判断队列是否为空即可。
class MyStack {
private:
queue<int> queue1;
public:
MyStack() {
}
void push(int x) {
queue1.push(x);
}
int pop() {
int length = queue1.size();
for(int i = 0;i<length-1;i++){
int tmp = queue1.front();
queue1.pop();
queue1.push(tmp);
}
int value = queue1.front();
queue1.pop();
return value;
}
int top() {
int length = queue1.size();
for(int i = 0;i<length-1;i++){
int tmp = queue1.front();
queue1.pop();
queue1.push(tmp);
}
int value = queue1.front();
queue1.pop();
queue1.push(value);
return value;
}
bool empty() {
return queue1.empty();
}
};
有效的括号
题目链接:有效的括号
给定一个只包括 '(',')','{','}','[',']' 的字符串 s ,判断字符串是否有效。
有效字符串需满足:
先有左括号,再有右括号- 左括号必须用相同类型的右括号闭合。
- 左括号必须以正确的顺序闭合。
- 每个右括号都有一个对应的相同类型的左括号。 文章讲解/视频讲解:programmercarl.com/0020.%E6%9C…
思路
设置一个stack,然后循环遍历字符串S,当遍历到左括号时,如’(', ‘[’, ‘{’, 将字符串push进stack中
当遍历到右括号时,判断stack栈顶的元素是否为该右括号对应的括号,如果是对应的,则将stack的栈顶pop出,
如果不是对应的,则说明字符串无效,返回false。
遍历完字符串之后,判断stack是否为空,如果不为空,说明还是无效,返回false,否则返回为true。
class Solution {
public:
bool isValid(string s) {
stack<char> stk;
for(int i = 0;i<s.size();i++){
if(s[i] == '[' || s[i] == '(' || s[i] == '{'){
stk.push(s[i]);
}
else{
if(stk.empty()) return false;
char tmp = stk.top();
bool success = (s[i] == ']' && tmp == '[') || (s[i] == ')' && tmp == '(') || (s[i] == '}' && tmp == '{');
if(!success) return false;
stk.pop();
}
}
return stk.empty();
}
};
删除字符串中的所有相邻重复项
题目链接:1047. 删除字符串中的所有相邻重复项
给出由小写字母组成的字符串 S,重复项删除操作会选择两个相邻且相同的字母,并删除它们。
在 S 上反复执行重复项删除操作,直到无法继续删除。
在完成所有重复项删除操作后返回最终的字符串。答案保证唯一。
思路
设置一个stack,这个stack可以由一个string实现。
然后循环遍历字符串s,如果当前遍历的字符和stack的栈顶相等,则将stack的栈顶pop出,并跳过这个字符。
如果当前遍历的字符和stack的栈顶不相等或者栈为空,则将字符push进这个栈。
由于我们的栈直接由string实现的,因此可以直接返回这个stack。
代码
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 位 整数表示。
思路
构建一个stack,用来存储数字。
遍历tokens数组,当遇到数字时,将其存入stack,当遇到计算符号时,从stack中取出两个数字分别为num2, num1
将num1与num2做运算,再存入stack中。
最终stack中剩余的一个数字,就是逆波兰表达式的结果。
C++实现
class Solution {
public:
int calculate(int num1, int num2, string op){
if(op == "+"){
return num1 + num2;
}
else if(op == "-"){
return num1 - num2;
}
else if(op == "*"){
return num1 * num2;
}
else if(op == "/"){
return num1 / num2;
}
else{
return num1 + num2;
}
}
int evalRPN(vector<string>& tokens) {
stack<int> nums;
for(int i = 0;i<tokens.size();i++){
if(tokens[i] != "+" && tokens[i] != "-" && tokens[i] != "*" && tokens[i] != "/"){
nums.push(stoi(tokens[i]));
}
else{
int num2 = nums.top();
nums.pop();
int num1 = nums.top();
nums.pop();
int res = calculate(num1, num2, tokens[i]);
nums.push(res);
}
}
return nums.top();
}
};
滑动窗口最大值
题目链接:239. 滑动窗口最大值
给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。
返回 滑动窗口中的最大值 。
设置一个大小为k的队列queue。
在滑动窗口处于初始位置时,将初始的k个元素推入队列中: 如果队列为空或者当前元素小于队列的队尾元素,直接将nums[i]推入队列尾部; 如果当前元素大于队列的队尾元素,则循环判断,只要队列的队尾元素小于当前元素,就将当前队尾排出,直到循环判断结束,将nums[i]推入队列尾部。 此时队列的队首就是当前窗口的最大值。
滑动窗口开始移动时,开始对整数数组nums的后续元素进行遍历: 此时滑动窗口的范围为[i, i + k - 1],如果nums[i - 1]等于队首元素,则将队首排出,说明此时队首已经不在滑动窗口中了; 对于当前值nums[i],如果队列为空或nums[i]小于队列的队尾元素,直接将nums[i]推入队列尾部,如果此时队列大小大于k,将队首排出; 如果nums[i]大于队列的队尾元素,则开始循环判断,只要队列的队尾元素小于nums[i],就将当前队尾排出,直到循环判断结束,将nums[i]推入队列尾部。 同样的,每次遍历,当前队列的队首就是当前窗口的最大值。
上述方法在构建队列时,可以保证队列中的元素是单调非增的,因此队首就是当前窗口的最大值。同时,因为需要对队列的队尾做排出操作,用deque双向队列来作为队列的容器。 注:如果用vector作为容器,会超时。因为排出队首元素是o(n)复杂度的。
class Solution {
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
vector<int> results;
deque<int> dQ;
for(int i = 0;i<k;i++){
if(dQ.empty() || nums[i] <= dQ.back()){
dQ.push_back(nums[i]);
}
else{
while(!dQ.empty() && dQ.back() < nums[i]) dQ.pop_back();
dQ.push_back(nums[i]);
}
}
results.push_back(dQ.front());
for(int i = k;i<nums.size();i++){
if(nums[i - k] == dQ.front()) dQ.pop_front();
if(dQ.empty() || nums[i] <= dQ.back()){
dQ.push_back(nums[i]);
if(dQ.size() > k) dQ.pop_front();
}
else{
while(!dQ.empty() && dQ.back() < nums[i]) dQ.pop_back();
dQ.push_back(nums[i]);
}
results.push_back(dQ.front());
}
return results;
}
};
前 K 个高频元素
题目链接:前 K 个高频元素
给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。
文章讲解/视频讲解:programmercarl.com/0347.%E5%89…
思路
用小顶堆来实现。 定义一种node结构,属性分别为值和频率。 首先遍历数组,统计每个元素的出现频率,将代表每个元素的node存入数组frequents。 定义一个存储node类型的小顶堆,堆的判断标准是node之间的频率,频率越低越靠前。 遍历数组frequents,将元素不断地push进这个小顶堆中,如果小顶堆的大小大于k,则将小顶堆的堆顶排出。
最终小顶堆中的所有元素,构成了前K个高频元素。
代码
struct node{
int value;
int frequence;
};
class Solution {
public:
vector<int> topKFrequent(vector<int>& nums, int k) {
auto cmp = [](node a, node b){return a.frequence > b.frequence;};
priority_queue<node, vector<node>, decltype(cmp)> Q(cmp);
vector<node> frequents;
unordered_map<int, int> hashMap;
for(int i = 0;i<nums.size();i++){
hashMap[nums[i]] += 1;
}
for(auto p : hashMap){
frequents.push_back({p.first, p.second});
}
for(int i = 0;i<frequents.size();i++){
Q.push(frequents[i]);
if(Q.size() > k) Q.pop();
}
vector<int> results;
while(!Q.empty()){
results.push_back(Q.top().value);
Q.pop();
}
return results;
}
};