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

68Design一样设计网站福鼎网站建设培训

68Design一样设计网站,福鼎网站建设培训,企业老板培训课程,深圳设计工作室有哪些STL之优先级队列#xff08;堆#xff09;的模拟实现与仿函数 文章目录 STL之优先级队列#xff08;堆#xff09;的模拟实现与仿函数优先级队列的概念priority_queue的接口介绍优先级队列的构造函数 priority_queue模拟实现类成员构造函数向下调整算法——正常实现 push向…STL之优先级队列堆的模拟实现与仿函数 文章目录 STL之优先级队列堆的模拟实现与仿函数优先级队列的概念priority_queue的接口介绍优先级队列的构造函数 priority_queue模拟实现类成员构造函数向下调整算法——正常实现 push向上调整——正常实现 pop 仿函数/函数对象仿函数的概念仿函数的作用 使用仿函数来实现优先级队列的逻辑判断新增模板参数使用仿函数实现泛型的向下调整算法与向上调整算法 优先级队列模拟——最终版代码仿函数的应用STL中实现的比较STL中的建堆算法 优先级队列的概念 优先队列是一种容器适配器根据严格的弱排序标准它的第一个元素总是它所包含的元素中最大的。 本质就是一个堆 此上下文类似于堆在堆中可以随时插入元素并且只能检索最大堆元素(优先队列中位于顶部的元素)。 . 优先队列被实现为容器适配器容器适配器即将特定容器类封装作为其底层容器类queue提供一组特定的成员函数来访问其元素。元素从特定容器的“尾部”弹出其称为优先队列的顶部。 底层容器可以是任何标准容器类模板也可以是其他特定设计的容器类。容器应该可以通过随机访问迭代器访问并支持以下操作 empty()检测容器是否为空size()返回容器中有效元素个数front()返回容器中第一个元素的引用push_back()在容器尾部插入元素pop_back()删除容器尾部元素 标准容器类vector和deque满足这些需求。默认情况下如果没有为特定的priority_queue类实例化指定容器类则使用vector。 需要支持随机访问迭代器以便始终在内部保持堆结构。容器适配器通过在需要时自动调用算法函数make_heap、push_heap和pop_heap来自动完成此操作 list不支持随机访问所以不能当做其标准容器类 priority_queue的接口介绍 pop删除堆顶的数据 top取出堆顶的数据 push向堆插入数据 size返回堆里面的元素个数 empty判断堆是否为空 swap交换两个堆 优先级队列的构造函数 可以使用迭代器进行初始化 但是这个迭代器是一个随机迭代器 //简单使用 #pragma once #include queue #includeiostream #include functional//less和greater的头文件 using namespace std; int main() {//大堆priority_queue int pq;pq.push(3);pq.push(1);pq.push(2);pq.push(5);while (!pq.empty()){cout pq.top() ;pq.pop();}cout endl;//小堆priority_queue int,vectorint,greaterint pq1;pq1.push(3);pq1.push(1);pq1.push(2);pq1.push(5);while (!pq1.empty()){cout pq1.top() ;pq1.pop();}cout endl;return 0; } priority_queue模拟实现 容器适配器是不需要迭代器的如果有了迭代器是不能保证最大/最小的先出 类成员 namespace MySTL {templateclass T,class Container std::vectorTclass priority_queue{private:Container _con;}; }构造函数 namespace MySTL {templateclass T,class Container std::vectorTclass priority_queue{public:priority_queue(){}templateclass InputIteratorpriority_queue(InputIterator first,InputIterator last):_con(first,last)//直接调用vector的构造{//此时还不是堆//向下调整建堆for (int i (_con.size() - 2) / 2; i 0; i){adjust_down(i);}}private:Container _con;}; } 向下调整算法——正常实现 namespace MySTL {templateclass T,class Container std::vectorTclass priority_queue{private:void adjust_down(size_t 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])//建大堆{std::swap(_con[parent], _con[child]);parent child;child parent * 2 1;}else{break;//没有更大的了退出}}}private:Container _con;}; } 向下调整算法的核心将父节点和自己的两个子节点对比找到其中一个最大大堆的或者最小小堆的子节点然后比较如果是建大堆若父节点比子节点还要小那么交换如果是建小堆若父节点比子节点还要大那么交换直到父节点比最大子节点还要大大堆或者是比最小的子节点还要小小堆则跳出循环或者直接调整到最后一个节点 push namespace MySTL {templateclass T,class Container std::vectorTclass priority_queue{public:void push(const T value){//插入后要保持堆的结构//要进行向上调整_con.push_back(value);adjust_up(_con.size()-1);}private:Container _con;}; } 向上调整——正常实现 namespace MySTL {templateclass T,class Container std::vectorTclass priority_queue{private:void adjust_up(size_t child){size_t parent (child - 1) / 2;while (child 0){if (_con[child] _con[parent])//建大堆{std::swap(_con[child], _con[parent]);child parent;parent (child - 1) / 2;}else{break;}}}private:Container _con;}; } //这里如果使用parent0那么因为子节点还在下一个位置则无法调整根节点 //如果是parent0 因为parent 0-1/2 仍然为0则就会出现死循环向上调整就是将现在子节点和父节点对比一下然后如果是建大堆若子节点大于父节点那么进行交换 如果是建小堆若子节点小于父节点那么也交换 结束条件直到根节点是孩子或者建大堆的时候孩子比父亲小建小堆的时候孩子比父亲大 向上调整算最好使用孩子作为循环借宿的条件 pop namespace MySTL {templateclass T,class Container std::vectorTclass priority_queue{public:void pop(){//交换std::swap(_con[0], _con[_con.size()-1]);//删除最后一个元素_con.pop_back();//向下调整算法adjust_down(0);}private:void adjust_down(size_t 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])//建大堆{std::swap(_con[parent], _con[child]);parent child;child parent * 2 1;}else{break;//没有更大的了退出}}}private:Container _con;}; } int main() {MySTL::priority_queue int pq;pq.push(3);pq.push(1);pq.push(2);pq.push(5);while (!pq.empty()){cout pq.top() ;pq.pop();}cout endl;return 0; } 仿函数/函数对象 仿函数的概念 首先仿函数究竟是什么呢——是一个类仿函数的类型对象我们叫做函数对象 templateclass T struct less {bool operator()(const T x,const T y){return x y;} }; templateclass T struct greater {bool operator()(const T x,const T y){return x y;} };仿函数的要求是要重载()()这也是一个运算符例如像是我们去调用pq1.pop()的函数后面的()就是一个函数调用符 int main() {lessint lessFunc;lessFunc(0, 1);return 0; }如果不看上面的类的定义单看后面的lessFunc(0,1)这就是一个函数调用函数名是lessFunc——但是现在实际上是一个类这就是为什么这个叫做仿函数这个对象叫做函数对象指的就是这个对象能像函数一样被使用 但是这个其实也是一个函数调用 lessFunc(0, 1);//将这个转换为一个运算符重载 lessFunc.operator()(0, 1);//等价于上面上面的仿函数的作用 那么仿函数究竟有什么作用呢看起来和一般的函数区别而且还多了一个封装看起来更加的麻烦了 我们可以举一个例子来看 //这是C语言的一个冒泡排序 void BubbleSort(int* a, int size,bool(*cmp)(int,int)) {for (int i 0; i size; i){int exchange 0;for(int j 1.;jsize-i;j){if (cmp(a[j-1],a[j])){std::swap(a[j - 1], a[j]);exchange 1;}}if (!exchange){break;}} } 我们可以 看到在C语言中如果我们想要解决升序降序的问题我们就要传一个函数指针过来——如果不用函数指针那么我们就只能重新写一个函数了 那么在C中的类里面我们是否也可以传一个函数指针呢可以但是这样子不好看C的设计都是在尽量的不去使用指针 所以仿函数这时候就派上用场了 template class T,class Compare void BubbleSort(T* a, int size,Compare com)//因为Compare其实是一个类所以要显示实例化而不是模板函数一样是推演实例化所以要多加一个参数 {for (int i 0; i size; i){int exchange 0;for(int j 1.;jsize-i;j){//if (a[j] a[j - 1])if (com(a[j], a[j - 1])){std::swap(a[j - 1], a[j]);exchange 1;}}if (!exchange){break;}} }int main() {lessint lessFunc;int a[] { 1,5,7,8,94,24,5,3,56,7 ,90 };BubbleSort(a, sizeof(a) / sizeof(a[0]), lessFunc);//升序排序//BubbleSort(a, sizeof(a) / sizeof(a[0]), lessint());//怎么写也是可以的因为是一个匿名对象如果这个函数对象是要使用一次的话可以直接使用匿名对象for (auto e : a){std::cout e ;}std::cout std::endl;greaterint greaterFunc;int b[] { 1,5,7,8,94,24,5,3,56,7 ,90 };BubbleSort(b, sizeof(b) / sizeof(b[0]), greaterFunc);//降序排序//BubbleSort(b, sizeof(b) / sizeof(b[0]), greaterint());for (auto e : b){std::cout e ;}return 0; } less和greater就是我们刚刚写的仿函数 从我们实现代码的角度来说这是一个泛型是一个逻辑泛型——即com是一个逻辑如果需要升序就传一个lessFunc如果要降序就传一个greaterFunc 类型泛型就是不限类型逻辑泛型就是不限逻辑 仿函数一般都是传值因为仿函数的拷贝代价很小仿函数里面没有成员变量所以只有一个字节作为标记位 但是如果想要传引用也行 void BubbleSort(T* a, int size,const Compare com)//也是可以传引用的但是要加上const否则就无法接收匿名对象因为匿名对象具有常性 {for (int i 0; i size; i){int exchange 0;for(int j 1.;jsize-i;j){//if (a[j] a[j - 1])if (com(a[j], a[j - 1]))//因为是const对象调用所以仿函数内部都要加上const{std::swap(a[j - 1], a[j]);exchange 1;}}if (!exchange){break;}} }templateclass T struct less {bool operator()(const T x,const T y)const//加上const让const对象能够调用{return x y;} }; templateclass T struct greater {bool operator()(const T x,const T y)const{return x y;} };我们也可以看到传引用其实很麻烦所以没有什么必要 使用仿函数来实现优先级队列的逻辑判断 我们上面的优先级队列要么只能是大堆要么只能是小堆不够泛用原因就是因为我们的逻辑是写死的但是学会了仿函数之后我们也可以实现逻辑泛型了 新增模板参数 templateclass T, class Container std::vectorT,class Compare lessT class priority_queue{public:// functionsprivate:Container _con;}; } 加上第三个模板参数仿函数 使用仿函数实现泛型的向下调整算法与向上调整算法 namespace MySTL {templateclass Tstruct less{bool operator()(const T x, const T y){return x y;}};templateclass Tstruct greater{bool operator()(const T x, const T y){return x y;}};templateclass T, class Container std::vectorT,class Compare lessTclass priority_queue{public://functionsprivate:void adjust_up(size_t child){size_t parent (child - 1) / 2;Compare com;while (child 0){//if (_con[child] _con[parent])//建大堆//if (_con[parent] _con[child])//建大堆if(com(_con[parent], _con[child]))//我们要与库保持一致要小于实现大堆那么就要parent在前就是与上面的逻辑一样{std::swap(_con[child], _con[parent]);child parent;parent (child - 1) / 2;}else{break;}}}void adjust_down(size_t parent){size_t child parent*2 1;Compare com;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在前child;//if (_con[parent] _con[child])//建大堆if(com(_con[parent], _con[child])){std::swap(_con[parent], _con[child]);parent child;child parent * 2 1;}else{break;//没有更大的了退出}}}private:Container _con;}; }测试用例 int main() {//大堆MySTL::priority_queue int pq;pq.push(3);pq.push(1);pq.push(2);pq.push(5);while (!pq.empty()){cout pq.top() ;pq.pop();}cout endl;//小堆MySTL::priority_queue int, vectorint, greaterint pq1;pq1.push(3);pq1.push(1);pq1.push(2);pq1.push(5);while (!pq1.empty()){cout pq1.top() ;pq1.pop();}cout endl;return 0; }优先级队列模拟——最终版代码 namespace MySTL {templateclass Tstruct less{bool operator()(const T x, const T y){return x y;}};templateclass Tstruct greater{bool operator()(const T x, const T y){return x y;}};templateclass T, class Container std::vectorT,class Compare lessTclass priority_queue{public:priority_queue()//无参构造{}templateclass InputIteratorpriority_queue(InputIterator first, InputIterator last):_con(first, last)//直接调用vector的构造{//此时还不是堆//向下调整建堆for (int i (_con.size() - 2) / 2; i 0; i){adjust_down(i);}}void push(const T value){_con.push_back(value);adjust_up(_con.size()-1);}void pop(){//交换std::swap(_con[0], _con[_con.size()-1]);//删除最后一个元素_con.pop_back();//向下调整算法adjust_down(0);}const T top() const{return _con[0];}bool empty() const{return _con.empty();}size_t size()const{return _con.size();}private:void adjust_up(size_t child){size_t parent (child - 1) / 2;Compare com;while (child 0){//if (_con[child] _con[parent])//建大堆//if (_con[parent] _con[child])//建大堆if(com(_con[parent], _con[child]))//我们要与库保持一致要小于实现大堆那么就要parent在前{std::swap(_con[child], _con[parent]);child parent;parent (child - 1) / 2;}else{break;}}}void adjust_down(size_t parent){size_t child parent*2 1;Compare com;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在前child;//if (_con[parent] _con[child])//建大堆if(com(_con[parent], _con[child])){std::swap(_con[parent], _con[child]);parent child;child parent * 2 1;}else{break;//没有更大的了退出}}}private:Container _con;}; } 仿函数的应用 要知道我们优先级队列的类型可以不只是库里面的类型还可以是自定义类型 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){_cout d._year - d._month - d._day;return _cout;}private:int _year;int _month;int _day; };如果一个自定义类型是支持比较大小的那么仿函数greater和less都是可以使用的里面的与 本质也是调用的是Date类的运算符重载 void testpriorityqueue() {priority_queueDate q1;//默认是大堆q1.push(Date(2010, 1, 1));q1.push(Date(2010, 1, 2));q1.push(Date(2010, 1, 3));cout q1.top() endl;priority_queueDate,vectorDate,greaterDate q2;//默认是大堆q2.push(Date(2010, 1, 1));q2.push(Date(2010, 1, 2));q2.push(Date(2010, 1, 3));cout q2.top() endl;} int main() {testpriorityqueue();return 0; } 现在如果我们传入的是这个自定义类型的指针呢 void testpriorityqueue() {//大堆priority_queueDate* q3;q3.push(new Date(2010, 1, 1));q3.push(new Date(2010, 1, 2));q3.push(new Date(2010, 1, 3)); cout *q3.top() endl;//小堆priority_queueDate*,vectorDate*,greaterDate* q4;/q4.push(new Date(2010, 1, 1));q4.push(new Date(2010, 1, 2));q4.push(new Date(2010, 1, 3));cout *q4.top() endl; } int main() {testpriorityqueue();return 0; }我们发现这时候堆的比较好像就失效了!这是为什么呢因为我们传过去的是date类的地址此时我们传入的仿函数是用地址的大小去比较的谁地址谁大谁地址小谁小 因为每次地址是随机的所以每次的结果都会在改变 那么现在的结果已经不符合我们预期了怎么办 我们就可以重新写一个关Date* 的仿函数 struct PDateCompare_greater {bool operator()(const Date* x,const Date* y){return *x *y;} }; struct PDateCompare_less {bool operator()(const Date* x,const Date* y){return *x *y;} }; void testpriorityqueue() {//大堆//priority_queueDate* q3;priority_queueDate*,vectorDate*,PDateCompare_less q3;q3.push(new Date(2010, 1, 1));q3.push(new Date(2010, 1, 2));q3.push(new Date(2010, 1, 3)); cout *q3.top() endl;//小堆priority_queueDate*,vectorDate*,PDateCompare_greater q4;q4.push(new Date(2010, 1, 1));q4.push(new Date(2010, 1, 2));q4.push(new Date(2010, 1, 3));cout *q4.top() endl; } int main() {testpriorityqueue();return 0; }如果比较的方式不是我们想要的那么我们就可以通过自己写一个仿函数来完成甚至如果自定义类不支持 与 重载我们也可以写一个仿函数来完成大小的比较 STL中实现的比较 STL中的建堆算法 STL中的建堆算法是在algorithm这个头文件中 push_heap将一个元素插入堆中pop_heap将一个元素从堆中移除make_heap 从一个范围内的元素中创建一个堆sort_heap 堆排序if_heap 判定一个范围内的元素是不是堆is_heap_until 找到第一个不按堆规则的元素
http://www.w-s-a.com/news/283537/

相关文章:

  • 网站建设辶金手指排名十二wordpress 当数据库
  • 无锡手机网站建设服务苏州展厅设计企业
  • 无锡网站制作需要多少钱北京二次感染最新消息
  • 网站开发视频播放无画面杭州房产信息网官网
  • 网站开发 改进如何创建公众号平台
  • wordpress网站响应很慢只有asp网站代码可以重新编译吗
  • 哪个网站教做饭做的好wordpress热点文章
  • 可以做推广东西的网站重庆网站建设 重庆网站制作
  • 珠海网站建设培训学校wordpress去版权 合法
  • 建设食品商购网站学校网站设计实验报告
  • 建个网站多少钱沭阳奥体小区做网站的
  • 广州视频网站建站公司php网页设计作业代码
  • 成都公司网站设计如何制作网址最简单的方法
  • 温州 做网站福建住房城乡建设部网站
  • 网站自动化采集成都网站设计费用
  • 广东专业网站定制建设淘宝网站的人员组织结构
  • 网站改版seo无锡有多少家公司
  • h5美食制作网站模板下载wordpress大学百度云
  • 零陵做网站建立网站的公司平台
  • 某企业电子商务网站建设网站开发实验结论
  • 自己做的网站突然打不开杭州哪些做网站公司好
  • 株洲专业建设网站免费cms内容管理系统
  • 网上建立网站赚钱网站建设方案书纯文字
  • 专业网站设计哪家好it外包合同模板
  • 个人网站备案都需要什么中小企业服务网
  • 佛山网站建设哪个在公司网站投简历该怎么做
  • 八戒网站做推广老域名全部失效请拿笔记好
  • iss服务器网站建设甘肃建设厅网站执业注册中心
  • 域名访问网站 过程网站 免费 托管运营
  • 下单的网站建设教程wordpress php7.1