STL之优先级队列(堆)的模拟实现与仿函数(8千字长文详解!)
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> #include<iostream> #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,vector<int>,greater<int>> 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 {template<class T,class Container = std::vector<T>>class priority_queue{private:Container _con;}; }
构造函数
namespace MySTL
{template<class T,class Container = std::vector<T>>class priority_queue{public:priority_queue(){}template<class InputIterator>priority_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
{template<class T,class Container = std::vector<T>>class 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 {template<class T,class Container = std::vector<T>>class priority_queue{public:void push(const T& value){//插入后要保持堆的结构!//要进行向上调整!_con.push_back(value);adjust_up(_con.size()-1);}private:Container _con;}; }
向上调整——正常实现
namespace MySTL
{template<class T,class Container = std::vector<T>>class 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;};
}
//这里如果使用parent>0,那么因为子节点还在下一个位置,则无法调整根节点
//如果是parent>=0 因为parent =(0-1)/2 仍然为0,则就会出现死循环!
向上调整,就是将现在子节点和父节点对比一下!然后如果是建大堆,若子节点大于父节点那么进行交换!
如果是建小堆,若子节点小于父节点,那么也交换!
结束条件:直到根节点是孩子,或者建大堆的时候,孩子比父亲小(建小堆的时候,孩子比父亲大!)
向上调整算最好使用孩子作为循环借宿的条件!
pop
namespace MySTL {template<class T,class Container = std::vector<T>>class 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; }
仿函数/函数对象
仿函数的概念
首先仿函数究竟是什么呢?——是一个类!仿函数的类型对象我们叫做函数对象!
template<class T> struct less {bool operator()(const T& x,const T& y){return x < y;} }; template<class T> struct greater {bool operator()(const T& x,const T& y){return x > y;} };
仿函数的要求是要重载(),()这也是一个运算符!例如像是我们去调用pq1.pop()的函数,后面的()就是一个函数调用符!
int main() {less<int> 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.;j<size-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.;j<size-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() {less<int> 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]), less<int>());//怎么写也是可以的!因为是一个匿名对象!如果这个函数对象是要使用一次的话!可以直接使用!匿名对象!for (auto& e : a){std::cout << e << " ";}std::cout << std::endl;greater<int> 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]), greater<int>());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.;j<size-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;}} }
template<class T> struct less {bool operator()(const T& x,const T& y)const//加上const让const对象能够调用!{return x < y;} }; template<class T> struct greater {bool operator()(const T& x,const T& y)const{return x > y;} };
我们也可以看到传引用其实很麻烦!所以没有什么必要!
使用仿函数来实现优先级队列的逻辑判断
我们上面的优先级队列要么只能是大堆!要么只能是小堆!不够泛用!原因就是因为我们的逻辑是写死的!但是学会了仿函数之后!我们也可以实现逻辑泛型了!
新增模板参数
template<class T, class Container = std::vector<T>,class Compare = less<T>> class priority_queue{public:// functionsprivate:Container _con;}; }
加上第三个模板参数仿函数!
使用仿函数实现泛型的向下调整算法与向上调整算法
namespace MySTL {template<class T>struct less{bool operator()(const T& x, const T& y){return x < y;}};template<class T>struct greater{bool operator()(const T& x, const T& y){return x > y;}};template<class T, class Container = std::vector<T>,class Compare = less<T>>class 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, vector<int>, greater<int>> 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
{template<class T>struct less{bool operator()(const T& x, const T& y){return x < y;}};template<class T>struct greater{bool operator()(const T& x, const T& y){return x > y;}};template<class T, class Container = std::vector<T>,class Compare = less<T>>class priority_queue{public:priority_queue()//无参构造{}template<class InputIterator>priority_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_queue<Date> q1;//默认是大堆q1.push(Date(2010, 1, 1));q1.push(Date(2010, 1, 2));q1.push(Date(2010, 1, 3));cout << q1.top() << endl;priority_queue<Date,vector<Date>,greater<Date>> 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_queue<Date*> 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_queue<Date*,vector<Date*>,greater<Date*>> 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_queue<Date*> q3;priority_queue<Date*,vector<Date*>,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_queue<Date*,vector<Date*>,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 找到第一个不按堆规则的元素
相关文章:

STL之优先级队列(堆)的模拟实现与仿函数(8千字长文详解!)
STL之优先级队列(堆)的模拟实现与仿函数 文章目录 STL之优先级队列(堆)的模拟实现与仿函数优先级队列的概念priority_queue的接口介绍优先级队列的构造函数 priority_queue模拟实现类成员构造函数向下调整算法——正常实现 push向…...

设施管理系统
随着经济的快速发展,各种基础设施都在更新,在企事业单位中各种设施也都难以管理,以往传统的管理模式已经无法适应现代社会的需求,设备管理的滞后反而会影响设施设备的使用效果,因此设施设备管理系统必不可少。那么什么…...

JavaScript:获取当前日期、星期、时间 | Data对象
文章目录 1 Date对象2 代码示例3 获取 yyyy-MM-dd 格式的日期 1 Date对象 JavaScript 中的 Date 对象表示日期和时间。Date 对象基于自 1970 年 1 月 1 日 00:00:00 UTC(协调世界时)以来的毫秒数。以下是 Date 对象的一些常用方法和属性。 getFullYear…...

Cadence原理图快速查找元器件的方法
1.Cadence原理图快速查找元器件的方法 ①在红框中输入元器件编号,点击望远镜的图标在底下的状态栏可看到查找到的相关元器件,点击元器件可自动定位当前元器件的位置。 ②点击hierarchy(层)可自主查找,找到后点击序号即…...

科目二 调整座椅
靠背倾角 座椅高低 座椅前后用手抬起座椅前的横杠,让座椅向后移动方便上车 靠背左侧,向后扳扳杠调整倾角 座椅左侧,上下扳动调整高低头顶距车顶有一拳的距离 座椅前横杠一只手提起横杠另一只手握住方向盘前拉、后推调整到合适位置,…...

02.加载GDT表,进入保护模式
加载GDT表,进入保护模式 加载GDT表,实现操作系统从实模式进入保护模式 参考 操作系统学习 — 启动操作系统:进入保护模式 保护模式与实模式 GDT、GDTR、LDT、LDTR 调用门与特权级 趣谈 Linux 操作系统 在01.硬盘启动盘,加载操作系…...

MySQL(进阶篇3.0)
锁 锁是计算机协调多个进程或线程并发访问某一资源的机制。在数据库中,除传统的计算机资源(CPU、RAM、I/O)的争用之外,数据也是一种供许多用户共享的资源。如何保证数据并发访问的一致性、有效性是所有数据库必须解决的一个问题&…...

2023.6.8小记——嵌入式系统初识、什么是ARM架构?
今天还挺充实的,早上在图书馆本来想学一下notion,结果看李沐老师的动手深度学习看到十点半,在电脑上配置了李沐老师的d2l和jupyter,等后续有时间的时候再继续学。 下午看了一下notion的使用方法,这玩意初学者用起来是…...

分布式运用之ELK企业级日志分析系统
1.ELK的相关知识 1.1 ELK的概念与组件 ELK平台是一套完整的日志集中处理解决方案,将 ElasticSearch、Logstash 和 Kiabana 三个开源工具配合使用, 完成更强大的用户对日志的查询、排序、统计需求。 ElasticSearch: 是基于Lucene(…...

【华为OD机试真题 C语言】8、停车场车辆统计 | 机试真题+思路参考+代码解析
文章目录 一、题目🎃题目描述🎃输入输出🎃样例1🎃样例2 二、思路参考三、代码参考🏆C语言 作者:KJ.JK 🍂个人博客首页: KJ.JK 🍂专栏介绍: 华为OD机试真题汇…...

c++ MES 对接(XML、JSON、SOAP)
🗑️ 清空 //MES系统对接可以使用多种协议,包括XML、JSON和SOAP等。 //以下是使用C语言进行MES系统对接的示例代码: //1. XML协议对接: //c #include <iostream> #include <string> #include <vector> #incl…...

idea导入java web项目带jar
可参考:idea导入Javaweb项目_小黑cc的博客-CSDN博客 配置tomcat 加载项目jar依赖 最后点ok,tomcat启动 jsp页面的项目,必须要加载这两个jar包...

【第55天|● 392.判断子序列 ● 115.不同的子序列 】
392.判断子序列 class Solution { public:bool isSubsequence(string s, string t) {if(s.size()0)return true;if(t.size()0) return false;vector<bool> dp(t.size()1, true);for(int i0; i<s.size(); i){for(int jt.size(); j>0; j--){if(s[i]t[j-1]&&…...

Dockerfile创建镜像
一、Docker镜像的创建 创建镜像有三种方法,分别为【基于已有镜像创建】、【基于本地模板创建】以及【基于Dockerfile创建】。 1.1 基于现有镜像创建 (1)首先启动一个镜像,在容器里做修改docker run -it centos:7 /bin/bash …...

基于 opencv 的人脸识别上课考勤系统,附源码,可作为毕业设计
一、简介 这个人脸识别考勤签到系统是基于大佬的人脸识别陌生人报警系统二次开发的。 项目使用Python实现,基于OpenCV框架进行人脸识别和摄像头硬件调用,同时也用OpenCV工具包处理图片。交互界面使用pyqt5实现。 该系统实现了从学生信息输入、人脸数据…...

.editorconfig 配置
有人会问:既然项目已经使用了 eslint 和 prettier,为什么还需要 EditorConfig? 为什么需要 EditorConfig? .editorconfig 是一个用于定义和维护跨不同编辑器和开发环境的一致编码样式的文件。它可以确保整个团队在使用不同编辑器…...

Spring 高级依赖注入 —— Bean的延迟依赖查找功能,ObjectFactory 和 ObjectProvider
介绍 首先明确一下什么是延迟查找,一般来说通过Autowired注解注入一个具体对象的方式是属于实时依赖查找,注入的前提是要保证对象已经被创建。而使用延迟查找的方式是我可以不注入对象的本身,而是通过注入一个代理对象,在需要用到…...

VSCode--Config
1. basic 1.1 调整字体 1.2 调整 remote login 输入框都在 TERMINAL 中实现 1.3 界面设置成中文 安装插件: 然后配置即可。 2.Linux 2.1 Install 2.1.1 offline Install vscode server 问题描述 内网开发,vscode 自身通过代理安装完 remote 插件后…...

代码随想录刷题第48天|LeetCode198打家劫舍、LeetCode213打家劫舍II、LeetCode337打家劫舍III
1、LeetCode198打家劫舍 题目链接:198、打家劫舍 1、dp[i]:考虑下标i(包括i)以内的房屋,最多可以偷窃的金额为dp[i]。 2、递推公式: 如果偷第i房间,那么dp[i] dp[i - 2] nums[i] …...

C# NTS 获取MuliiLineString中的所有线
/// <summary>/// 获取多段线的所有线/// </summary>/// <param name"ml"></param>/// <returns></returns>public static List<LineString> GetLineStrings(this MultiLineString ml){List<LineString> lineString…...

CodeWhisperer插件使用体验
官方教程点击跳转 使用工具 1.vscode 2.插件(AWS Toolkit),免费使用 安装以后如何使用 1.首先要有一个aws账号 2.插件下载好以后登录aws账号,我们主要用这款插件的CodeWhisperer这个功能,其它的自行看官方教程了解。 注意事项:我们在从vs…...

机器学习笔记 - 多实例学习(MIL)弱监督学习
一、多实例学习概述 多实例学习(MIL)是一种弱监督学习形式,其中训练实例被排列在称为袋的集合中,并为整个袋提供标签。这种方式越来越受到人们的关注,因为它自然适合各种问题,并允许利用弱标记数据。因此,它被应用于计算机视觉和文档分类等不同的应用领域。 多实例学习(…...

SQL Server 2008 定时自动备份和自动删除方法
SQL Server 2008 数据定时自动备份和自动删除方法,同一个计划兼备数据备份数数据删除的操作方法 工具/原料 SQL Server 2008 方法/步骤 1、 点击实例名下的【管理】-【维护计划】-点击鼠标右键,点击【维护计划向导】,填写计划名称&…...

代码生成器实现
代码生成器实现 实现封装元数据的工具类实现代码生成器的代码编写掌握模板创建的 构造数据模型 需求分析 借助Freemarker机制可以方便的根据模板生成文件,同时也是组成代码生成器的核心部分。对于Freemarker而 言,其强调 数据模型 模板 文件 的思…...

【Python基础】Python函数(基本函数)
文章目录 Python函数1、函数多返回值2、函数多种传参方式(1)位置参数(2)关键字参数(3)缺省参数(4)不定长参数位置传递关键字传递 3、小结 Python函数 1、函数多返回值 Q:如果一个函数要有多个返回值,该如何书写代码? # 使用多个变量&#…...

Vue3 + TS + Vite —— 大屏可视化 项目实战
前期回顾 Vue3 Ts Vite pnpm 项目中集成 —— eslint 、prettier、stylelint、husky、commitizen_彩色之外的博客-CSDN博客搭建VIte Ts Vue3项目并集成eslint 、prettier、stylelint、huskyhttps://blog.csdn.net/m0_57904695/article/details/129950163?spm1001.2014…...

EasyExcel 批量导入并校验数据
文章目录 前言一、pom二、使用步骤1.导入对象2.读入数据并保存 前言 EasyExcel 批量导入并校验数据 一、pom <dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>2.2.7</version></depend…...

亚马逊、Allegro卖家建立属于自己的测评系统,实现批量优质账号养成
卖家搭建一套完整的测评系统,卖家自己能够养出批量优质账号,并完全掌控真实买家的浏览、加购、下单和评价等风控数据规律。我们的系统能够自主加速推广,防御反击,同时节省运营成本,实现高效的测评运营。 我们的系统支…...

springboot的目录结构作用
springboot单体项目结构大概如下。 代码都在src/main下, java是后端代码 java下最基本的包 dao(mapper) entity(model) service controller 其他的包根据项目需求扩展。 resources下是配置文件。 如果不是前后端分离,resources下放的是静态文件…...

微信小程序基础使用-请求数据并渲染
小程序基本使用-请求数据并渲染 小程序模板语法-数据绑定 在js中定义数据 Page({data: {isOpen: true,message: hello world!} })小程序的data是一个对象,不同于vue的data是一个函数 在模块中获取使用数据 小程序中使用 {{}} 实现数据与模板的绑定 内容绑定&a…...