题目和链接
232.用栈实现队列
题意:
请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作(
push
、pop
、peek
、empty
):实现
MyQueue
类:
void push(int x)
将元素 x 推到队列的末尾int pop()
从队列的开头移除并返回元素int peek()
返回队列开头的元素boolean empty()
如果队列为空,返回true
;否则,返回false
做题状态:
第一题就不会,看来进步空间大大滴o(╥﹏╥)o
题解:
class MyQueue {
public:
stack<int>instack,outstack;
void in2out(){
while(!instack.empty()){
outstack.push(instack.top());
instack.pop();
}
}
MyQueue() {
}
void push(int x) {
instack.push(x);
}
int pop() {
if(outstack.empty()){
in2out();
}
int x=outstack.top();
outstack.pop();
return x;
}
int peek() {
if(outstack.empty()){
in2out();
}
int x=outstack.top();
return outstack.top();
}
bool empty() {
return instack.empty()&&outstack.empty();
}
};
总结:
我只能说,题解总是让我大为震撼,大吃一惊,怀疑自我,恍然大悟……
225. 用队列实现栈
题意:
请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作(push、top、pop 和 empty)。
实现 MyStack 类:
void push(int x) 将元素 x 压入栈顶。
int pop() 移除并返回栈顶元素。
int top() 返回栈顶元素。
boolean empty() 如果栈是空的,返回 true ;否则,返回 false 。
做题状态:
不能说不会,只能说完全不会
题解:
(官方)
入栈操作时,首先将元素入队到 queue2,然后将 queue 1的全部元素依次出队并入队到 queue 2,此时 queue2的前端的元素即为新入栈的元素,再将 queue 1和 queue 2互换,则 queue1的元素即为栈内的元素,queue1的前端和后端分别对应栈顶和栈底。
由于每次入栈操作都确保 queue1 的前端元素为栈顶元素,因此出栈操作和获得栈顶元素操作都可以简单实现。出栈操作只需要移除 queue 1 的前端元素并返回即可,获得栈顶元素操作只需要获得 queue 1的前端元素并返回即可(不移除元素)。
由于 queue1用于存储栈内的元素,判断栈是否为空时,只需要判断 queue 1是否为空即可。
class MyStack {
public:
queue<int> queue1;
queue<int> queue2;
/** Initialize your data structure here. */
MyStack() {
}
/** Push element x onto stack. */
void push(int x) {
queue2.push(x);
while (!queue1.empty()) {
queue2.push(queue1.front());
queue1.pop();
}
swap(queue1, queue2);
}
/** Removes the element on top of the stack and returns that element. */
int pop() {
int r = queue1.front();
queue1.pop();
return r;
}
/** Get the top element. */
int top() {
int r = queue1.front();
return r;
}
/** Returns whether the stack is empty. */
bool empty() {
return queue1.empty();
}
};
总结:
………………嗯。。。
20. 有效的括号
题意:
给定一个只包括 '(',')','{','}','[',']' 的字符串 s ,判断字符串是否有效。
有效字符串需满足:
1.左括号必须用相同类型的右括号闭合。
2.左括号必须以正确的顺序闭合。
3.每个右括号都有一个对应的相同类型的左括号。
示例 1:
输入:s = "()"
输出:true
做题状态:
以前做过,能写出来,看到了一篇很好的题解,用了unordered_map,还挺有意思的,放在这记录一下
题解:
class Solution {
public:
bool isValid(string s) {
unordered_map<char,int> m{{'(',1},{'[',2},{'{',3},
{')',4},{']',5},{'}',6}};
stack<char> st;
bool istrue=true;
for(char c:s){
int flag=m[c];
if(flag>=1&&flag<=3) st.push(c);
else if(!st.empty()&&m[st.top()]==flag-3) st.pop();
else {istrue=false;break;}
}
if(!st.empty()) istrue=false;
return istrue;
}
};
总结:
积极联系以前学过的知识,数据结构啥的,思考怎么优化代码
1047. 删除字符串中的所有相邻重复项
题意:
输入:"abbaca"
输出:"ca"
做题状态:
知道用栈,但是真的不会写
题解:
一个一个比对,栈空或者与栈顶不相等:入栈;
栈不空且相等:出栈
class Solution {
public:
string removeDuplicates(string s) {
string stk;
for (char ch : s) {
if (!stk.empty() && stk.back() == ch) {
stk.pop_back();
} else {
stk.push_back(ch);
}
}
return stk;
}
};
总结:
本若即真嘟不知道string有empty,back,push_back和pop_back Σ(⊙▽⊙"a
150.逆波兰表达式求值
题意:
输入:tokens = ["2","1","+","3","*"] 输出:9 解释:该算式转化为常见的中缀算术表达式为:((2 + 1) * 3) = 9示例 2:
输入:tokens = ["4","13","5","/","+"] 输出:6 解释:该算式转化为常见的中缀算术表达式为:(4 + (13 / 5)) = 6
做题状态:
做到会做的题真的很开心,
有几处可以优化的:见题解2
题解:1
class Solution {
public:
int evalRPN(vector<string>& tokens) {
stack<int>st;
for(string ch:tokens){
if(ch=="+"){
int x=st.top();
st.pop();
int y=st.top();
st.pop();
st.push(x+y);
}
else if(ch=="-"){
int x=st.top();
st.pop();
int y=st.top();
st.pop();
st.push(y-x);
}
else if(ch=="*"){
int x=st.top();
st.pop();
int y=st.top();
st.pop();
st.push(x*y);
}
else if(ch=="/"){
int x=st.top();
st.pop();
int y=st.top();
st.pop();
st.push(y/x);
}
else st.push(atoi(ch.c_str()));
}//for
return st.top();
}
};
2.优化点的
作者:Hypocrisy
(我觉得比我写的好)
class Solution {
public:
int evalRPN(vector<string>& tokens) {
stack<int> numberStack;
for (auto s : tokens) {
char t = s.back();
if (t == '+' || t == '-' || t == '*' || t == '/') {
int b = numberStack.top();
numberStack.pop();
int a = numberStack.top();
numberStack.pop();
switch (t) {
case '+':
numberStack.push(a + b);
break;
case '-':
numberStack.push(a - b);
break;
case '*':
numberStack.push(a * b);
break;
case '/':
numberStack.push(a / b);
break;
default:
break;
}
} else {
int number = 0;
int len = s.length();
bool positive = s[0] == '-' ? false : true;
int i = 0;
if (!positive)
i++;
for (; i < len; i++) {
number *= 10;
number += s[i] - '0';
}
if (!positive)
number *= -1;
numberStack.push(number);
}
}
return numberStack.top();
}
};
总结:
1.atoi+c_str()用于字符
2.stoi,字符串
3.switch替代题解1的
4.unordered_map<string,function<int(int,int)>> map={
{"+",[](int a,int b){return a+b;}},
{"-",[](int a,int b){return a-b;}},
{"*",[](int a,int b){return a*b;}},
{"/",[](int a,int b){return a/b;}}
} ;
5.易错:前后顺序a/b还是b/a
239.滑动窗口最大值
题意:
给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。
返回 滑动窗口中的最大值 。
示例 1:
输入:nums = [1,3,-1,-3,5,3,6,7], k = 3
输出:[3,3,5,5,6,7]
做题状态:
不会,嘻嘻
题解:1
优先队列(官方题解)
class Solution {
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
int n = nums.size();
priority_queue<pair<int, int>> q;
for (int i = 0; i < k; ++i) {
q.emplace(nums[i], i);
}
vector<int> ans = {q.top().first};
for (int i = k; i < n; ++i) {
q.emplace(nums[i], i);
while (q.top().second <= i - k) {
q.pop();
}
ans.push_back(q.top().first);
}
return ans;
}
};
题解:2
class Solution {
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
int n = nums.size();
deque<int> q;
for (int i = 0; i < k; ++i) {
while (!q.empty() && nums[i] >= nums[q.back()]) {
q.pop_back();
}
q.push_back(i);
}
vector<int> ans = {nums[q.front()]};
for (int i = k; i < n; ++i) {
while (!q.empty() && nums[i] >= nums[q.back()]) {
q.pop_back();
}
q.push_back(i);
while (q.front() <= i - k) {
q.pop_front();
}
ans.push_back(nums[q.front()]);
}
return ans;
}
};
347.前k个高频元素
题意:
给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。
示例 1:
输入: nums = [1,1,1,2,2,3], k = 2
输出: [1,2]
做题状态:
想用桶排序做,会写,但是时间好像不太行就没这么做,然后就开始看题解了,学会了大顶堆,小顶堆
题解:
如果堆的元素个数小于 k,就可以直接插入堆中。
如果堆的元素个数等于 k,则检查堆顶与当前出现次数的大小。如果堆顶更大,说明至少有 k 个数字的出现次数比当前值大,故舍弃当前值;否则,就弹出堆顶,并将当前值插入堆中。
class Solution {
public:
static bool cmp(pair<int, int>& m, pair<int, int>& n) {
return m.second > n.second;
}
vector<int> topKFrequent(vector<int>& nums, int k) {
unordered_map<int, int> occurrences;
for (auto& v : nums) {
occurrences[v]++;
}
// pair 的第一个元素代表数组的值,第二个元素代表了该值出现的次数
priority_queue<pair<int, int>, vector<pair<int, int>>, decltype(&cmp)> q(cmp);
for (auto& [num, count] : occurrences) {
if (q.size() == k) {
if (q.top().second < count) {
q.pop();
q.emplace(num, count);
}
} else {
q.emplace(num, count);
}
}
vector<int> ret;
while (!q.empty()) {
ret.emplace_back(q.top().first);
q.pop();
}
return ret;
}
};
总结:
1、为什么这里要用小的:这样可以直接与下一个元素进行比较
2.注意积累这里的cmp,与结构体的类似
3.这个好神奇,第一次见:
priority_queue<pair<int, int>, vector<pair<int, int>>, decltype(&cmp)> q(cmp);
总结回顾:
1.堆 处理海量数据的 topK,分位数 非常合适,优先队列应用在元素优先级排序,比如本题的频率排序非常合适。与基于比较的排序算法 时间复杂度 O(nlogn) 相比,使用 堆,优先队列复杂度可以下降到 O(nlogk),在总体数据规模 n 较大,而维护规模 k 较小时,时间复杂度优化明显。(来自CCode的题解)