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

C++基础:模拟实现priority_queue(堆),详细介绍仿函数

引言

        手把手带你模拟实现堆:priority_queue。详细介绍仿函数,感受仿函数的魅力

一、模拟实现priority_queue

优先级队列:priority_queue,底层的数据结构是堆。

这里实现的是大根堆,小根堆把比较符号改一下即可。

用vector做适配器来实现

先写出基本框架:

	template<class T, class container = vector<T>>class priority_queue{public://函数private:container _con;  };

两个核心操作:向上调整算法,向下调整算法

(看代码理解,很简单)

	template<class T, class container = vector<T>>class priority_queue{public://函数private:void adjust_down(size_t parent) //核心,向下调整算法{size_t child = parent * 2 + 1;while (child < _con.size()){if (child + 1 < _con.size() - 1 && _con[child] < _con[child + 1]) child++;if (_con[parent] < _con[child]){swap(_con[parent], _con[child]);parent = child;child = parent * 2 + 1;}else{break;}}}void adjust_up(size_t child) //核心,向上调整算法{size_t parent = (child - 1) / 2;while (child > 0){if (_con[parent] < _con[child]){swap(_con[parent], _con[child]);child = parent;parent = (child - 1) / 2;}else{break;}}}private:container _con;  };

插入push(),删除pop(),返回堆顶元素top(),判空empty(),堆中元素个数size()

	template<class T, class container = vector<T>>class priority_queue{public: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);}const T& top()// 返回堆顶元素{return _con[0];}bool empty() const //判空{return _con.empty();}size_t size() const //返回元素个数{return _con.size();}private:void adjust_down(size_t parent) //核心,向下调整算法{size_t child = parent * 2 + 1;while (child < _con.size()){if (child + 1 < _con.size() - 1 && _con[child] < _con[child + 1]) child++;if (_con[parent] < _con[child]){swap(_con[parent], _con[child]);parent = child;child = parent * 2 + 1;}else{break;}}}void adjust_up(size_t child) //核心,向上调整算法{size_t parent = (child - 1) / 2;while (child > 0){if (_con[parent] < _con[child]){swap(_con[parent], _con[child]);child = parent;parent = (child - 1) / 2;}else{break;}}}private:container _con;  };

用迭代器区间构造堆

注意:自己写了构造函数,编译器不会生成默认构造函数了,所以要强制编译器自己生成默认构造函数。

	template<class T, class container = vector<T>>class priority_queue{public:priority_queue() = default;  //因为下面写了个构造函数//所以要强制函数生成默认构造template<class InputIterator>priority_queue(InputIterator first, InputIterator last) //用迭代器构造堆的构造函数:_con(first, last){//向下调整建堆O(N), 向上调整算法(N*lnN)for (int i = (_con.size() - 1 - 1) / 2; i >= 0; i--){adjust_down(i);}}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);}const T& top(){return _con[0];}bool empty() const{return _con.empty();}size_t size() const{return _con.size();}private:void adjust_down(size_t parent) //核心,向下调整算法{size_t child = parent * 2 + 1;while (child < _con.size()){if (child + 1 < _con.size() - 1 && _con[child] < _con[child + 1]) child++;if (_con[parent] < _con[child]){swap(_con[parent], _con[child]);parent = child;child = parent * 2 + 1;}else{break;}}}void adjust_up(size_t child) //核心,向上调整算法{size_t parent = (child - 1) / 2;while (child > 0){if (_con[parent] < _con[child]){swap(_con[parent], _con[child]);child = parent;parent = (child - 1) / 2;}else{break;}}}private:container _con;  };

 测试来看一下:

void test02()
{vector<int> v = { 1, 2, 3,4, 5, 6 };stl::priority_queue<int> pq(v.begin(), v.end());while (!pq.empty()){cout << pq.top() << " ";pq.pop();}
}
int main()
{test02();return 0;
}

符合大根堆。

二、仿函数

什么是仿函数?

        仿函数(Functor)也被叫做函数对象(Function Object),它是一种行为类似函数的对象。要创建仿函数,只需定义一个类或结构体,并在其中重载operator()。当这个类的实例像函数那样被调用时,实际执行的就是重载后的operator()

看这段话可能不能很清楚,下面一步一步来引进仿函数。

正常实现一个比较小于的函数:

bool lessFunc(int x, int y)
{return x < y;
}int main()
{cout << lessFunc(1, 2);return 0;
}

用仿函数来实现一个比较小于的函数:

bool lessFunc(int x, int y)
{return x < y;
}//仿函数:
template<class T>
class Less
{
public:bool operator()(const T& x, const T& y){return x < y;}
};int main()
{cout << lessFunc(1, 2) << endl;Less<int> LessF;cout << LessF(1, 2) << endl;//仿函数的本质:cout << LessF.operator()(1, 2) << endl;return 0;
}

可以看到,仿函数是定义一个类或结构体,并在其中重载operator()。

仿函数的设计可以替代函数指针。

作用一:

用仿函数对冒泡排序进行改进一下:

// < 升序
// > 降序
template<class T, class Compare>
void BubbleSort(T* a, int n, Compare com)// com作为参数,需要传递一个对象过来
{int exchange = 0;for (int i = 0; i < n; i++){for (int j = 0; j < n - i - 1; j++){//if (a[j + 1] < a[j])if(com(a[j + 1], a[j])) //传函数来控制比较逻辑{exchange = 1;swap(a[j], a[j + 1]);}}if (exchange == 0){break;}}
}
//仿函数:
template<class T>
class Less
{
public:bool operator()(const T& x, const T& y){return x < y;}
};template<class T>
class Greater
{
public:bool operator()(const T& x, const T& y){return x > y;}
};

 实际应用:

int main()
{//cout << lessFunc(1, 2) << endl;//Less<int> LessF;//cout << LessF(1, 2) << endl;////仿函数的本质://cout << LessF.operator()(1, 2) << endl;int a[] = { 1 ,6, 1, 5, 4, 3, 10};BubbleSort(a, 7, Less<int>()); //控制升序for (auto& e : a){cout << e << " ";}cout << endl;BubbleSort(a, 7, Greater<int>()); //控制降序for (auto& e : a){cout << e << " ";}cout << endl;return 0;
}

使用仿函数控制priority_queue的大根堆和小根堆

(将看到函数指针的不行之处)

        要控制是大根堆还是小根堆,需要调整算法的比较逻辑。

仿函数:

//仿函数:
template<class T>
class Less
{
public:bool operator()(const T& x, const T& y){return x < y;}
};template<class T>
class Greater
{
public:bool operator()(const T& x, const T& y){return x > y;}
};

增加一个参数: 

namespace bit
{//增加一个类型参数:template<class T, class Container = vector<T>, class Compare = Less<T>>class priority_queue{private:Container _con;    Compare com;  //传比较函数};
}
namespace bit
{template<class T, class Container = vector<T>, class Compare = Less<T>>class priority_queue{public://函数。。。private:void adjust_up(int child){Compare com;int parent = (child - 1) / 2;while (child > 0){//if (_con[child] > _con[parent])//if (_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;size_t child = parent * 2 + 1;while (child < _con.size()){// 选出大的那个孩子if (child + 1 < _con.size() && com(_con[child], _con[child + 1]))++child;if (com(_con[parent], _con[child])){swap(_con[child], _con[parent]);parent = child;child = parent * 2 + 1;}else{break;}}}private:Container _con;};
}

核心算法:

template<class T, class container = vector<T>, class Compare = Less<T> > //模拟是小根堆class priority_queue{public://函数private:		void adjust_down(size_t parent) //核心,向下调整算法{size_t child = parent * 2 + 1;while (child < _con.size()){if (child + 1 < _con.size() - 1 && com(_con[parent], _con[child])) child++;//if (_con[parent] < _con[child])if (com(_con[parent], _con[child])){swap(_con[parent], _con[child]);parent = child;child = parent * 2 + 1;}else{break;}}}void adjust_up(size_t child) //核心,向上调整算法{size_t parent = (child - 1) / 2;while (child > 0){//if (_con[parent] < _con[child])if(com(_con[parent], _con[child])){swap(_con[parent], _con[child]);child = parent;parent = (child - 1) / 2;}else{break;}}}private:container _con;  Compare com;};

来试一下,建一个小根堆:

int main()
{//bit::priority_queue<int> pq;stl::priority_queue<int, vector<int>, Greater<int>> pq;pq.push(4);pq.push(1);pq.push(6);pq.push(9);pq.push(4);while (!pq.empty()){cout << pq.top() << " ";pq.pop();}cout << endl;return 0;
}

这里priority_queue传的是类型,Greater<int> 是一个类型。这就是仿函数的作用。

作用二:

来看代码:

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);
private:int _year;int _month;int _day;
};ostream& operator<<(ostream& _cout, const Date& d)
{_cout << d._year << "-" << d._month << "-" << d._day;return _cout;
}int main()
{stl::priority_queue<Date*, vector<Date*>> q1;q1.push(new Date(2018, 10, 29));q1.push(new Date(2018, 10, 28));q1.push(new Date(2018, 10, 30));while (!q1.empty()){cout << *q1.top() << " ";q1.pop();}cout << endl;return 0;
}

        这段代码想要对日期进行排序,但是堆里面存的是Data*,不能按照日期进行排序。

这时,可以通过仿函数来改变比较逻辑。

struct PDataLess
{bool operator()(const Date* p1, const Date* p2){return *p1 < *p2;}
};
int main()
{stl::priority_queue<Date*, vector<Date*>, PDataLess> q1;q1.push(new Date(2018, 10, 29));q1.push(new Date(2018, 10, 28));q1.push(new Date(2018, 10, 30));while (!q1.empty()){cout << *q1.top() << " ";q1.pop();}cout << endl;return 0;
}

等等等等

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

相关文章:

  • 游戏盾从哪些方面保护网站业务?
  • GTSuite许可证性能优化建议
  • 第4章唯一ID生成器——4.4 基于数据库的自增主键的趋势递增的唯一ID
  • 前缀和-974.和可被k整除的子数组-力扣(LeetCode)
  • 实现视频实时马赛克
  • OpenShift AI - 将 Python 库安装到 Workbench 共享存储中
  • 【跨国数仓迁移最佳实践3】资源消耗减少50%!解析跨国数仓迁移至MaxCompute背后的性能优化技术
  • 深度学习篇---PaddleDetection模型选择
  • 《HCIA-Datacom 认证》希赛三色笔记:Vlan间三层通信过程解析
  • 用LangGraph实现聊天机器人记忆功能的深度解析
  • JVM知识点(1)
  • 通过管理工具(hgdbdeveloper)新建用户无法授权
  • 子数组和 问题汇总
  • AI应用:电路板设计
  • C++ 模板类型 <T>,对函数参数传递兼容性检查
  • 【Linux系统编程】Ext2文件系统
  • 001 Configuration结构体构造
  • 【C++篇】“内存泄露”的宝藏手段:智能指针
  • OpenCV 学习探秘之三:从图像读取到特征识别,再到机器学习等函数接口的全面实战应用与解析
  • Excel批量加密工具,一键保护多个文件
  • 【图像处理基石】如何对遥感图像进行实例分割?
  • 【RAG搭建Agent应用实战】基于检索增强生成(RAG)搭建特定场景Agent应用
  • Spring Boot 防重放攻击全面指南:原理、方案与最佳实践
  • AI产品经理手册(Ch3-5)AI Product Manager‘s Handbook学习笔记
  • 【Linux基础】find在linux中查找文件
  • Jenkins 详解
  • 准大一GIS专业新生,如何挑选电脑?
  • 【Kotlin】const 修饰的编译期常量
  • 医疗超声成像专用AFE模拟前端
  • 【CSS】盒子类型