剑指offer刷题记录--队列&栈

1. JZ9 用两个栈实现队列(易)

在这里插入图片描述

1. 官方和自己(推荐)

push操作就直接往stack1中push, pop操作需要分类一下:如果stack2为空,那么需要将stack1中的数据转移到stack2中,然后在对stack2进行pop,如果stack2不为空,直接pop就ok。
复杂度:时间复杂度push和pop均为为 O ( 1 ) O(1) O(1),空间复杂度为 O ( n ) O(n) O(n)

//push操作就直接往stack1中push, 
// pop操作需要分类一下:如果stack2为空,那么需要将stack1中的数据转移到stack2中,然后在对stack2进行pop,
// 如果stack2不为空,直接pop就ok。

class Solution
{
public:
    void push(int node) {
        stack1.push(node);    
    }
    
    int pop() {
        int res=-1;
        //1倒入2
        if(stack2.empty()){
            while(!stack1.empty()){
                stack2.push(stack1.top());
                stack1.pop();
            }    
        }
        res = stack2.top();
        stack2.pop();
        return res;
    }
private:
    stack<int> stack1; //插入栈,入队
    stack<int> stack2; //删除栈,出队
};

2. 本题小结

  1. 双栈一个管理队头,一个管理队尾,当队头为空但又需要pop的时候,将队尾中的所有元素倒过来再pop()
    在这里插入图片描述
    在这里插入图片描述

2. JZ73 翻转单词序列(易)

在这里插入图片描述

1. 自己的栈(推荐)

class Solution {
public:
    string ReverseSentence(string str) {
        string res="";
        if(str.length()==0) return res;
        stack<string> sta;
        string tmp="";
        for(int i=0,j=0; i<str.length(); ++i){
            if(!isspace(str[i]) )  //非空格
            {
                tmp+=str[i];
                if(i==str.length()-1 && tmp!="")
                    sta.push(tmp);                    
            }
            else if(tmp!=""){
                sta.push(tmp);
                tmp="";
            }
        }
        while(!sta.empty()){
            res+=sta.top();
            sta.pop();
            if(!sta.empty())  res+=" ";
        }
        return res;
    }
};

2. 官方

有个方法是两次反转,但是这可以用栈找到一个单词之后单独反转reverse(str.begin(), str.end())

3. 本题小结

  1. 利用栈实现翻转,好理解。

3. JZ30 包含min函数的栈(中)

在这里插入图片描述

1. 官方双栈法(推荐)

思考:之前自己想过维护一个最小值变量,当调用min()函数时就返回这个值,但是如果最小值被pop()了,怎么找到下一个最小值呢?继续想,可不可以管理多个最小值,一个值弹出之后,对应的把此时的最小值也弹出,于是引入辅助栈,每次push数据到数据栈ataSta中同时也将当时对应的min值push到MinSta中,pop时同时pop数据栈和辅助栈,就完成了带min的栈的数据结构。
在这里插入图片描述

class Solution {
public:
    
    stack<int> DataSta;
    stack<int> MinSta;
    
    //数据栈入栈,最小栈比较入栈最小值
    void push(int value) {
        DataSta.push(value);
        if(MinSta.empty()) MinSta.push(value);
        else MinSta.push(MinSta.top()<=value ? MinSta.top():value);
    }
    void pop() {
        if(DataSta.empty())
            throw std::exception(std::logic_error{"Stack is empty."});
        else{
            DataSta.pop();
            MinSta.pop();
        }
        
    }
    int top() {
        return DataSta.top();       
    }
    int min() {
        return MinSta.top();
    }
};

2. 本题小结

  1. 使用最小栈MinSta来管理每个push操作之后当时的最小值。

4. JZ31 栈的压入、弹出序列(中)

1. 官方辅助栈

在这里插入图片描述
时间和空间复杂度均为 O ( n ) O(n) O(n)

class Solution {
public:    
    bool IsPopOrder(vector<int> pushV,vector<int> popV) {
        int Size = pushV.size();
        stack<int> sta;
        for(int i=0,j=0;i<Size; ++i){
            //保证栈不空,到底的情况会在下面处理
            if(sta.empty()) sta.push(pushV[j++]);
            //按照入栈顺序压入栈,直到找到该出栈的元素,如果全部入了还是没找到该出的,就证明不是
            while(sta.top()!=popV[i]){
                if(j==Size) return false;
                sta.push(pushV[j++]);
            }
            sta.pop();
        }
        return true;
    }
};

2. 原地操作(推荐)

在这里插入图片描述
时间复杂度为 O ( n ) O(n) O(n),空间复杂度 O ( 1 ) O(1) O(1),原地操作,无序额外空间。

class Solution {
public:    
    bool IsPopOrder(vector<int> pushV,vector<int> popV) {
        int n=0;    //栈空间大小
        int j=0;    //出栈序列下标
        //对于每个待入栈的元素
        for(int num:pushV)
        {
            pushV[n] = num ;
            //当栈不为空且栈顶等于当前出栈序列
            while(n>=0 && pushV[n]==popV[j]){
                //出栈,减小栈空间
                ++j;
                --n;
            }
            ++n;
        }
        //如果最后栈为空则证明全部出去了,是对的
        return n==0;
    }
};

3. 本题小结

  1. 使用辅助栈来压入待出的元素,直到找到带出的元素,如果找不到,说明false
  2. 原地操作(需要征求面试官是否能原地操作),pushV的前半部分没用了,可以当作栈来使用,最后如果栈都空了,说明true,否则false

5. JZ59 滑动窗口的最大值(难)

在这里插入图片描述

1. 官方双向队列(推荐)

思路就是只把有可能成为窗口最大值的数据推到双向队列中,双向队列的队首deq.front()存放的时是该窗口中最大元素的下标。

  1. 首先处理第一个窗口,不会越界,如果后面进来的数据比前面的大,那么前面的数据不可能是最大值,如果新数据 a a a比队尾数据小,但是是有可能成为最大值的(当后续所有值都比 a a a)小时,a就是最大值。
  2. 在后面的窗口中,可能存在越界的情况,判断队首是否越界,若越界,则pop_front(),若未越界,则判断新值 b b b是否大于队尾的下标对应的数,循环pop_back()队尾下标对应的值小于 b b b的下标,然后把 b b b的下标 p u s h b a c k ( ) push_back() pushback(),每个窗口处理结束之后往Res中push_back(nums(deq.front()))
class Solution {
public:
    //仿照带有min函数的栈,引入辅助栈MaxSta,每次移动之后将最大值insert到vector<int>中
    vector<int> maxInWindows(const vector<int>& nums, int size) {
        vector<int> Res;  //保存最大值
        deque<int> deq;
        //没必要使用队列来实现窗口移动
        //处理第一个窗口
        for(int i=0; i<size; ++i){
            while(!deq.empty() && nums[deq.back()]<=nums[i] ){
                deq.pop_back();
            } 
            deq.push_back(i);
        }
        Res.push_back(nums[deq.front()]);
        //处理
        for(int i=size; i<nums.size(); ++i){
            //超过窗口范围的下标直接pop_front,由于每次只移动一个,所以只需要判断最开始的越没越界即可,不用循环
            if(!deq.empty() && deq.front()<i-size+1)
                deq.pop_front();
            //尾部小于新值的都pop_back()
            while(!deq.empty() && nums[deq.back()]<=nums[i])
                deq.pop_back();
            deq.push_back(i); //推入新下标
            //推入的是最大的或者比队尾小的
            Res.push_back(nums[deq.front()]);
        }
        return Res;
    }
};

2. 双栈实现队列+含有max()函数的栈

这两种题之前都做过,可以结合起来实现 O ( n ) O(n) O(n)的算法,但是实现比较复杂。

3. 官方暴力解法

class Solution {
public:
    vector<int> maxInWindows(const vector<int>& num, unsigned int size) {
        vector<int> res;
        //窗口大于数组长度的时候,返回空
        if(size <= num.size() && size != 0) 
            //数组后面要空出窗口大小,避免越界
            for(int i = 0; i <= num.size() - size; i++){
                //寻找每个窗口最大值
                int max = 0; 
                for(int j = i; j < i + size; j++){
                    if(num[j] > max)
                        max = num[j];
                }
                res.push_back(max);
            }
        return res;
    }
};

3. 本题小结

  1. 挺烧脑的,看看书 P 289 − 291 P_{289-291} P289291
    在这里插入图片描述
    在这里插入图片描述

  2. 当然可以使用暴力解法,但是没意义了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值