算法日志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;
    }
};

posted @ 2025-03-26 17:45  玉米面手雷王  阅读(9)  评论(0)    收藏  举报