算法日志6:站队是职场的必修课
栈和队列
本文为栈和队列相关题目
栈和队列是以底层容器完成其所有的工作,对外提供统一的接口,底层容器是可插拔的(也就是说我们可以控制使用哪种容器来实现栈的功能)。
所以STL中栈和队列往往不被归类为容器,而被归类为container adapter(容器适配器)。
我们常用的SGI STL,如果没有指定底层实现的话,默认是以deque为缺省情况下栈的底层结构。
deque是一个双向队列,只要封住一段,只开通另一端就可以实现栈的逻辑了。
SGI STL中 队列底层实现缺省情况下一样使用deque实现的。
我们也可以指定vector为栈的底层实现,初始化语句如下:
std::stack<int, std::vector<int> > third; // 使用vector为底层容器的栈
刚刚讲过栈的特性,对应的队列的情况是一样的。
队列中先进先出的数据结构,同样不允许有遍历行为,不提供迭代器, SGI STL中队列一样是以deque为缺省情况下的底部结构。
也可以指定list 为起底层实现,初始化queue的语句如下:
std::queue<int, std::list<int>> third; // 定义以list为底层容器的队列
所以STL 队列也不被归类为容器,而被归类为container adapter( 容器适配器)。
用栈实现队列
class MyQueue {
public:
stack<int> in_stack;
stack<int> out_stack;
MyQueue() {
}
void push(int x) {
int tmp = 0;
while(!out_stack.empty()){
tmp = out_stack.top();
out_stack.pop();
in_stack.push(tmp);
}
in_stack.push(x);
}
int pop() {
int tmp = 0;
while(!in_stack.empty()){
tmp = in_stack.top();
in_stack.pop();
out_stack.push(tmp);
}
tmp = out_stack.top();
out_stack.pop();
return tmp;
}
int peek() {
int tmp = 0;
while(!in_stack.empty()){
tmp = in_stack.top();
in_stack.pop();
out_stack.push(tmp);
}
tmp = out_stack.top();
return tmp;
}
bool empty() {
if(in_stack.empty() && out_stack.empty()) return 1;
else return 0;
}
};
/**
* 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();
*/
队列实现栈
class MyStack {
public:
queue<int> q;
queue<int> bq;
MyStack() {
}
void push(int x) {
while(!bq.empty()){
q.push(bq.front());
bq.pop();
}
q.push(x);
}
int pop() {
while(q.size()>1){
bq.push(q.front());
q.pop();
}
int tmp = q.front();
q.pop();
while(!bq.empty()){
q.push(bq.front());
bq.pop();
}
return tmp;
}
int top() {
while(q.size()>1){
bq.push(q.front());
q.pop();
}
int tmp = q.front();
bq.push(q.front());
q.pop();
while(!bq.empty()){
q.push(bq.front());
bq.pop();
}
return tmp;
}
bool empty() {
if(q.empty()&&bq.empty()) return 1;
else return 0;
}
};
/**
* 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();
*/
有效的括号
class Solution {
public:
bool isValid(string s) {
stack<char> st1;
if (s.size()%2) return false;
for(char ss:s){
// cout<<ss<<endl;
if(ss == '(') st1.push(')');
else if(ss == '[') st1.push(']');
else if(ss == '{') st1.push('}');
else if(!st1.empty() && st1.top() == ss){
// cout<<ss<<endl;
st1.pop();
}
else {
//cout<<"this false";
return false;
}
}
if(st1.empty()) return true;
else return false;
}
};
删除字符串中的所有相邻重复项
class Solution {
public:
void myreverse(string &s ,int l, int r){
while(r>l){
swap(s[l],s[r]);
l++;
r--;
}
}
string removeDuplicates(string s) {
stack<char> my_stack;
for(char ss:s){
if(my_stack.empty()) {
my_stack.push(ss);
continue;
}
if(ss == my_stack.top()){
my_stack.pop();
}
else{
my_stack.push(ss);
}
}
string res = "";
while(!my_stack.empty()){
res += my_stack.top();
my_stack.pop();
}
myreverse(res,0,res.size()-1);
return res;
}
};
逆波兰式
class Solution {
public:
int evalRPN(vector<string>& tokens) {
stack<int> my_stack;
int tmp1, tmp2, res;
for(string token:tokens){
// 不要检查数字,因为比较复杂,有多位数,负数这些情况难以判断
// if(token[0]>='0'&&token[0]<='9'){
// cout<<stoi(token);
// my_stack.push(stoi(token));
// }
// else{
// tmp2 = my_stack.top();
// my_stack.pop();
// tmp1 =my_stack.top();
// my_stack.pop();
// cout<<tmp1<<tmp2<<endl;
// res = 0;
// res = token[0] == '+'? tmp1+tmp2:res;
// res = token[0] == '-'? tmp1-tmp2:res;
// res = token[0] == '/'? tmp1/tmp2:res;
// res = token[0] == '*'? tmp1*tmp2:res;
// my_stack.push(res);
// }
if(token == "+" || token == "-" || token == "*" || token == "/"){
tmp2 = my_stack.top();
my_stack.pop();
tmp1 = my_stack.top();
my_stack.pop();
res = 0;
res = token == "+"? tmp1+tmp2:res;
res = token == "-"? tmp1-tmp2:res;
res = token == "/"? tmp1/tmp2:res;
res = token == "*"? tmp1*tmp2:res;
my_stack.push(res);
//cout<<"res token"<<token<<endl;
}
else{
my_stack.push(stoi(token));
//cout<<"normal token"<<token<<endl;
}
}
return my_stack.top();
}
};
滑动窗口最大值
我真傻,真的。我单知道堆可以方便地获取最大值,我不知道怎么从堆中删除元素,原来不用急着删除
-- Warning
我悟了,队尾只要有更年轻(i越靠后)同时还能力更强(数值越大)的,留着其他比它更早入职同时能力却更差的没有什么意义,统统开了;队首的虽然能力最强,但是年龄最大,一旦发现它超过年龄范围(不在滑动窗口的范围内),不用看能力就可以直接开了
-- 连夜把微信名改了
我悟了, 队尾比不过同龄人的删掉,队头超出时代区间的删掉,历史就是这么不断更迭的
--TinTin酱
class Solution {
public:
class myqueue{
public:
deque<int> q;
void pop(int v){
if(!q.empty() && v == q.front()){
q.pop_front();
}
}
void push(int v){
while(!q.empty() && v > q.back()){
q.pop_back();
}
q.push_back(v);
}
int front(){
return q.front();
}
};
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
myqueue q;
vector<int> res;
for(int i = 0; i<k;i++){
q.push(nums[i]);
}
res.push_back(q.front());
for(int i = k; i<nums.size();i++){
q.pop(nums[i-k]);
q.push(nums[i]);
res.push_back(q.front());
}
return res;
}
};
前 K 个高频元素
第一种,哈希表+快排
用unorderd_map存出现频率数组 ,然后转成vector<vector<int>>,然后定义自己排序方法mysort ,调用sort()进行快排,然后把排好序的数组中的前k个取出来返回即可
class Solution {
public:
static bool mysort(const vector<int>& a, const vector<int>& b) {
return a[1] > b[1]; // 按子向量的第一个元素降序排序
}
vector<int> topKFrequent(vector<int>& nums, int k) {
unordered_map<int,int> nums_map;
for(int num:nums){
nums_map[num]++;
}
vector<vector<int>> map_vec;
for(auto tmp:nums_map){
map_vec.push_back(vector<int>{tmp.first, tmp.second});
}
// for(int i = 0 ; i<map_vec.size();i++){
// cout<<map_vec[i][0]<<" "<<map_vec[i][1]<<endl;
// }
sort(map_vec.begin(), map_vec.end(), mysort);
// for(int i = 0 ; i<map_vec.size();i++){
// cout<<map_vec[i][0]<<" "<<map_vec[i][1]<<endl;
// }
vector<int> res;
for(int i = 0 ; i<k;i++){
res.push_back(map_vec[i][0]);
}
return res;
}
};
第1.5种,还是哈希加快排
优化了下,不用vector<vector<int>>去接unordered_map了,改成vector<pair<int,int>>了,代码行数少了,更清晰一点
class Solution {
public:
static bool mysort(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> nums_map;
for(int num:nums){
nums_map[num]++;
}
vector<pair<int, int>> map_vec;
// vector<vector<int>> map_vec;
for(pair<int,int> tmp:nums_map){
map_vec.push_back(tmp);
}
// for(int i = 0 ; i<map_vec.size();i++){
// cout<<map_vec[i][0]<<" "<<map_vec[i][1]<<endl;
// }
sort(map_vec.begin(), map_vec.end(), mysort);
// for(int i = 0 ; i<map_vec.size();i++){
// cout<<map_vec[i][0]<<" "<<map_vec[i][1]<<endl;
// }
vector<int> res;
for(int i = 0 ; i<k;i++){
res.push_back(map_vec[i].first);
}
return res;
}
};
第二种,优先队列(小顶堆)
注意
跟sort中指定排序函数不一样,
优先队列priority_queue中指定的排序函数不能是普通函数,必须通过仿函数(Functor)或函数指针来实现。
这是因为 std::priority_queue 的第三个模板参数是一个类型,而不是一个具体的函数实例。
class Solution {
public:
struct MyComparator {
bool operator()(const pair<int, int>& lhs, const pair<int, int>& rhs) const {
return lhs.second > rhs.second; // 最小堆:按 second 的值增序排列
}
};
vector<int> topKFrequent(vector<int>& nums, int k) {
unordered_map<int, int> nums_map;
for(int i = 0; i< nums.size(); i++){
nums_map[nums[i]]++;
}
priority_queue<pair<int,int>, vector<pair<int,int>>, MyComparator> q;
for(auto it : nums_map){
q.push(it);
if(q.size()>k){
q.pop();
}
}
vector<int> result(k);
for (int i = k - 1; i >= 0; i--) {
result[i] = q.top().first;
q.pop();
}
return result;
}
};

浙公网安备 33010602011771号