您的位置:首页 > 教育 > 锐评 > 南宁专业网站开发_ps网页设计从零开始教程_推广软文怎么写_夫唯seo怎么样

南宁专业网站开发_ps网页设计从零开始教程_推广软文怎么写_夫唯seo怎么样

2025/5/25 10:13:08 来源:https://blog.csdn.net/weixin_71964451/article/details/147543404  浏览:    关键词:南宁专业网站开发_ps网页设计从零开始教程_推广软文怎么写_夫唯seo怎么样
南宁专业网站开发_ps网页设计从零开始教程_推广软文怎么写_夫唯seo怎么样

priority_queue的介绍

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

它的主要特点是:

  1. 最大堆实现priority_queue默认是一个最大堆,这意味着最高优先级的元素(最大值)总是位于队列的前端。
  2. 不直接支持索引访问:与数组或向量不同,优先队列不支持通过索引访问元素。
  3. 只允许在队尾插入元素:新元素只能被添加到队列的末尾。
  4. 只允许在队首删除元素:只能从队列的前端移除元素,这通常是最高优先级的元素。
  5. 不保证元素的顺序:除了最高优先级的元素总是在队首外,其他元素的顺序是未定义的。

priority_queue的使用

请添加图片描述

优先级队列默认使用vector作为其底层存储数据的容器,在vector上又使用了堆算法将vector中元素构造成堆的结构,因此priority_queue就是堆,所有需要用到堆的位置,都可以考虑使用priority_queue。注意:默认情况下priority_queue是大堆。
来看一下它的函数接口有哪些吧.
请添加图片描述

下面我们就来模拟实现其中一些常用的函数接口吧。


基本结构:

template<class T, class Container = vector<T>>
class priority_queue{private:Container _con;}

top

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

取得优先级最高的元素,其实就是vector的第一个元素。


empty & size

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

由于底层容器是vector,所以进行操作的时候也是对vector的操作一样。


push

void adjust_up(int child)
{int parent = (child - 1) / 2;while (child > 0){if (_con[parent] < _con[child]){swap(_con[child], _con[parent]);child = parent;parent = (child - 1) / 2;}else{break;}}
}
void push(const T& x)
{_con.push_back(x);adjust_up(_con.size() - 1);
}

pop

void adjust_down(int parent)
{size_t child = parent * 2 + 1;while (child < _con.size()){if (child + 1 < _con.size() && _con[child] < _con[child + 1]){++child;}if (_con[parent] < _con[child]){swap(_con[child], _con[parent]);parent = child;child = parent * 2 + 1;}else{break;}}
}void pop()
{swap(_con[0], _con[_con.size() - 1]);_con.pop_back();adjust_down(0);
}

我们已经知道priority_queue类似堆,所以进行加入或者删除数据的时候,其实就是跟堆一样,在添加完之后要满足大堆或者小堆的性质。而要满足堆的性质,就需要用到向上调整算法和向下调整算法。如果有不知道这个算法的同学,可以点开下面这个链接,里面详细介绍了这2种算法。(向上调整算法在堆插入里面)
向上调整和向下调整算法


仿函数

仿函数(Functor)是一种对象,它的行为类似于函数。仿函数可以被当作函数使用,但它们实际上是对象,这意味着它们可以拥有状态(成员变量)和行为(成员函数)。仿函数通常用于算法中,例如标准模板库(STL)中的算法,允许用户自定义比较或操作逻辑。下面来看一个例子:

struct Add
{int operator()(int x, int y){return x + y;}
};int main()
{Add add;int ret = add(1, 2);cout << ret << endl;return 0;
}

在上面的代码中,我们定义了Add的结构体,里面重载了operator()这个运算符,实现了2个整数相加的逻辑。在主函数中,我们创建了一个Add的实例化对象add,并通过调用函数调用运算符对两个整数进行相加。由于跟普通函数调用的方法一样,只是多了需要实例化一个对象这一步骤,所以 称之为仿函数。那么为什么不直接写一个函数来实现加法运算,而要用到仿函数呢?这就要提到普通函数与仿函数的区别了。

  1. 对象 vs 非对象
    • 仿函数是一个对象,它可以拥有状态(成员变量)和行为(成员函数)。
    • 普通函数不是一个对象,它没有状态,只能执行预定义的操作。
  2. 调用方式
    • 仿函数通过重载operator()来实现调用,可以像普通函数那样使用参数列表进行调用。
    • 普通函数通过其函数名直接调用,需要符合函数签名。
  3. 状态保持
    • 仿函数可以维护状态,因为它有成员变量,可以在多次调用之间保持数据。
    • 普通函数不能维护状态,因为它们没有成员变量。
  4. 多态性
    • 仿函数可以实现多态性,不同的仿函数可以有不同的operator()实现。
    • 普通函数是静态的,它们在编译时类型就已经确定,不具备运行时多态性。
  5. 参数传递
    • 仿函数可以作为对象传递,可以被赋值给其他对象或作为参数传递给函数。
    • 普通函数可以通过函数指针或函数引用传递,但不能作为对象传递。
  6. 泛型编程
    • 仿函数可以与模板结合使用,实现泛型编程,适用于多种数据类型。
    • 普通函数是特定类型的,不能直接用于泛型编程。
      C语言中函数指针用法如下:
int Add(int x, int y)
{return x + y;
}int main()
{int(*pf3)(int, int) = Add;printf("%d\n", (*pf3)(2, 3));   //5printf("%d\n", pf3(3, 5));      //8return 0;
}

请添加图片描述

函数指针其实就是用来存放函数的地址,未来通过地址能够调⽤函数的。如果我们要将函数的地址存放起来,就得创建函数指针变量。
在C语言中如果我们想将一个函数作为参数传给其它函数,那么就要在这个函数中准确写出函数指针的类型,所以这就是要使用仿函数的原因之一。
看完了仿函数之后,如果我们想将优先队列按照小堆的方式排列,难道还需要改变2个算法之间的符号方向吗?这肯定是不需要的,只要我们写出比较大小的仿函数就行了,按照自己的规则来比较大小。另外还需要在模板参数中传入第3个参数。

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

完整版代码:

#pragma once
#include<vector>namespace hjw
{template<class T, class Container = vector<T>,class Compaer = Less<T>>class priority_queue{public:priority_queue(){}void adjust_up(int child){Compaer com;int parent = (child - 1) / 2;while (child > 0){if(com(_con[parent],_con[child]))//if (_con[parent] < _con[child]){swap(_con[child], _con[parent]);child = parent;parent = (child - 1) / 2;}else{break;}}}void adjust_down(int parent){Compaer com;size_t child = parent * 2 + 1;while (child < _con.size()){//if (child + 1 < _con.size() && _con[child] < _con[child + 1])if (child + 1 < _con.size() && com(_con[child], _con[child + 1])){++child;}//if (_con[parent] < _con[child])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);}bool empty(){return _con.empty();}const T& top(){return _con[0];}size_t size(){return _con.size();}private:Container _con;};
}

使用less的时候是大堆,使用greater是小堆。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com