模拟实现priority_queue

模拟实现priority_queue

1. priority_queue的介绍

priority_queue文档介绍

  1. 优先队列是一种容器适配器,根据严格的弱排序标准,它的第一个元素总是它所包含的元素中最大的。
  2. 此上下文类似于堆,在堆中可以随时插入元素,并且只能检索最大堆元素(优先队列中位于顶的元素)。
  3. 优先队列被实现为容器适配器,容器适配器即将特定容器类封装作为其底层容器类,queue提供一组特定的成员函数来访问其元素。元素从特定容器的“尾部”弹出,其称为优先队列的顶部。
  4. 底层容器可以是任何标准容器类模板,也可以是其他特定设计的容器类。容器应该可以通过随机访问迭代器访问,并支持以下操作:
  • empty():检测容器是否为空
  • size():返回容器中有效元素个数
  • front():返回容器中第一个元素的引用
  • push_back():在容器尾部插入元素
  • pop_back():删除容器尾部元素
  1. 标准容器类vector和deque满足这些需求。默认情况下,如果没有为特定的priority_queue类实例化指定容器类,则使用vector。
  2. 需要支持随机访问迭代器,以便始终在内部保持堆结构。容器适配器通过在需要时自动调用算法函数make_heap、push_heap和pop_heap来自动完成此操作。

2. priority_queue的使用

优先级队列默认使用vector作为其底层存储数据的容器,在vector上又使用了堆算法将vector中元素构造成堆的结构,因此priority_queue就是堆,所有需要用到堆的位置,都可以考虑使用priority_queue 。注意:默认情况priority_queue是大堆。

[注意]

  1. 默认情况下,priority_queue是大堆。
#include <vector>
#include <queue>
#include <functional> // greater算法的头文件
int main()
{
    
    
	// 默认情况下,创建的是大堆,其底层按照小于号比较
	vector<int> v{
    
     3,2,7,6,0,4,1,9,8,5 };
	priority_queue<int> q1;
	for (auto& e : v)
		q1.push(e);
	cout << q1.top() << endl;
	// 如果要创建小堆,将第三个模板参数换成greater比较方式
	priority_queue<int, vector<int>, greater<int>> q2(v.begin(), v.end());
	cout << q2.top() << endl;
}

在这里插入图片描述

  1. 如果在priority_queue中放自定义类型的数据,用户需要在自定义类型中提供> 或者< 的重载。

3. 仿函数

3.1 仿函数的介绍

对于上面的创建成小堆的格式,发现有一个:greater<int> 参数,priority_queue中自带的缺省参数为less,即升序大堆,因此想要建大堆就不需要进行换参数的操作,直接priority_queue<int>就是大堆,上面的结果就是这样,想要换成小堆就得这样:priority_queue<int, vector<int>, greater<int>>通过利用仿函数可以将比较的顺序进行颠倒,通过改变内置的比较符号从而灵活的改变按照大小的排序。

仿函数又叫函数对象,仿函数就是一个类,仿函数的函数对象就是类对象,成员是什么无所谓,下面先看一个简单的例子:

namespace yj
{
    
    
	template<class T>
	class less
	{
    
    
	public://operator()就是一个运算符重载
		bool operator()(const T& x, const T& y) const
		{
    
    
			return x < y;
		}
	};

	template<class T>
	class greater
	{
    
    
	public:

		bool operator()(const T& x, const T& y) const
		{
    
    
			return x > y;
		}
	};
}

int main()
{
    
    
	yj::less<int> lessFunc;
	lessFunc(1, 2);
    //lessFunc.operator()(1, 2);//实际上是这样的展开过程
	return 0;
}

即在之前的印象中,通过调用lessFunc,就可以把lessFunc看成一个函数名,但在这里lessFunc是一个函数对象,仿函数的对象,这个对象可以像函数一样去使用,但实际上这个对象不是直接像往常的函数一样直接调用。而是调用了运算符重载,即实际上就是这样: lessFunc.operator()(1, 2);但不会这样去写,因为运算符重载就是为了可读性。

3.2 仿函数的好处

在C语言中qsort的升降序是使用函数指针,传入大于就是大于比较,传入小于就是小于比较。那C++为什么不使用函数指针呢?

候捷老师总结:

为什么函数指针可以达到“将数组操作当做算法的参数”,那又何必有所谓的仿函数呢?函数指针毕竟不能满足STL对抽象性的要求,也不能满足软件积木的要求---->函数指针无法和STL其他组件搭配,产生更灵活的变化。

4. priority_queue的模拟实现

学习了优先级队列中的仿函数的相关知识,就可以模拟实现一个priority_queue了,与上次的stack/queue的模拟实现类似,底层也是适配器实现,没有用到迭代器。

namespace yj
{
    
    

    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 adjust_up(int child)
        {
    
    

            Compare com;

            int parent = (child - 1) / 2;

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


        void adjust_down(int parent)
        {
    
    

            Compare com;

            int child = parent * 2 + 1;

            while (child < _con.size())
            {
    
    
                //找左右孩子中最大的那个
                if(child+1<_con.size() && com(_con[child], _con[child + 1]))
                    ++child;

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


        void push(const T& x)     //插入元素 + 向上调整
        {
    
    
            _con.push_back(x);
            adjust_up(_con.size() - 1);     
        }

        
        void pop()                //删除元素(交换堆顶元素和堆尾元素,后删除) + 向下调整
        {
    
    
            swap(_con[0], _con[_con.size() - 1]);
            _con.pop_back();
            adjust_down(0);
        }

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


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


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

    private: 
        Container _con;
    };
}

5. 用仿函数实现日期比较

日期类的比较用仿函数:

namespace yj: 
{
    
    
    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;
        }
    };
    
    class Date
    {
    
    
    public:
        Date(int year = 1900, int month = 1, int day = 1)
            : _year(year)
            , _month(month)
            , _day(day)
        {
    
    }

        bool operator<(const Date& d)const
        {
    
    
            return (_year < d._year) ||
                (_year == d._year && _month < d._month) ||
                (_year == d._year && _month == d._month && _day < d._day);
        }

        bool operator>(const Date& d)const
        {
    
    
            return (_year > d._year) ||
                (_year == d._year && _month > d._month) ||
                (_year == d._year && _month == d._month && _day > d._day);
        }

        friend ostream& operator<<(ostream& _cout, const Date& d)
        {
    
    
            _cout << d._year << "-" << d._month << "-" << d._day;
            return _cout;
        }

    private:
        int _year;
        int _month;
        int _day;
    };
    
    void test2()
    {
    
    
        priority_queue<Date> q0;
        q0.push(Date(2018, 10, 29));
        q0.push(Date(2018, 10, 30));
        q0.push(Date(2018, 10, 28));
        cout << q0.top() << endl;
       
        priority_queue<Date, vector<Date>,  greater<Date>> q1;
        q1.push(Date(2018, 10, 29));
        q1.push(Date(2018, 10, 30));
        q1.push(Date(2018, 10, 28));
        cout << q1.top() << endl;
     }
}

在这里插入图片描述

如果是Date*,就需要自己再去写一个仿函数:即地址解引用的仿函数。

namespace yj: 
{
    
    
    class Date
    {
    
    
    public:
        Date(int year = 1900, int month = 1, int day = 1)
            : _year(year)
            , _month(month)
            , _day(day)
        {
    
    }

        bool operator<(const Date& d)const
        {
    
    
            return (_year < d._year) ||
                (_year == d._year && _month < d._month) ||
                (_year == d._year && _month == d._month && _day < d._day);
        }

        bool operator>(const Date& d)const
        {
    
    
            return (_year > d._year) ||
                (_year == d._year && _month > d._month) ||
                (_year == d._year && _month == d._month && _day > d._day);
        }

        friend ostream& operator<<(ostream& _cout, const Date& d)
        {
    
    
            _cout << d._year << "-" << d._month << "-" << d._day;
            return _cout;
        }

    private:
        int _year;
        int _month;
        int _day;
    };    

    class PDateLess        //大堆
    {
    
    
    public:
        bool operator()(const Date* p1, const Date* p2)
        {
    
    
            return *p1 < *p2;
        }
    };

    class PDateGreater        //小堆
    {
    
    
    public:
        bool operator()(const Date* p1, const Date* p2)
        {
    
    
            return *p1 > *p2;
        }
    };
    
    void test2()
    {
    
    
        priority_queue<Date*, vector<Date*>, PDateLess> q1;
        q1.push(new Date(2018, 10, 29));
        q1.push(new Date(2018, 10, 30));
        q1.push(new Date(2018, 10, 28));
        cout << *(q1.top()) << endl;
        
        priority_queue<Date*, vector<Date*>, PDateGreater> q2;
        q2.push(new Date(2018, 10, 29));
        q2.push(new Date(2018, 10, 30));
        q2.push(new Date(2018, 10, 28));
        cout << *(q2.top()) << endl;
    }
}

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/Ryujianli/article/details/129630295