【C++】priority_queue模拟实现+仿函数+反向迭代器

自我名言只有努力,才能追逐梦想,只有努力,才不会欺骗自己。在这里插入图片描述
喜欢的点赞,收藏,关注一下把!在这里插入图片描述

1.priority_quyue

priority_queue文档介绍

  1. 优先队列也和栈,队列一样是一种容器适配器,容器适配器即将特定容器类封装作为其底层容器类。
  2. 优先级底层是堆,而堆又是一个完全二叉树,因此可以选用vector和dequeue来作为特定的容器,dequeue头插头删效率高,但是随机访问效率低一些。因此选择vector作为特定的容器更为合适。
  3. 优先级队列默认是一个大堆
  4. 优先级队列规定优先级高的先出。

在这里插入图片描述

priority_queue默认是一个大堆,这是由第三个参数less仿函数)所控制的,虽然less明面意思是小于,但是默认是一个大堆,默认大的优先级较高,大的先出。greater(仿函数),明面意思是大于,但是默认是一个小堆,默认小的优先级比较高,小的先出。

这里先不介绍仿函数,直接用就可以了,下面再详细讲。

1.1priority_queue的使用

函数说明接口说明
priority_queue()/priority_queue(first,last)构造一个空的优先级队列
empty()检测优先级队列是否为空,是返回true,否则返回false
push(x)在优先级队列中插入元素x
pop()删除优先级队列中最大(最小)元素,即堆顶元素
top()返回优先级队列中最大(最小元素),即堆顶元素

在这里插入图片描述

注意缺省值不能隔着传。

在这里插入图片描述

写一道题来熟悉接口。
215. 数组中的第K个最大元素

在这里插入图片描述
在数据结构的学过堆,并且这里问题提供两个思路

思路一:
堆排序
时间复杂度:O(N*log2N)

思路二:
拿N个元素建立一个大堆,然后popK-1次,这样top拿的就是第K个最大的元素。
时间复杂度:建堆O(N),调整堆O(Klog2N) ----> O(N+Klog2N)

思路三:
拿K个元素建立一个小堆,再将N-K个元素与堆顶元素比较,大于堆顶元素就入堆,最后堆顶元素就是就是第K个大的元素。
时间复杂度:O(K+(N-K)*log2K) ----->O(N)

这里给出第三个思路的代码。

class Solution {
public:
    int findKthLargest(vector<int>& nums, int k) {
        //前k个建小堆
        priority_queue<int,vector<int>,greater<int>> pq(nums.begin(),nums.begin()+k);

        //N-k与堆顶元素比较,大于堆顶就入堆,再次调整成小堆
        for(size_t i=k;i<nums.size();++i)
        {
            if(nums[i]>pq.top())
            {
                pq.pop();
                pq.push(nums[i]);
            }
        }
        return pq.top();
    }
};

1.2priority_queue模拟实现

关于这的一些向上调整算法和向下调整算法再堆的实现,画图和代码分析建堆,堆排序,时间复杂度以及TOP-K问题里都有详细介绍,这里不在细说了。

1.2.1无参构造+一段区间构造

//大堆
template<class T, class Container = vector<T>>
class priority_queue
{
public:
	priority_queue()
	{}

	template <class InputIterator>
	priority_queue(InputIterator first, InputIterator last)
		:_con(first, last)
	{
		//向下调整法建堆
		for (int i =( _con.size() - 1 - 1) / 2; i >= 0; --i)
		{		
			adjustdown(i);
		}
	}

	void adjustdown(int parent)
	{
		int minchild = parent * 2 + 1;
		while (minchild < _con.size())
		{
			if (minchild + 1 < _con.size() && _con[minchild + 1] > _con[minchild])
			{
				minchild++;
			}

			if (_con[minchild] > _con[parent])
			{
				swap(_con[minchild], _con[parent]);
				parent = minchild;
				minchild = parent * 2 + 1;
			}
			else
			{
				break;
			}
		}
	}

private:
	Container _con;
};

1.2.2push

//向上调整法
void adjustup(int child)
	{
		int parent = (child - 1) / 2;
		while (child > 0)
		{
			if (_con[child] > _con[parent])
			{
				swap(_con[child], _con[parent]);
				child = parent;
				parent = (child - 1) / 2;
			}
			else
			{
				break;
			}
		}
	}
	
void push(const T& val)
{
	_con.push_back(val);
}

1.2.3pop

void pop()
{
	assert(!empty());
	swap(_con[0], _con[_con.size() - 1]);
	_con.pop_back();
	adjustdown(0);
	
}

1.2.4empty

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

1.2.5size

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

1.2.6top

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

这里还是和库里有些区别,我们写第三个参数,仿函数,下面我们来介绍它,并且在完善我们的代码。

2.仿函数

2.1什么是仿函数

仿函数其实是一个类的对象,也可以说是函数对象,因为这个类的对象可以像函数一样使用。

仿函数类别有很多种,今天主要介绍less和greater(比大小仿函数 )。

还记得C语言给我们提供了一个qsort函数,这个函数用到了函数指针。
这个函数指针指向一个比大小的函数。

C++既然包容C了,为什么不直接用这个函数指针呢?
C++主要觉得函数指针太麻烦了。因此引入了仿函数。

拿冒泡排序为例。

下面是普通的冒泡排序,如果排升序>,降序<,但是这都不能由使用者来决定,只能由写这个排序的人才能决定。这样给用户的体验感不好,自己不能控制。如果自己能够控制排序的方式就好了,所以这里可以写个比大小的仿函数。

void Bubblesort(int* a,int n)
{
	for (int i = 0; i < n - 1; ++i)
	{
		int flag = 1;
		for (int j = 0; j < n - i - 1; ++j)
		{
			if (a[j] > a[j+1])
			{
				swap(a[j], a[j + 1]);
				flag = 0;
			}
		}
		if (flag == 1)
		{
			break;
		}
	}
}

仿函数

template<class T>
class less
{
public:
	bool operator()(const T& x, const T& y)
	{
		return x < y;
	}
};

template<class T>
class greater
{
public:
	bool operator()(const T& x, const T& y)
	{
		return x > y;
	}
};

这样看起来为什么一个类的对象可以叫做函数对象了,这个对象可以像函数一样调用。

less<int> lessfunc;
greater<int> greaterfunc
lessfunc(0, 1);
greaterfunc(0,1)
//这里是运算符重载  lessfunc.operator()(0,1)

现在将冒泡排序写成一个函数模板,把仿函数加上。这样就可以自己控制升序还是降序了。

template<class T,class Compare>
void Bubblesort(T* a,int n,Compare _com)
{
	for (int i = 0; i < n - 1; ++i)
	{
		int flag = 1;
		for (int j = 0; j < n - i - 1; ++j)
		{
			//升序
			//if (a[j] > a[j+1])
			if (_com(a[j],a[j+1]))
			{
				swap(a[j], a[j + 1]);
				flag = 0;
			}
		}
		if (flag == 1)
		{
			break;
		}
	}
}
int main()
{	
	int a[] = { 4,1,2,5,7,3,9,0,6,8 };
	Bubblesort(a, sizeof(a) / sizeof(a[0]), bit::greater<int>());

	return 0;
}

2.2增加仿函数的priority_queue模拟实现完整代码

#include<iostream>
#include<queue>
#include<vector>
#include<assert.h>
#include<algorithm>
using namespace std;


namespace bit
{

	template<class T>
	class less
	{
	public:
		bool operator()(const T& x, const T& y)
		{
			return x < y;
		}
	};

	template<class T>
	class greater
	{
	public:
		bool operator()(const T& x, const T& y)
		{
			return x > y;
		}
	};

	//大堆
	//template<class T, class Container = vector<T>>
	template<class T, class Container = vector<T>,class Compare=less<T>>
	class priority_queue
	{
	public:
		priority_queue()
		{}

		template <class InputIterator>
		priority_queue(InputIterator first, InputIterator last)
			:_con(first, last)
		{
			//向下调整法建堆
			for (int i =( _con.size() - 1 - 1) / 2; i >= 0; --i)
			{		
				adjustdown(i);
			}
		}

		void adjustdown(int parent)
		{
			Compare _com;
			int minchild = parent * 2 + 1;
			while (minchild < _con.size())
			{
				
				//if (minchild + 1 < _con.size() && _con[minchild + 1] > _con[minchild])
				if (minchild + 1 < _con.size() && _com(_con[minchild],_con[minchild+1]))
				{
					minchild++;
				}

				//if (_con[minchild] > _con[parent])
				if (_com(_con[parent],_con[minchild]))
				{
					swap(_con[minchild], _con[parent]);
					parent = minchild;
					minchild = parent * 2 + 1;
				}
				else
				{
					break;
				}
			}
		}

		void adjustup(int child)
		{
			Compare _com;
			int parent = (child - 1) / 2;
			while (child > 0)
			{
				
				//if (_con[child] > _con[parent])
				if (_com(_con[parent],_con[child]))
				{
					swap(_con[child], _con[parent]);
					child = parent;
					parent = (child - 1) / 2;
				}
				else
				{
					break;
				}
			}
		}

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

		void pop() 
		{
			assert(!empty());
			swap(_con[0], _con[_con.size() - 1]);
			_con.pop_back();

			adjustdown(0);
		}

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

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

		const T& top()
		{
			assert(!empty());
			return _con[0];
		}
	private:
		Container _con;
	};
}

2.3自定义仿函数

看下面一组图片对比结果

在这里插入图片描述
在这里插入图片描述
发现当类型是指针的时候,结果就出现问题。
原因是因为指针位置是不确定的,不知道谁大谁小。但是我们的仿函数比的就是指针的大小。
因此这个仿函数已经不合适,所以可以自定义一个仿函数来适配。

struct lessfunc
{
	bool operator()(Date* const& x, Date* const& y)
	{
		return *x < *y;
	}
};

struct greaterfunc
{
	bool operator()(Date* const& x, Date* const& y)
	{
		return *x > *y;
	}
};

写了这了匹配的仿函数,结果就没问题了。
在这里插入图片描述

3.反向迭代器

在前面vector和list模拟实现,我们只实现了正向迭代器和const正向迭代器,没有去实现反向迭代器。

这是因为反向迭代器也是一个容器适配器
给我一个正向迭代器我可以适配出反向迭代器。
给我一个const正向迭代器我可以适配出const反向迭代器。

也就是我们复用现有的来帮我们实现所需要的。

在这里插入图片描述

3.1list反向迭代器

复用iterator创造reverse_iterator,因此把iterator传过去。
在这里插入图片描述

template<class iterator,class Ref ,class Ptr>
class Reverseiterator
{
public:

	//构造
	Reverseiterator(iterator It)
		:_it(It)
	{}
	//这里也可以这样给参,这样就少了一次拷贝构造,但是有一个问题
	//Reverseiterator(iterator& It)
	//reverse_iterator(end())这句代码,
	//end(),返回的是iterator的临时对象,临时对象具有常性,如果参数给的是&的话,前面必须加上const否则报错。
	Reverseiterator(const iterator& It)
		:_it(It)
	{}

private:
	iterator _it;
};

在这里插入图片描述

//++rit
	self& operator++()
	{
		--it;
		return *this;
	}

	//rit++
	self operator++(int)
	{
		iterator tmp(*this);
		--it;
		return tmp;
	}

	//--rit
	self& opeartor--()
	{
		++it;
		return *this;
	}

	//rit--
	self operator--(int)
	{
		iterator tmp(*this);
		++it;
		return tmp;
	}

在这里插入图片描述

Ref operator*()
	{
		iterator tmp = _it;
		//先--,在解引用
		return *(--tmp);
	}
	
//针对自定义类型
Ptr operator->()
{
	return &(operator*());
}

完整代码

template<class iterator,class Ref ,class Ptr>
class Reverseiterator
{
	typedef Reverseiterator<iterator, Ref, Ptr> self;
public:
	Reverseiterator(iterator It)
		:_it(It)
	{}

	Ref operator*()
	{
		iterator tmp = _it;
		return *(--tmp);
	}

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

	//++rit
	self& operator++()
	{
		--_it;
		return *this;
	}

	//rit++
	self operator++(int)
	{
		iterator tmp(*this);
		--_it;
		return tmp;
	}

	//--rit
	self& operator--()
	{
		++_it;
		return *this;
	}

	//rit--
	self operator--(int)
	{
		iterator tmp(*this);
		++_it;
		return tmp;
	}

	bool operator!=(const self& s)
	{
		return _it != s._it;
	}

private:
	iterator _it;
};


reverse_iterator rbegin()
	{
		return reverse_iterator(end());
	}

reverse_iterator rend()
{
	return reverse_iterator(begin());
}

3.2vector反向迭代器

我们的思路和list的反向迭代器一样,把上面代码复用给vector,就实现了vector的反向迭代器。
在这里插入图片描述

在这里插入图片描述

3.3list与vector的const反向迭代器

const_reverse_iterator rbegin() const
{
	return const_reverse_iterator(end());
}

const_reverse_iterator rend() const
{
	return const_reverse_iterator(begin());
}

如果你的对象是const,就用的是cosnt反向迭代器。
在这里插入图片描述

  • 20
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 30
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值