C++ stack and queue

1. stack模拟实现


C++STL中的栈是一种容器适配器,它是将vector/list进行封装,push/pop等接口直接调用vector/list的接口即可,不需要像C语言那样,从头开始造轮子

namespace byh
{
	template<class T, class Container = deque<T>>
	class stack
	{
	public:
		void push(const T& val)
		{
			_con.push_back(val);
		}

		void pop()
		{
			_con.pop_back();
		}

		const T& top()
		{
			return _con.back();
		}

		bool empty()
		{
			return _con.empty();
		}

		size_t size()
		{
			return _con.size();
		}

	private:
		Container _con;
	};
}

int main()
{
	byh::stack<int> st;
	st.push(1);
	st.push(2);
	st.push(3);
	st.push(4);
	while (!st.empty())
	{
		cout << st.top() << " ";
		st.pop();
	}
	cout << endl;
	return 0;
}

在模板实例化时,我们给栈传什么容器,它就调用该容器的接口,默认容器是deque

deque是一种双端队列,它结合了vectorlist 的优势

vector

  • 优势:能够随机访问
  • 劣势:头插/删,中间插/删要挪动数据,效率低;扩容有消耗

list

  • 优势:任意位置插入/删除效率高
  • 劣势:不支持随机访问

可以说,vectorlist是两个互补的容器,既然这样,能不能把这两个容器的优势结合;因为有这样的需求,就有了deque

它的底层会有一个中控指针数组,每个元素都指向一个长度为N的数组

尾插时,如果当前的所有数组都满了,则开辟新的指针,指向新的数组,插入元素

头插时,在数组中,以从后往前的顺序依次插入元素

访问第i个元素时,如果第一个数组没满,i减去第一个数组元素个数,进行下面的运算:

  • i / N -->找到该元素在第几个数组
  • i % N -->找到是在该数组的第几个元素

如果第一个数组满了,直接进行上面的运算

如果中控指针数组满了,进行扩容,虽然最总还是要扩容,但消耗上大大降低

deque

  • 优点:头插/删,尾插/删效率都不错
  • 缺点:中间插/删会比较麻烦,不如list;随机访问的效率不如vector

stackqueue的默认容器使用deque的原因就在于它的头插/删,尾插/删效率可以,并且不会使用随机访问或者中间插/删

2.queue模拟实现


namespace byh
{
	template<class T, class Container = deque<T>>
	class queue
	{
	public:
		void push(const T& val)
		{
			_con.push_back(val);
		}

		void pop()
		{
			_con.pop_front();
		}

		T& back()
		{
			return _con.back();
		}

		T& front()
		{
			return _con.front();
		}

		const T& back() const
		{
			return _con.back();
		}

		const T& front() const
		{
			return _con.front();
		}

		bool empty()
		{
			return _con.empty();
		}

		size_t size()
		{
			return _con.size();
		}

	private:
		Container _con;
	};
}

int main()
{
	byh::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;
	
	return 0;
}

3. priority_queue


queue系列中,除了先进先出的普通queue之外,还有一个优先级队列priority_queue

pop时,不再是先进先出,而是按规定的优先级取出元素

#include<iostream>
#include<queue>

int main()
{
	std::priority_queue<int> pq;
	pq.push(3);
	pq.push(2);
	pq.push(4);
	pq.push(5);
	pq.push(1);
	while (!pq.empty())
	{
		std::cout << pq.top() << " ";
		pq.pop();
	}
	std::cout << std::endl;
	return 0;
}
// 输出 5 4 3 2 1

在这里插入图片描述

仿函数实际上是一个类,只不过我们可以用函数的方式去使用它

它的内部是对()运算符的重载

    template<class T>
    struct Less
    {
        bool operator()(const T& x, const T& y)
        {
            return x < y;
        }
    };

void Test()
{
    byh::Less<int> less;
    cout << less(1, 2) << endl;// 有名对象

    cout << less.operator()(1, 2) << endl;

    cout << byh::Less<int>()(1, 2) << endl;// 匿名对象
}

我们可以在仿函数内部控制比较的逻辑,生活中大部分场景下都是用自定义类型描述一个对象,如何控制该对象的比较,进行排序,就需要我们规定好比较方式

struct Goods
{
	Goods(const char* str, double price, int evaluate)
		:_name(str)
		,_price(price)
		,_evaluate(evaluate)
	{}

	string _name;
	double _price;
	int _evaluate;
};

struct ComparePriceLess
{
	bool operator()(const Goods& x, const Goods& y)
	{
		return x._price < y._price;
	}
};

//...

int main()
{
	vector<Goods> v = { {"苹果",3.1,2},{"草莓",4.2,4},{"苹果",2.6,3} };
	sort(v.begin(), v.end(), ComparePriceLess());
	return 0;
}

3.1 模拟实现

	template<class T>
	struct Less
	{
		bool operator()(const T& x, const T& y)
		{
			return x < y;
		}
	};

	template<class T>
	struct Greater
	{
		bool operator()(const T& x, const T& y)
		{
			return x > y;
		}
	};

	template<class T, class Container = vector<T>, class Compare = Less<T>>
	class priority_queue
	{
	public:
		void AdjustUp(int child)
		{
			int parent = (child - 1) / 2;
			Compare com;

			while (child > 0)
			{
				if (com(_con[parent], _con[child]))
				{
					swap(_con[parent], _con[child]);
					child = parent;
					parent = (child - 1) / 2;
				}
				else break;
			}
		}

		void push(const T& val)
		{
			_con.push_back(val);
			AdjustUp(size() - 1);
		}

		void AdjustDown(int parent)
		{
			int child = parent * 2 + 1;
			Compare com;

			while (child < size())
			{
				if (child + 1 < size() && com(_con[child], _con[child + 1]))
					child++;
				if (com(_con[parent], _con[child]))
				{
					swap(_con[parent], _con[child]);
					parent = child;
					child = parent * 2 + 1;
				}
				else break;
			}
		}

		void pop()
		{
			swap(_con[0], _con[size() - 1]);
			_con.pop_back();
			AdjustDown(0);
		}

		T& top()
		{
			return _con[0];
		}

		const T& top() const
		{
			return _con[0];
		}

		size_t size() const
		{
			return _con.size();
		}

		bool empty() const
		{
			return _con.empty();
		}

	private:
		Container _con;
	};

4. Reverse_Iterator


反向迭代器与正向迭代器功能类似,它们都是遍历,不同的是,反向迭代器++往前走,--往后走

我们可以对正向迭代器的++--修改,但这样做,每个容器的迭代器都要进行拷贝修改,不仅代码冗余,还麻烦

于是,我们用const迭代器的思路,将Reverse_Iterator写成一个模板,我们给它传哪个容器的迭代器,编译器就帮我们形成哪个容器的反向迭代器,从而减少我们的工作量

STL中,rbegin()rend()的位置与begin()end()相反

访问时,是解引用当前位置的上一个位置

在这里插入图片描述

namespace byh
{
	template<class Iterator, class Ref, class Ptr>
	struct ReverseIterator
	{
		Iterator _it;

		ReverseIterator(Iterator it)
			:_it(it)
		{}

		Ref operator*()
		{
			Iterator temp = _it;
			return *(--temp);
		}

		Ptr operator->()
		{
			return &(operator*());
		}

		// ++it
		ReverseIterator operator++()
		{
			_it--;
			return *this;
		}

		// --it
		ReverseIterator operator--()
		{
			_it++;
			return *this;
		}

		bool operator!=(ReverseIterator rit)
		{
			return _it != rit._it;
		}
	};
}
erator it)
			:_it(it)
		{}

		Ref operator*()
		{
			Iterator temp = _it;
			return *(--temp);
		}

		Ptr operator->()
		{
			return &(operator*());
		}

		// ++it
		ReverseIterator operator++()
		{
			_it--;
			return *this;
		}

		// --it
		ReverseIterator operator--()
		{
			_it++;
			return *this;
		}

		bool operator!=(ReverseIterator rit)
		{
			return _it != rit._it;
		}
	};
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值