栈和队列

复习的时候,如果想看原题目,直接搜索每题前面的序号,优先在leetcode上搜(leetcode包含了剑指)。
☺☺☺

(1)剑指9.1:用两个栈实现队列(LeetCode225)

不管是用两个栈实现队列,还是用两个队列实现栈,我们都仅在pop时处理,这样方便理解,也方便记忆。
同样的题,再LeetCode和牛客上竟然不一样!!emmm
使用栈实现队列的下列操作:

push(x) – 将一个元素放入队列的尾部。
pop() – 从队列首部移除元素。
peek() – 返回队列首部的元素。
empty() – 返回队列是否为空。
示例:
MyQueue queue = new MyQueue();
queue.push(1);
queue.push(2);
queue.peek(); // 返回 1
queue.pop(); // 返回 1
queue.empty(); // 返回 false
思路:
在这里插入图片描述
push:直接弹入stack1就可以了。
pop:在弹出时,看stack2是否为空,如果为空,就把Stack1中的元素倒到stack2,这时将stack2栈顶元素弹出就行了。
如果不为空,说明之前从stack1过来的元素还没全部弹出,这是接着弹出就行了。

peek:需要先把左边栈stack1的元素倒到右边的栈,再访问右边的栈stack2的栈顶,最后记得访问后再倒回去
在这里插入图片描述

class MyQueue {
private:
    stack<int> stack1;
    stack<int> stack2;
    
public:
    /** Initialize your data structure here. */
    MyQueue() {
        
    }
    
    /** Push element x to the back of queue. */
    void push(int x) {
        stack1.push(x);
    }
    
    /** Removes the element from in front of queue and returns that element. */
    int pop() {
        int a;
        if(stack2.empty()){
            while(!stack1.empty()){//把栈1元素移到栈2
                a=stack1.top();
                stack2.push(a);
                stack1.pop();
            }
        }
        a=stack2.top();
        stack2.pop();

        while(!stack2.empty()){//栈2移栈1
           stack1.push(stack2.top());
           stack2.pop();
        }

        return a;
    }
    
    /** Get the front element. */
    int peek() {
        while(!stack1.empty()){
        stack2.push(stack1.top());
        stack1.pop();
        }
        int cur;
        cur = stack2.top();
        while(!stack2.empty()){//再移回去
            stack1.push(stack2.top());
            stack2.pop();
        }
        return cur;
    }
    
    /** Returns whether the queue is empty. */
    bool empty() {  //因为最后都移到了栈1,所以只需要判断栈1是否为空
        return stack1.empty();
    }
};
/**
 * 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();
 */

☺☺☺

(2)剑指9.2:用两个队列实现栈(LC232)

使用队列实现栈的下列操作:

push(x) – 元素 x 入栈
pop() – 移除栈顶元素
top() – 获取栈顶元素
empty() – 返回栈是否为空
在这里插入图片描述

**两个队列的实现 仅在pop时处理
1、pop时,最后一个元素之前的元素全部出队,放到临时队列
2、pop最后一个元素
3、再将临时队列中的元素全部移回来
**
在这里插入图片描述

class MyStack {
private:
    queue<int> queue1;
    queue<int> queue2;
public:
    /** Initialize your data structure here. */
    MyStack() {
        
    }
    
    /** Push element x onto stack. */
    void push(int x) {
        queue1.push(x);//插入队列
    }
    
    /** Removes the element on top of the stack and returns that element. */
    int pop() {
        while(queue1.size()>1){//将前size-1个元素移至中转队列
            queue2.push(queue1.front());
            queue1.pop();
        }
        int a=queue1.front();
        queue1.pop();
        while(!queue2.empty()){//再将中转队列中的所有元素移回队列
            queue1.push(queue2.front());
            queue2.pop();
        }
        return a;
    }
    
    /** Get the top element. */
    int top() {
        return queue1.back();
    }
    
    /** Returns whether the stack is empty. */
    bool empty() {
        return queue1.empty();
    }
};

☺☺☺

(3)剑指30:包含min函数的栈(LC155)

设计一个支持 push,pop,top 操作,并能在常数时间内检索到最小元素的栈。

push(x) – 将元素 x 推入栈中。
pop() – 删除栈顶的元素。
top() – 获取栈顶元素。
getMin() – 检索栈中的最小元素
在这里插入图片描述
在这里插入图片描述
思路:用两个栈,一个栈存数据,一个栈存各阶段最小数

class MinStack {
private:
    stack<int> s;  //本栈
    stack<int> stmp;   //辅助栈
public:
    /** initialize your data structure here. */
    MinStack() {
        
    }    
    void push(int x) {
        s.push(x);  //先把值压入本栈
        if(stmp.empty() || x < stmp.top()){  //每次保证最小的值到stmp
            stmp.push(x);
        }
        else{
            stmp.push(stmp.top());  //保证stmp中和s中元素个数相同,这样pop时就可以同时pop
        }   
    }
    
    void pop() {
        stmp.pop();
        s.pop();
    }
    
    int top() {
        return s.top();
    }
    
    int getMin() {
        return stmp.top();  //栈顶元素为当前最小数,因为它一直保存的最小值
    }
};

☺☺☺

(4)剑指31:栈的压入、弹出序列 【中等】(LC946)

输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如,序列 {1,2,3,4,5} 是某栈的压栈序列,序列 {4,5,3,2,1} 是该压栈序列对应的一个弹出序列,但 {4,3,5,1,2} 就不可能是该压栈序列的弹出序列。

示例 1:
输入:pushed = [1,2,3,4,5], popped = [4,5,3,2,1]
输出:true
解释:我们可以按以下顺序执行:
push(1), push(2), push(3), push(4), pop() -> 4,
push(5), pop() -> 5, pop() -> 3, pop() -> 2, pop() -> 1
示例 2:
输入:pushed = [1,2,3,4,5], popped = [4,3,5,1,2]
输出:false
解释:1 不能在 2 之前弹出。

在这里插入图片描述
思路:借助一个辅助栈。看图很容易理解。
一直往栈中压入元素,直到栈顶元素和出栈序列中的第一个元素相等,将栈顶元素弹出。这样循环等压栈序列遍历完成,如果辅助栈还不为空,说明弹出序列不是该栈的弹出顺序。

class Solution {
public:
    bool validateStackSequences(vector<int>& pushed, vector<int>& popped){
        if(pushed.empty() && popped.empty()) return true;  //两个空栈返回true
        
        stack<int> s;
        int j=0; //标记poped中元素的位置
        for(int i=0; i<pushed.size(); i++){//扫描入栈序列
            s.push(pushed[i]);//push元素直到栈顶元素为pop序列中的元素
            while(!s.empty()&& s.top()==popped[j]){ //相等时,辅助栈出栈,弹出序列索引加一
                s.pop();
                j++;
            }
        }
        if(s.empty()){ //如果为空,则说明入栈序列和出栈序列相匹配,为true,否则为false
            return true;
        }
        else{
            return false;
        }
    }  
};

在这里插入图片描述
☺☺☺

(5)剑指59.1:滑动窗口的最大值 (LC239)

这题看了半天,还好我没放弃。原来是hard

给定一个数组 nums 和滑动窗口的大小 k,请找出所有滑动窗口里的最大值。
示例:

输入: nums = [1,3,-1,-3,5,3,6,7], 和 k = 3
输出: [3,3,5,5,6,7] 
解释: 

  滑动窗口的位置(下标)           最大值
 0  1   2   3  4  5  6  7
------------------------------------
[1  3  -1] -3  5  3  6  7       3
 1 [3  -1  -3] 5  3  6  7       3
 1  3 [-1  -3  5] 3  6  7       5
 1  3  -1 [-3  5  3] 6  7       5
 1  3  -1  -3 [5  3  6] 7       6
 1  3  -1  -3  5 [3  6  7]      7

提示:
你可以假设 k 总是有效的,在输入数组不为空的情况下,1 ≤ k ≤ 输入数组的大小。

法1
【核心思想】
用双向队列存储数组的
下标**
【数据结构】
双向队列
【思路】
第一步: 实时更新数字最大的下标
第二步: 实时判断当前数字最大的小标是否在滑动窗口内部 ,i 始终为滑动窗口的头
第三步: 压入下标
第四步: 当滑动窗口的元素满足要求,即可压入元素
或理解:
用双向队列存储数组的下标,每一轮进行移动窗口、维护和输出的动作
每一轮使最大的数对应的下标在双向队列的最左端,如果双向队列中“左边下标对应的元素”要小于“右边下标对应的元素”,那么就把左边的元素进行清除维护,输出双向队列最左端下标对应的元素**

class Solution {
public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        vector<int>result;       //存放结果的vector
        deque<int>temp;          //记录最大数字小标的双向队列
        int length=nums.size();
        for(int i=0;i<length;i++)
        {
            while(!temp.empty()&&nums[i]>nums[temp.back()]){ //如果队列尾部的小标所应对的元素小于当前元素,就把队列尾部的下标删除
                temp.pop_back();
            }
                                                      //i+1-k表示退后三部的元素的下标
            if(!temp.empty()&& temp.front()< i-k+1){   //看队列首下标左边是否超出了3之外(是否出了窗口之外)
                temp.pop_front();
            }

            temp.push_back(i);  //把当前元素下标放入队列
            if(i-k+1>=0) //if (i >= k -1)   // i>=下标2,说明构成一个窗口大小了
               result.push_back(nums[temp.front()]) ;
        }
        return result;
    }
};

法2:最容易想到的笨办法。
一个i指示每个窗口的起点,在i循环内j遍历该窗口找出最大值。

class Solution {
public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        
        vector<int> res;  //存放结果
        if(nums.empty()) return res;  //必须写
        int len=nums.size();
        for(int i=0; i<=len-k; i++){  //定位每个窗口的第一个元素
            int max=nums[i];
            for(int j=i; j<i+k; j++){  //在每个窗口中找最大值
                if(nums[j]>max){
                    max=nums[j];
                }
            }
            res.push_back(max);
        }
        return res;
    }
};

☺☺☺

(6)剑指59.2:队列的最大值 【中等】

请定义一个队列并实现函数 max_value 得到队列里的最大值,要求函数max_value、push_back 和 pop_front 的时间复杂度都是O(1)。
若队列为空,pop_front 和 max_value 需要返回 -1

示例 1:
输入: 
["MaxQueue","push_back","push_back","max_value","pop_front","max_value"]
[[],[1],[2],[],[],[]]
输出: [null,null,null,2,1,2]
示例 2:
输入: 
["MaxQueue","pop_front","max_value"]
[[],[],[]]
输出: [null,-1,-1]
 
限制:
1 <= push_back,pop_front,max_value的总操作数 <= 10000
1 <= value <= 10^5

解题思路
使用一个数组记录数据
使用一个双端队列维护一个单调递减的队列,数组头部就是当前最大值

用两个队列来完成任务
【数据结构】队列与双向队列
【思路】
用一个队列
保存正常元素**,另一个双向队列保存单调递减的元素
入栈时,第一个队列正常入栈;第二个队列是递减队列,所以需要与之前的比较,从尾部把小于当前value的全部删除(因为用不到了)
出栈时,第一个队列正常出栈;同时与第二个队列的头部值作比较,如果相同,那么一起出栈**

class MaxQueue {
public:
    queue<int> que;  //普通队列
    deque<int> deq; //双向队列  double  queue方便记忆
    MaxQueue() {
        
    }
    int max_value() {
        if (deq.empty()) {
            return -1;
        }
        return deq.front();
    }
    
    void push_back(int value) {
        while (!deq.empty() && deq.back() < value) {//反正是要插入的,但是如果前面的值小于当前就要去掉
            deq.pop_back();  //保证最大值队列中存的是主极大与次极大
        }
        deq.push_back(value);
        que.push(value);
    }//插入的时候就不要考虑删除时候的事情
    
    int pop_front() {
        if (que.empty()) {
            return -1;
        }
        int ans = que.front();
        if (deq.front() == ans) { //如果弹出的元素即为当前最大值,注意把最大值队列队首元素也出队。这样才能知道下一个最大值
            deq.pop_front();
        }
        que.pop();
        return ans;
    }
};

比较:
min栈的实现:队列中是尾部插入,首部删除(滑动窗口是这种情况,故用队列较好),而min栈较方便,在一端插入和删除
最大值队列在push当前值之前,需将之前较小的值出队处理(从队尾开始判断),队首元素即为当前队列的最大值

知识点:deque 与 queue
deque 顺序容器。
deque中的元素可以通过位置下标来访问。支持快速的随机访问。deque各方面都与vector类似,唯一的差别是:deque支持在容器的头部和尾部快速插入和删除(所以加双向队列),而且在两端插入和删除元素都不会导致重新分配空间。

queue 队列。尾部插入,头部删除。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值