stack使用+模拟实现

在这里插入图片描述

stack

学习stack的使用首先就是文档
在这里插入图片描述

首先可以看到stack这个容器的模板参数比以前的vector和list等容器改变了一个参数,以前的第二个参数是alloc在这里插入图片描述

也就是空间配置器(内存池),而stack的这个container是一个新的名词,叫做容器适配器。说到容器适配器,实际就是将其他容器作为底层容器,比如后面给的缺省参数deque是双端队列,当然我们也可以换成其他容器比如list或者vector都是可以的。
为什么叫做容器适配器,这里可以类比与电源适配器,电源适配器就是将我们220v的交流电转换成我们需要的电。容器适配器也是一样的。通过对其他容器的封装实现了栈的后进先出的功能。这个部分在模拟代码哪里可以清楚看到。

说到了容器适配器,现在我们需要整体了解一下STL的六大组件。
1.容器(是常见的数据结构:string,vector,list…)
2.算法(常用算法sort,reverse,find…)
3.迭代器(不关心底层容器的实现结构,使用简单统一的方式来访问和修改容器)
4.适配器(就是在某些底层容器加以封装形成其他容器的特性)
5.仿函数(使用类重载操作符,使得这个类的对象可以像函数一样使用)
6空间配置器(内存池)

stack的基本函数接口

在这里插入图片描述

这里的emplace功能上是和push一样的,但是引入了C++11里面的右值引用。
这里主要看一下构造函数
在这里插入图片描述

这里的container_type就是第二个模板参数容器适配器,所以这里的构造函数,如果我们第二个参数用的是缺省参数deque,可以直接使用deque来拷贝构造一个stack对象。下面我是用的vector,道理是一样的。

void test2()
{
	vector<int> vv(10, 6);
	stack<int,vector<int>> stk2(vv);
    //拷贝构造也是可以使用的。
	stack<int, vector<int>> stk(stk2);

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

虽然构造函数里面并没有标注拷贝构造等等以前的那些构造函数,但是只要是你使用的那个容器适配器可以使用的构造函数,stack也是可以一起使用的。

下面就来看一下模拟实现stack的代码

//stack.h
#pragma once

#include<vector>

namespace xzj
{
	template<class T,class Container = std::vector<T>>
	class stack
	{
	public:
		
		void push(const T& x)
		{
			_a.push_back(x);
		}
		void pop()
		{
			_a.pop_back();
		}
		bool empty()
		{
			return _a.empty();
		}
		T& top()
		{
			return _a.back();
		}
		const T& top() const
		{
			return _a.back();
		}
		size_t size()
		{
			return _a.size();
		}

	private:
		Container _a;
	};
}

这是最简单的容器适配器的使用,这里使用的是vector,也可以换成list等等,当天也可在类模板实例化的时候指定底层的容器适配器是什么容器。

关于栈的习题

下面来看几道栈的习题的应用。

232. 用栈实现队列

解题思路:
此题可以用两个栈实现,一个栈进行入队操作,另一个栈进行出队操作出队操作: 当出队的栈(skpop)不为空的时候,直接进行出栈操作,如果为空,需要把入队的栈(skpush)元素全部导入到出队的栈,然后再进行出栈操作
计算有效元素个数,就是这两个栈的有效元素个数之和

class MyQueue {
public:
    stack<int> skpush;
    stack<int> skpop;
    MyQueue() {

    }
    
    void push(int x) {
        skpush.push(x);
    }
    
    int pop() {
        if(skpop.empty())
        {
            while(!skpush.empty())
            {
                skpop.push(skpush.top());
                skpush.pop();
            }
        }
        int tmp  = skpop.top();
        skpop.pop();
        return tmp;
    }
    
    int peek() {
        if(skpop.empty())
        {
            while(!skpush.empty())
            {
                skpop.push(skpush.top());
                skpush.pop();
            }
        }
        return skpop.top(); 
    }
    
    bool empty() {
        return skpop.empty() && skpush.empty();
    }
};
150. 逆波兰表达式求值

思路:逆波兰表达式是将操作符按照优先级排列了,所以遍历数组,遇到数字就入栈,遇到操作符就取栈顶的两个数字进行运算将结果再入栈(要注意先出栈的是右操作符)最后栈内剩下的那个数字就是最后的结果。

class Solution {
    public:
    
    void GetNum(stack<int>& stk,int& left,int& right)
    {
        right = stk.top();
        stk.pop();
        left = stk.top();
        stk.pop();
    }
    
    int evalRPN(vector<string>& tokens) {
        stack<int> stk;
        int left = 0,right = 0;
        for(const auto& ch : tokens)
        {
            switch(ch.back())
            {
                case '+':
                    GetNum(stk,left,right);
                    stk.push(left + right);
                    break;
                case '-':
                    GetNum(stk,left,right);
                    stk.push(left - right);
                    break;
                case '*':
                    GetNum(stk,left,right);
                    stk.push(left * right);
                    break;
                case '/':
                    GetNum(stk,left,right);
                    stk.push(left / right);
                    break;
                default:
                    stk.push(stoi(ch));
                    break;
            }
        }
        return stk.top();
    }
};

逆波兰表达式又叫做后缀表达式,就是操作符是在操作数的后面。我们平时使用的都是中缀表达式,也即是操作符的两端是操作数。

上面题目的思路是将后缀表达式转换成中缀表达式计算。那么我们如何将中缀表达式转换成后缀表达式呢?
中缀表达式转后缀表达式
1.首先遇到数字就放进新的vector容器内,遇到运算符就要开始判断,1.如果存放运算符的栈为空,那么将运算符入栈。2.如果该运算符比栈顶的运算符优先级更高,那么将该运算符入栈。3.如果该运算符的优先级低于栈顶的运算符,那么出栈顶的运算符,该运算符继续和栈顶的运算符比较,直到该运算符优先级大于栈顶元素,或者栈为空,就将该运算符入栈。
代码如下(没有包含括号的情况,如果遇到括号,需要设计一个标志位,标记括号内的运算符优先级大于括号外的优先级):

void Infix_to_Suffix(vector<string>& fix)
{
	vector<string> ans;
	stack<string> stk;
	for (auto str : fix)
	{
		switch (str.back())
		{
			case '+':
			{
				if (stk.empty())
					stk.push(str);
				else
				{
					string& tmp = stk.top();
					while (stk.empty() && (tmp.back() == '*' || tmp.back() == '/'))
					{
						ans.push_back(stk.top());
						stk.pop();
						tmp = stk.top();
					}
					stk.push(str);
				}
				break;
			}
			case '-':
			{
				if (stk.empty())
					stk.push(str);
				else
				{
					string& tmp = stk.top();
					while (stk.empty() && (tmp.back() == '*' || tmp.back() == '/'))
					{
						ans.push_back(stk.top());
						stk.pop();
						tmp = stk.top();
					}
					stk.push(str);
				}
				break;
			}
			case '*':
			{
				stk.push(str);
				break;
			}
			case '/':
			{
				stk.push(str);
				break;
			}
			default:
				ans.push_back(str);
				break;
		}
	}

	while (!stk.empty())
	{
		ans.push_back(stk.top());
		stk.pop();
	}
	for (auto& ss : ans)
	{
		cout << ss << "  ";
	}
}

void test3()
{
	vector<string> fix{ "4","+","13","/","5" };
	Infix_to_Suffix(fix);
}
215. 数组中的第K个最大元素

思路:使用优先级队列,优先级队列实际就是将vector封装然后加上堆的算法,实际就是一个堆,用nums构建一个优先级队列,默认是大堆,但是这里需要小堆,用前k个数字构建一个小堆,然后遍历剩下的数字,如果遇到比堆顶元素大的那么就删除堆顶元素然后插入新元素,最后这个堆里面的k个数就是整个nums里面最大的k个,第k大的数字就是这个堆里面最小的数字。所以最后返回堆顶的数字。

class Solution {
public:
    int findKthLargest(vector<int>& nums, int k) {
        priority_queue pq(nums.begin(),nums.begin()+k,greater());

        for(size_t i = k;i<nums.size();i++)
        {
            if(pq.top() < nums[i])
            {
                pq.pop();
                pq.push(nums[i]);
            }
        }
        return pq.top();
    }
};
JZ31 栈的压入、弹出序列

思路:遍历pushV同时遍历popV将pushV中的数字插入到栈内,如果栈顶数字和popV的数字相同则出栈,popV走到下一个数字,继续比较这需要写一个循环,直到栈为空,或者两者不相等就停止,因为可能遇到连续出栈的情况所以写成循环。
最后若是popV走到了结尾,那么就是一个合法的弹出序列否则就是不合法的。

class Solution {
public:
    bool IsPopOrder(vector<int> pushV,vector<int> popV) {
        stack<int> stk;
        int i = 0;
        for(auto e : pushV)
        {
            stk.push(e);
            while(!stk.empty() && stk.top() == popV[i])
            {
                stk.pop();
                i++;
            }
        }
        return i == popV.size();
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

KissKernel

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值