stack,queue,deque,priority_queue与仿函数

🧸🧸🧸各位大佬大家好,我是猪皮兄弟🧸🧸🧸
在这里插入图片描述

一、stack、queue的使用

leetcode-最小栈
要求时间复杂度O(1)
所以用一个正常栈和一个辅助栈来完成
st: 4 3 5 6 1
minst:4 3 1
即可,因为要取最小元素的话,必须删除掉5 6才能够拿到4 3,所以5 6没必要存入最小栈。

class MinStack {
public:
    void push(int val) {
        st.push(val);
        if(minst.empty()||val<=minst.top())
        {
            minst.push(val);
        }
    }
    
    void pop() {
        if(st.top()==minst.top())
        {
            minst.pop();
        }
        st.pop();
    }
    
    int top() {
        return st.top();
    }
    
    int getMin() {  
        return minst.top();
    }
private:
    stack<int> st;
    stack<int> minst;
};

nowcoder-栈的压入、弹出序列
在这里插入图片描述
这道题模拟的是入栈和出栈的过程,用一个栈来实现模拟

class Solution {
public:
    bool IsPopOrder(vector<int> pushV,vector<int> popV) {
        stack<int> st;
        int popi=0;
        for(auto& i :pushV)
        {
            st.push(i);
            while(popi<popV.size()&&!st.empty()&&st.top()==popV[popi])
            {
                st.pop();
                popi++;
            }
        }
        return st.empty();
    }
};

二、模拟实现

一、容器适配器

适配器是一种设计模式(设计模式是一套被反复使用的,被多数人知晓的、经过分类编目的,代码设计经验的总结),该种模式是将一个类的接口转换成客户端希望的另一个接口。

二、stack、queue模拟

栈和队列是没有迭代器的,因为需要保持它的特性,所以用不了迭代器
stack和queue是容器适配器,
关键是==复用==

stack模拟

namespace zhupi
{
	template<class T,class Container = deque<T>>//双端队列,下面会说
	class Stack
	{
	public:
		void push(const T& x)
		{
			_con.push_back(x);	
		}
		void pop()
		{
			_con.pop_back()
		}
		T&top() 
		{
			return _con.back();
		}
		const T&top() const
		{
			return _con.back();
		}
		bool empty() const
		{
			return _con.empty();
		}
		size_t size() const
		{
			return _con.size();
		}
	private:
		Container _con;		
	};
}

queue模拟

Queue不能够使用vector进行适配,因为vector不提供pop_front接口

namespace zhupi
{
	template<class T,class Container=deque<T>>
	class Queue
	{
	void push(const T& x)
	{
		_con.push_back(x);		
	}
	void pop()
	{
		_con.pop_front();
	}
	T&front()
	{
		return _con.front();
	}
	const T&front() const
	{
		return _con.front();
	}
	T&back()
	{
		return _con.back();
	}
	const T&back() const
	{
		return _con.back();
	}
	bool empty() const
	{
		return _con.empty();
	}
	size_t size() const
	{
		return _con.size();	
	}
	private:
		Container _con;
	};
}

三、deque

deque是一个双端开口的队列
在这里插入图片描述
在这里插入图片描述
deque由一个个小数组buffer构成,buffer数组有一定的大小,拥有一个中控(中央控制指针数组)来存每个数组,其次,头插的位置由特定的数据结构来完成

1. operator[]

(i-第一个buffer)/8就可以知道在第几个
(i-第一个buffer)%8就可以知道在那个buffer的第几个

2.设计的缺陷

1.operator[]的计算相比于vector[],大量使用的时候性能太低
(release几乎性能降低一般,debug差距 更大)

在这里插入图片描述

2.中间的插入删除效率很低,挪数据
3.底层角度,迭代器会很复杂
迭代器设计:四个指针first,last,cur,node
first,last指向开始和结束,cur指向现在的buffer的首地址,node指针取访问中控指针数组获得buffer位置

3.结论

1.真正适合的场景是头尾的插入删除(相比vector和list而言),所以,deque很适合做stack和 queue的默认适配容器。
2.中间插入删除就多用list
3.随机访问尽量用vector

四、优先级队列priority_queue

在这里插入图片描述
优先级队列其实是一个堆,所以,默认适配容器是vector
priority_queue也是不支持迭代器的,因为需要保证优先级
默认是大的优先级高(大堆)

priority_queue的仿函数比较的是优先级,也就是权重,所以less出来的是大堆,greater出来的是小堆

1.priority_queue的使用

#include <queue>
void test_priority_queue1()
{
	priority_queue<int> pq;
	pq.push(1);
	pq.push(2);
	pq.push(3);
	//因为不支持迭代器,而且是堆,所以
	while(!pq.empty())
	{
		cout<<pq.top()<<" ";
		pq.pop();
	}
}
void test_priority_queue1()
{
	int a = {3,2,7,6,0,4,1,9,8,5};
	//迭代区间构造,数组的迭代器是原生指针
	priority_queue<int> heap(a,a+sizeof(a)/sizeof(int));
//  priority_queue<int,vector<int>,less<int>>
	//遍历
}

leetcode-数组中的第k个最大元素
在这里插入图片描述
要求时间复杂度O(n),找topK当然是用堆,向下调整建堆时间复杂度O(n)

class Solution{
	public:
		int fineKthLargest(vector<int>&nums,int k){
			priority_queue<int> pq(num.begin(),nums.end());
			while(--k)
			{
				pq.pop();
			}
			return pq.top();
		}
};

2.priority_queue的模拟实现

主要是adjust_up和adjust_down函数
向下调整是从最后一个结点的父节点开始
1.即(child-1)/2
2.向下调整建堆因为跳过了 很多结点,几乎是1/2,所以能达到O(N)的时间复杂度

#include<vector>
namespace zhupi
{
	template<class T,class Container=vector<T>,class Compare=std::less<int>>
	class priority_queue
	{
		template<class InputIterator>
		//迭代区间初始化,向下建堆
		priority(InputIterator first,InputIterator last)
		{	
			while(first!=last)
			{
				_con.push_back(*first);
			}
			//向下建堆从最后一个叶子结点的父节点开始
			//left_child = parent*2+1;
			//parent = (child-1)/2
			for(int i=(_con.size()-1-1)/2;i>=0;i--)
			{
				adjust_down(i);
			}
		}
		//暂时是大堆
		class 
		void adjust_up(int child)
		{
			Compare com;
			int parent = (child-1)/2;
			while(parent)
			{
				if(com(_con[parent],_con[child]))//仿函数com
				{
					std::swap(_con[child],_con[parent]);//仿函数 com
					child =parent;
					parent=(child-1)/2;
				}
				else
				{
					break;
				}
			}
		}
		void push(const T& x)
		{
			_con.push_back(x);
			adjust_up(_con.size()-1);
		}
		
		//暂时是大堆
		void adjust_down(size_t parent)
		{
			Compare com;
			size_t child = parent*2+1;
			if(child+1)<_con.size()&&com(_con[child],_con[child+1]))//仿函数com
				child++;
			if((_con[child]<_con[parent]))//仿函数 com
			{
				std::swap(_con[child],_con[parent]);
				parent=child;
				child=parent*2+1;
			}
			else 
				break;
		}
		
		void pop()
		{
			std::swap(_con[0],_con[_con.size()-1]);
			_con.pop_back();
			adjust_down(0);
		}

		const T& top() const
		{
			return _con[0];
		}
		bool empty() const
		{
			return _con.empty();
		}
		size_t size() const
		{
			return _con.size();
		}
	private:
		Container _con;
	};
}

五、仿函数

STL六大组件之一,仿函数其实是一个类,类里面重载运算符() operator(),使得该类的对象能像函数一样的去调用。

template<class T>
class Less
{
public:
	bool operator()(const T& l,const T& r) const
	{
		return l<r;
	{
};
template<class T>
class Greater
{
public:
	bool operator()(const T& l,const T& r) const
	{
		return l>r;
	{
};

int main()
{
	Less<int> lsFunc;
	cout<<lsFunc(1,2);//像函数一样的去访问

	Greater<int> gtFunc;
	cout<<gtFunc(1,2);
	return 0;
}

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

猪皮兄弟

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

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

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

打赏作者

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

抵扣说明:

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

余额充值