当前位置: 首页 > news >正文

CD53.【C++ Dev】模拟实现优先级队列(含仿函数)

目录

1.知识回顾

2.看看STL的堆

3.仿函数

特点1: 类似函数

特点2: 能保存状态

4.容器适配器的复习

5.模拟实现优先级队列

框架代码

构造函数

向下调整函数

测试代码:使用std的仿函数

测试代码:传入自制的仿函数

测试代码:使用自定义类型建堆

size

empty

pop

push

向上调整函数

top

6.LeetCode上的题


1.知识回顾

堆的复习:

101.【C语言】数据结构之二叉树的堆实现(顺序结构) 1

102.【C语言】数据结构之二叉树的堆实现(顺序结构) 2

103.【C语言】数据结构之用堆对数组排序

104.【C语言】数据结构之建堆的时间复杂度分析

118.【C语言】数据结构之排序(堆排序和冒泡排序)

之前在CC38.【C++ Cont】模拟实现堆文章写过堆的模拟实现,只不过没有带上模版,而且比较粗糙,这里重新写

2.看看STL的堆

看看框架:

#ifndef __STL_LIMITED_DEFAULT_TEMPLATES
template <class T, class Sequence = vector<T>, class Compare = less<typename Sequence::value_type> >
#else
template <class T, class Sequence, class Compare>
#endif
class  priority_queue 
{//......
}

Sequence是容器适配器,Compare可以传入仿函数

3.仿函数

stackoverflow网仿函数functors(不是functions)给出的定义:

A functor is pretty much just a class which defines the operator(). That lets you create objects which "look like" a function.

特点1: 类似函数

仿函数是有operator()成员函数的类,能起到类似函数("仿")的作用.不是函数却可以像函数一样调用

例如以下的例子:

class Less
{
public:bool operator()(const int& x, const int& y) const{return x < y;}
};

测试代码:

下面是使用仿函数完整的写法,Less第一个字母大写,为了和std命名空间的less区分

int main()
{Less lessfunction;//先实例化//再调用operator()cout << lessfunction.operator()(2, 3);return 0;
}

运行结果:

当然可以简写:类的对象可以像函数一样使用,替代函数指针

lessfunction(2, 3);//写法类似函数

也可以写成匿名对象(即Less())的形式:

Less()(2, 3);

当然也可以加入模版参数:

#include <iostream>
using namespace std;
template<class T>
class Less
{
public:bool operator()(const T& x, const T& y) //const{return x < y;}
};
int main()
{Less<int> lessfunction;//类要显式实例化cout << lessfunction(2, 3);return 0;
}

运行结果:

特点2: 能保存状态

以stackoverflow what-are-c-functors-and-their-uses网的回答的代码为例:

#include <algorithm>    
#include <vector>      
struct add_x 
{add_x(int val) : x(val) {}  int operator()(int y) const { return x + y; }private:int x;
};int  main()
{std::vector<int> in = { 1,2,3 };std::vector<int> out(in.size());std::transform(in.begin(), in.end(), out.begin(), add_x(1));return 0;
}

运行结果:

(注:transform函数的作用是将容器中的元素进行某种形式的转换并将结果存储到另一个容器中)

这里使用其中一种调用方法: 

std::transform(in.begin(), in.end(), out.begin(), add_x(1));

1.transform的第三个参数是对单个元素应用的一元操作函数或函数对象

2. add_x(1)先用1去构造add_x匿名对象: add_x(int val) : x(val) {} ,这样私有变量x的值为1

3.int operator()(int y) const { return x + y; }可以看出会对每一个元素都加x

仿函数比普通函数要好的一点是仿函数内部可以保存状态,例如上方的私有变量x经过初始化就不变了,如果使用函数就必须写一个恰好把参数加 x 的函数,这样比较麻烦,而上方的仿函数是通用的,它会加上你在初始化时指定的任意值

而且某些情况下仿函数会被编译器内联:

    
#include <iostream>
struct add_x
{add_x(int val) : x(val) {}int operator()(int y) const { return x + y; }private:int x;
};int  main()
{int y = 0;std::cin >> y;y=add_x(1).operator()(y);std::cout << y;return 0;
}

 反汇编分析:没有出现call指令,说明被内联了

4.容器适配器的复习

参见CD49.【C++ Dev】容器适配器模式(含typename的用法)文章

5.模拟实现优先级队列

对标STL库,使用STL的vector和less

框架代码

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

剩下只需要实现成员函数:

构造函数

使用迭代区间:[first,last),先讲元素存到容器适配器中,然后对容器适配器建堆

template<class InputIterator>
priority_queue(InputIterator first, InputIterator last)
{while (first != last){_con.push_back(*first);first++;}adjust_down(Container _con);
}

向下调整函数

之前在102.【C语言】数据结构之二叉树的堆实现(顺序结构) 2文章中写过:

void AdjustDown(HPDataType* a, int n, int parent)
{int child = parent * 2 + 1;while (child < n){if (a[child + 1] > a[child]){child++;}if (a[child] > a[parent]){Swap(&a[child], &a[parent]);parent = child;child = child * 2 + 1;}else{break;}}
}

但需要改:
1.需要借助仿函数Compare,不能写<或>

2.child+1可能会越界,需要检查

使用短路运算的特性即可 if (child+1<_con.size() && _cmp(_con[child], _con[child + 1]))

3.默认情况下是建立大堆,调整下比较顺序

_cmp(_con[child], _con[child + 1])

_cmp(_con[parent], _con[child])

namespace mystl
{template <class T, class Container = std::vector<T>,class Compare = std::less<T> >class priority_queue{public:template<class InputIterator>priority_queue(InputIterator first, InputIterator last){while (first != last){_con.push_back(*first);first++;}for (int i = (_con.size() - 2) / 2; i >= 0; i--){adjust_down(i);}}private:Container _con;Compare _cmp;void adjust_down(int parent){int child = parent * 2 + 1;while (child < _con.size()){if (child+1<_con.size() && _cmp(_con[child], _con[child + 1])){child++;}if (_cmp(_con[parent], _con[child])){std::swap(_con[child], _con[parent]);parent = child;child = child * 2 + 1;}else{break;}}}};
}
测试代码:使用std的仿函数
#include "queue.h"
int  main()
{std::vector<int> arr = { 1,3,2,5,9 };mystl::priority_queue<int> pq(arr.begin(),arr.end());return 0;
}

运行结果: 

测试代码:传入自制的仿函数
#include "queue.h"
template<class T>
struct mygreater
{
public:bool operator()(T& x, T& y){return x > y;}
};
int  main()
{std::vector<int> arr = { 1,3,2,5,9 };mystl::priority_queue<int,std::vector<int>, mygreater<int>> pq(arr.begin(),arr.end());return 0;
}

运行结果:

 

测试代码:使用自定义类型建堆

例如之前CD20.【C++ Dev】类和对象(11) 日期类对象的成员函数(++、--、日期-日期)文章写的日期类,需要手动写仿函数

#include "queue.h"
class Date
{
public:Date(int year, int month, int day){_year = year;_month = month;_day = day;}Date(const Date& x){_year = x._year;_month = x._month;_day = x._day;}bool operator< (const Date& d2) const{if (_year < d2._year)return true;else if (_year == d2._year && _month < d2._month)return true;else if (_year == d2._year && _month == d2._month && _day < d2._day)return true;return false;}
private:int _year;int _month;int _day;
};int  main()
{std::vector<Date> arr = { Date(2025, 7, 15),Date(2024, 6, 15),Date(2025, 9, 15),Date(2026, 1, 1) };mystl::priority_queue<Date,std::vector<Date>, std::less<Date>> pq(arr.begin(),arr.end());return 0;
}

有operator<,可以用less 

运行结果:

size

返回堆的大小

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

empty

判断堆是否为空

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

pop

弹出堆顶元素,注意先让首元素和尾元素交换,这样才能尾删

void pop()
{std::swap(_con[0], _con[size() - 1]);_con.pop_back();adjust_down(0);
}

测试代码:

#include "queue.h"
int  main()
{std::vector<int> arr = { 1,3,2,5,9 };mystl::priority_queue<int,std::vector<int>, std::less<int>> pq(arr.begin(),arr.end());pq.pop();return 0;
}

运行结果:

push

尾插后向上调整

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

向上调整函数

之前在101.【C语言】数据结构之二叉树的堆实现(顺序结构) 1文章中写过:

void AdjustUp(HPDataType* a, int child)
{int parent = (child - 1) / 2;while (child>0){if (a[child] > a[parent]){Swap(&a[parent], &a[child]);child = parent;parent = (parent - 1) / 2;}else{break;}}
}

稍微改改:

1.需要借助仿函数Compare,不能写<或>

3.默认情况下是建立大堆,调整下比较顺序

void adjust_up(int child)
{int parent = (child - 1) / 2;while (child > 0){if (_cmp(_con[parent],_con[child])){std::swap(_con[parent], _con[child]);child = parent;parent = (parent - 1) / 2;}else{break;}}
}

测试代码:

#include "queue.h"
int  main()
{std::vector<int> arr = { 1,3,2,5,9 };mystl::priority_queue<int,std::vector<int>, std::less<int>> pq(arr.begin(),arr.end());pq.push(10);return 0;
}

运行结果:

top

返回堆顶元素

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

6.LeetCode上的题

使用自制的priority_queue

namespace mystl
{template <class T, class Container = std::vector<T>,class Compare = std::less<T> >class priority_queue{public:template<class InputIterator>priority_queue(InputIterator first, InputIterator last){while (first != last){_con.push_back(*first);first++;}for (int i = (_con.size() - 2) / 2; i >= 0; i--){adjust_down(i);}}size_t size(){return _con.size();}bool empty(){return _con.empty();}void pop(){std::swap(_con[0], _con[size() - 1]);_con.pop_back();adjust_down(0);}void push(const T& x){_con.push_back(x);adjust_up(_con.size() - 1);}T& top(){return _con[0];}private:Container _con;Compare _cmp;void adjust_down(int parent){int child = parent * 2 + 1;while (child < _con.size()){if (child+1<_con.size() && _cmp(_con[child], _con[child + 1])){child++;}if (_cmp(_con[parent], _con[child])){std::swap(_con[child], _con[parent]);parent = child;child = child * 2 + 1;}else{break;}}}void adjust_up(int child){int parent = (child - 1) / 2;while (child > 0){if (_cmp(_con[parent],_con[child])){std::swap(_con[parent], _con[child]);child = parent;parent = (parent - 1) / 2;}else{break;}}}};
}class Solution {
public:int findKthLargest(vector<int>& nums, int k) {mystl::priority_queue<int,vector<int>,greater<int>> pq(nums.begin(),nums.begin()+k);for (int j=k;j<nums.size();j++){if (pq.top()<nums[j]){pq.pop();pq.push(nums[j]);}}return pq.top();}
};

提交结果:

http://www.dtcms.com/a/283181.html

相关文章:

  • 【计算机网络】数据通讯第二章 - 应用层
  • 深度学习之反向传播
  • 【迭代】PDF绘本录音播放,点读笔方案调研和初步尝试
  • leetcode 725 分割链表
  • 微算法科技研究量子视觉计算,利用量子力学原理提升传统计算机视觉任务的性能
  • Kafka入门
  • 语音增强论文汇总
  • Go基本数据类型
  • 81、面向服务开发方法
  • Redisson实现分布式锁
  • Redisson实现限流器详解:从原理到实践
  • HTML 入门教程:从零开始学习网页开发基础
  • 前端知识:浏览器工作原理与开发者工具知识笔记
  • WIN10系统优化篇(一)
  • Leetcode 02 java
  • IDEA报错“资源找不到”?重启就好了!!?
  • 使用Dify构建HR智能助理,深度集成大模型应用,赋能HR招聘管理全流程,dify相关工作流全开源。
  • 城市蓝影.
  • 服务注册nacos和OpenFerign(用于封装跨服务之间的调用方法)
  • kubernetes学习笔记(一)
  • 数据结构 双向链表(2)--双向链表的实现
  • 黄仁勋链博会演讲实录:脱掉皮衣,穿上唐装,中文开场
  • 完善评论发布功能
  • PHP面向对象编程:类与对象的基础概念与实践
  • 从0到1搭建Lazada账号矩阵:自养号测评的精细化养号全攻略
  • Linux 定时器应用示例
  • 功能测试和回归测试
  • C# WPF后台设置控件样式失效的解决方法
  • 【Vue】tailwindcss + ant-design-vue + vue-cropper 图片裁剪功能(解决遇到的坑)
  • 从规模到效率:大模型三大定律与Chinchilla定律详解