【C++】priority_queue使用和模拟实现——仿函数(下)

简介: 【C++】priority_queue使用和模拟实现——仿函数(下)

2. 仿函数


1. 仿函数的概念

在上文中,我们看到priority_queue类模板中,有三个模板参数,其中第三个就是仿函数

仿函数到底是什么呢?

仿函数(functors),也叫函数对象(function objects),是STL六大组件中的一部分,这里我们没办法一次性讲完它,所以就基于priority_queue的实现稍微介绍一下。


实际上,就实现意义而言,函数对象这个名字更加贴切:一种具有函数特质的对象。但是,仿函数似乎能更加符合的描述他的行为。所以这里我们就采用仿函数这种叫法。


在学习STL之前我们就已经了解了泛型编程的概念,C++引入了模板让我们的编程能够随意的控制数据类型,现在引入了仿函数的概念,让我们能够控制逻辑。


那么现在让我们见见仿函数:

1f70eb17fbdc1246bf2a446301dd0d61.png

1b2f793994c9c3a1856ca6e9406e3cef.png

这两个就是我们在priority_queue的参数列表中可能用到的仿函数。

29af9244576fc2a834b40b2b5e2761ea.png

可以看到,在使用的时候,实际上类似一个函数的调用,因此被称为仿函数


2.尝试实现仿函数

仿函数的本质就是一个运算符重载operator(),重载的是函数调用的运算符。由于仿函数本身也就是一个类模板,所以我们的实现如下

template<class T>
class less
{
    public:
    bool operator()(const T& x, const T& y)//less和greater需要实现的就是一个比较,所以这里的返回值是bool类型
    {
        return x < y;
    }
};
template<class T>
class greater
{
    public:
    bool operator()(const T& x, const T& y)
    {
        return x > y;
    }
};


这样就算是实现了less和greater的仿函数。

这里只是稍微提了一下仿函数的概念,仿函数毕竟是STL六大组件之一,其中包含的东西还有很多,我们对仿函数的学习还在路上


3.priority_queue的模拟实现


有了上述只是的铺垫,现在我们已经有了模拟实现priority_queue的能力,那么just do it。

1.priority_queue的结构

首先,对于函数模板的设计,我们和库里面对其,给了三个参数,分别表示参数存入容器的参数类型,容器类型和仿函数,其中默认的仿函数是less,建大堆。

template<class T, class Container = std::vector<T>, class Compare = less<T>>
class priority_queue
{
public:
    //...
private:
    Container _con;
}; 


2. 接口实现

按照我们之前在【数据结构】树与二叉树实现堆的经验,一定需要实现的两个接口是向上调整和向下调整,这也是整个堆的核心接口,所以我们就先着重实现这两个功能性接口,这两个接口实现完成之后,其他的结构性接口都很容易实现啦。


1.向下调整算法

c81e00578641733c5096cc0906fe280f.png

这里,我们以小堆为例(在这里偷个懒:小堆的图有以前画过的),我们需要做的事情就是找到两个孩子中较小的,然后与父节点比较大小,如果父节点大于子节点就执行交换,然后原来的子节点成为新的父节点,再次进行上述步骤。直到符合堆的结构

void adjust_down(size_t parent)
{
    Compare cmp;//实例化仿函数
    int minchild = 2 * parent + 1;
    while (minchild < _con.size())
    {
        if (minchild + 1 < _cin.size() && cmp(_con[minchild], con[minchild + 1]))//使用仿函数找到符合条件的子节点
        {
            minchild++;
        }
        if (cmp(_con[parent], _con[minchild]))//判断是否满足堆结构
        {
            std::swap(_con[parent], _con[minchild]);
            //父子节点迭代
            parent = minchild;
            minchild = 2 * parent + 1;
        }
    }
}


2. 向上调整算法

e1d5932d2003467b0a41bcc02612b0fc.png

这里向上调整的操作,从指定的孩子节点位置开始,和父节点比较,判断是否满足堆结构,如果不满足,就交换父子节点,然后原来的父节点编程子节点,再次进行上述操作,直到满足堆结构为止。

void adjust_up(size_t child)
{
    Compare cmp;//实例化仿函数
    size_t parent = (child - 1) / 2;
    while (child > 0)
    {
        if (cmp(_con[parent], _con[child]))
        {
            std::swap(_con[parent], _con[child]);
            //父子迭代
            child = parent;
            parent = (child - 1) / 2;
        }
    }
}


3.构造函数

构造函数分为无参的构造和迭代器区间构造

//无参的构造函数
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)
    {
        adjust_down(i);
    }
}


4.修改数据

priority_queue的数据修改只有push和pop两种情况

push数据就直接尾插,然后向上调整堆,直到满足堆的结构即可,pop数据就交换堆顶数据和最后一个数据,然后容器pop_back,然后向下调整直到剩余数据满足堆结构即可

void push(const T& val)
{
    _con.push_back(val);//尾插数据
    adjust_up(_con.size() - 1);//向上调整堆结构
}
void pop()
{
    swap(_con[0], _con[_con.size() - 1]);//交换堆顶和堆内最后一个元素
    _con.pop_back;//容器尾删
    adjust_down(0);//向下调整堆结构
}


5.获取数据

剩下的接口就直接复用容器提供的接口即可

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


相关文章
|
5月前
|
C++ 容器
【C++】仿函数在模板中的应用——【默认模板实参】详解(n)
【C++】仿函数在模板中的应用——【默认模板实参】详解(n)
|
19天前
|
存储 算法 C++
c++的学习之路:17、stack、queue与priority_queue
c++的学习之路:17、stack、queue与priority_queue
31 0
|
2月前
|
存储 算法 C语言
【C++入门到精通】C++入门 —— priority_queue(STL)优先队列
本文介绍了C++的STL组件`std::priority_queue`,它是一个容器适配器,实现优先队列数据结构,通常基于堆实现。文章讨论了优先队列的基本概念、特点和底层堆结构,强调了其自动排序和优先级最高元素的访问。还展示了如何定义、插入、访问和移除元素,以及自定义比较函数。此外,提供了模拟实现`priority_queue`的代码示例,探讨了仿函数的作用,包括默认的`std::less`和自定义比较函数。文章鼓励读者进一步探索C++的优先队列及其应用。
27 3
|
18天前
|
编译器 C语言 C++
【C++进阶(七)】仿函数深度剖析&模板进阶讲解
【C++进阶(七)】仿函数深度剖析&模板进阶讲解
|
29天前
|
存储 算法 C++
【C++初阶】STL详解(九) priority_queue的使用与模拟实现
【C++初阶】STL详解(九) priority_queue的使用与模拟实现
21 0
|
2月前
|
存储 算法 C++
C++:stack、queue、priority_queue增删查改模拟实现、deque底层原理
C++:stack、queue、priority_queue增删查改模拟实现、deque底层原理
35 0
|
2月前
|
算法 C++ 容器
【C++练级之路】【Lv.10】【STL】priority_queue类和反向迭代器的模拟实现
【C++练级之路】【Lv.10】【STL】priority_queue类和反向迭代器的模拟实现
|
2月前
|
存储 算法 调度
【C/C++ 数据结构 优先队列】了解学习`std::priority_queue`的使用
【C/C++ 数据结构 优先队列】了解学习`std::priority_queue`的使用
44 3
|
2月前
|
算法 安全 编译器
C++:模版进阶 | Priority_queue的模拟实现
C++:模版进阶 | Priority_queue的模拟实现
|
3月前
|
算法 C语言 C++
C++:priority_queue模拟实现
C++:priority_queue模拟实现
22 0