【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];
}


相关文章
|
3月前
|
C++
C++(十七)仿函数
### Functor 仿函数简介 Functor(仿函数)是一种通过在类中实现 `operator()` 使其行为像函数的对象。这种方式可以让类拥有更多可定制的功能,同时保持函数般的简洁调用方式。下面展示了如何定义和使用一个计算幂运算的仿函数类,并通过示例说明了其在 `std::sort` 中的优势与灵活性。
|
5月前
|
存储 算法 Java
【C++】优先级队列priority_queue模拟实现&&仿函数
【C++】优先级队列priority_queue模拟实现&&仿函数
42 1
|
6月前
|
算法 安全 编译器
【C++进阶】模板进阶与仿函数:C++编程中的泛型与函数式编程思想
【C++进阶】模板进阶与仿函数:C++编程中的泛型与函数式编程思想
62 1
|
5月前
|
存储 算法 C语言
【C++】详解STL的适配器容器之一:优先级队列 priority_queue
【C++】详解STL的适配器容器之一:优先级队列 priority_queue
|
7月前
|
存储 算法 C语言
从C语言到C++_19(容器适配器+stack和queue模拟实现+优先级队列priority_queue)(下)
从C语言到C++_19(容器适配器+stack和queue模拟实现+优先级队列priority_queue)
58 2
|
7月前
|
算法 C语言 C++
从C语言到C++_20(仿函数+优先级队列priority_queue的模拟实现+反向迭代器)(上)
从C语言到C++_20(仿函数+优先级队列priority_queue的模拟实现+反向迭代器)
56 1
|
7月前
|
算法 C语言 C++
从C语言到C++_20(仿函数+优先级队列priority_queue的模拟实现+反向迭代器)(下)
从C语言到C++_20(仿函数+优先级队列priority_queue的模拟实现+反向迭代器)
31 0
从C语言到C++_20(仿函数+优先级队列priority_queue的模拟实现+反向迭代器)(下)
|
6月前
|
C++
C++函数对象(仿函数)
C++函数对象(仿函数)
|
6月前
|
存储 设计模式 算法
【C++航海王:追寻罗杰的编程之路】priority_queue(优先队列) | 容器适配器你知道哪些?
【C++航海王:追寻罗杰的编程之路】priority_queue(优先队列) | 容器适配器你知道哪些?
57 0
|
7月前
|
算法 C语言 C++
从C语言到C++_19(容器适配器+stack和queue模拟实现+优先级队列priority_queue)(中)
从C语言到C++_19(容器适配器+stack和queue模拟实现+优先级队列priority_queue)
51 0