stack和queue的使用及OJ题

一、stack的介绍和使用

1.1 stack的介绍

1️⃣stack是一种容器适配器,专门用在具有后进先出操作的上下文环境中,其删除只能从容器的一端进行元素的插入与提取操作
2️⃣stack是作为容器适配器被实现的,容器适配器即是对特定类封装作为其底层的容器,并提供一组特定的成员函数来访问其元素,将特定类作为其底层的,元素特定容器的尾部(即栈顶)被压入和弹出。
3️⃣stack的底层容器可以是任何标准的容器类模板或者一些其他特定的容器类,这些容器类应该支持以下操作:

  • empty:判空操作
  • back:获取尾部元素操作
  • push:尾部插入元素操作
  • pop:尾部删除元素操作

4️⃣标准容器vector、deque、list均符合这些需求,默认情况下,如果没有为stack指定特定的底层容器,默认情况下使用deque。

示意图:
在这里插入图片描述

1.2 stack的使用

void test_stack()
{
	stack<int> s;
	s.push(1);
	s.push(2);
	s.push(3);
	s.push(4);

	while (!s.empty())
	{
		cout << s.top() << " ";
		s.pop();
	}
	cout << endl;
	cout << s.size() << endl;
	cout << s.empty() << endl;
}

1.3 stack的OJ题

最小栈

题目描述
设计一个支持 push ,pop ,top 操作,并能在常数时间内检索到最小元素的栈。
实现 MinStack 类:

  • MinStack() 初始化堆栈对象。
  • void push(int val) 将元素val推入堆栈。
  • void pop() 删除堆栈顶部的元素。
  • int top() 获取堆栈顶部的元素。
  • int getMin() 获取堆栈中的最小元素

思路一
我们可能首先想到的是,实现一个栈,再设置一个min记录最小值,每次push一次,比较更新min的值
在这里插入图片描述
问题:我们pop数据的时候,找不到之前的最小值了,需要重新遍历一遍才能找到最小值,时间复杂度变为了O(N)

思路二:两个栈,一个普通栈(st),一个记录最小值的栈(minST),这个最小栈每个位置与普通栈一 一对应,它的栈顶元素就是对应普通栈中所剩余元素的最小值,这样的思路的实质是以空间换时间。
在这里插入图片描述
优化:minST中可能会存入大量重复的值,这是没有必要的。
push:
我们只需要判断一下,push到普通栈的值是否大于minST栈顶的值,判断是否要push入minST
pop:普通栈一直pop,直到pop到了与minST栈顶相同时,minST再出栈
这样能相对节省一点空间
在这里插入图片描述
bug:push的值等于minST也要插入minST栈
在这里插入图片描述

完整解答:

class MinStack {
    stack<int> _st;
    stack<int> _minST;
public:
    // 这里不需要写,用默认构造函数即可,对于自定义类型会去调用它自己的默认构造函数初始化
    MinStack() {}
    
    void push(int val) {
        _st.push(val);

        if(_minST.empty()||val<=_minST.top())
        _minST.push(val);
    }
    
    void pop() {
        // 如果_minST栈顶元素等于出栈元素,那么_minST栈顶元素就要出栈
        if(_minST.top()==_st.top())
            _minST.pop();

        _st.pop();
    }
    
    int top() {return _st.top();}
    
    int getMin() { return _minST.top();}
};

栈的压入、弹出序列

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

  1. 0<=pushV.length == popV.length <=1000
  2. -1000<=pushV[i]<=1000
  3. pushV 的所有数字均不相同

如果这道题,我们通过寻找它的规律来解题,排列组合的情况太多了,规律也是不怎么好找,所以我们通过题目给的测试用例:用入栈顺序去模拟它的出栈顺序,若可以模拟出来,那么这种算法就是OK的!

思路:
1、入栈位置的值与出栈位置的值比较,不相等,那么这个值先入栈,后面再出
2、如果入栈位置的值等于出栈位置的值,这个值还是先入栈,持续出数据,直到栈为空或者栈顶的元素跟出栈序列的值不匹配

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

class Solution
{
public:
    bool IsPopOrder(vector<int> pushV, vector<int> popV)
    {
        stack<int> st;
        size_t pushi = 0, popi = 0;
        while (pushi < pushV.size())
        {
            // 一直入栈
            st.push(pushV[pushi]);
            pushi++;

            // 可能会遇到连续出栈的情况
            // 若栈不为空且栈顶元素与出栈序列依次匹配
            while (!st.empty() && st.top() == popV[popi])
            {
                st.pop();
                popi++;
            }
        }
        return popi == popV.size();// 最后popi走完了,说明全部匹配
    }
};

逆波兰表达式的求解

题目描述:
根据 逆波兰表示法,求表达式的值。
有效的算符包括 +、-、*、/ 。每个运算对象可以是整数,也可以是另一个逆波兰表达式。
注意:两个整数之间的除法只保留整数部分。
可以保证给定的逆波兰表达式总是有效的。换句话说,表达式总会得出有效数值且不存在除数为 0 的情况。

注意:逆波兰表达式是一种后缀表达式,所谓后缀就是指算符写在后面。

平常使用的算式则是一种中缀表达式,如 ( 1 + 2 ) * ( 3 + 4 )
该算式的逆波兰表达式写法为 ( ( 1 2 + ) ( 3 4 + ) * )

逆波兰表达式主要有以下两个优点:

1.去掉括号后表达式无歧义,上式即便写成 1 2 + 3 4 + * 也可以依据次序计算出正确结果。
2.适合用栈操作运算:遇到数字则入栈;遇到算符则取出栈顶两个数字进行计算,并将结果压入栈中

完整解答:

class Solution {
public:
    int evalRPN(vector<string>& tokens)
    {
        stack<int> st;
        for (const auto str : tokens)
        {
            // str为操作符
            if (str == "+" || str == "-" || str == "*" || str == "/")
            {
                // 先取出右操作数
                int right = st.top();
                st.pop();
                int left = st.top();
                st.pop();


                //switch语句中表达式类型只能是整型或者字符型
                switch (str[0])
                {
                case '+':
                    st.push(left + right);
                    break;

                case '-':
                    st.push(left - right);
                    break;

                case '*':
                    st.push(left * right);
                    break;

                case '/':
                    st.push(left / right);//题目说了不存在除数为0的情况
                    break;

                default:
                    assert(false);
                }

            }
            // str为操作数
            else
            {
                st.push(stoi(str));
            }
        }
        return st.top();
    }
};

二、queue的介绍和使用

2.1 queue的介绍

1️⃣队列是一种容器适配器,专门用于在FIFO上下文(先进先出)中操作,其中从容器一端插入元素,另一端提取元素。
2️⃣队列作为容器适配器实现,容器适配器即将特定容器类封装作为其底层容器类,queue提供一组特定的成员函数来访问其元素。元素从队尾入队列,从队头出队列。
3️⃣底层容器可以是标准容器类模板之一,也可以是其他专门设计的容器类。该底层容器应至少支持以下操作:

  • empty:检测队列是否为空
  • size:返回队列中有效元素的个数
  • front:返回队头元素的引用
  • back:返回队尾元素的引用
  • push:在队列尾部入队列
  • pop:在队列头部出队列

4️⃣标准容器类deque和list满足了这些要求。默认情况下,如果没有为queue实例化指定容器类,则使用标准容器deque。
示意图:
在这里插入图片描述

2.2 queue的使用

void test_queue()
{
	queue<int> q;
	q.push(1);
	q.push(2);
	q.push(3);
	q.push(4);

	while (!q.empty())
	{
		cout << q.front() << " ";
		q.pop();
	}
	cout << endl;
	cout << q.size() << endl;
	cout << q.empty() << endl;
}

2.3 queue的OJ题

用队列实现栈

class MyStack {
public:
    queue<int> q1 ,q2;
    MyStack() {}
    
    void push(int x) {
        q2.push(x);
        while(!q1.empty())
        {
            int front=q1.front();
            q2.push(front);
            q1.pop();
        }
        swap(q1,q2);
    }
    
    int pop() {
        int front=q1.front();
        q1.pop();
        return front;
    }
    
    int top() {
        int front=q1.front();
        return front;
    }
    
    bool empty() 
    { return q1.empty();}
};
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值