C++之打造my vector篇

news/2024/9/18 15:07:30/ 标签: c++, 学习

目录

前言

1.参照官版,打造vector的基本框架

2.丰富框架,实现接口方法

基本的迭代器实现

数据的[]访问

容量和数据空间的改变

vector空间大小的返回与判空

数据的增删

数据打印

拷贝构造和赋值重载

3.扩展延伸,深度理解代码

迭代器失效问题

使用memcpy的拷贝问题

结束语


前言

前面章节我们讲解了vector相关接口,方法的使用,本节内容我们将自己创造vector,模仿官方的接口方法。

1.参照官版,打造vector的基本框架

通过查看官方文档我们知道,vector是个可以变化的数组,是个容器,可以储存一系列数据,

是典型的模版类。

且有三个基本成员start,finish,end_of_storage,我们可以理解为指向数组的开端,数据的结尾,以及容量的结束指针。

上图为插入成员后的分布情况。

故创造了一下的基本框架,因为是我们自己的实现,所以定义了一个my_vector的命名空间,

namespace my_vector {template<class T>class vector {public:typedef T* iterator;typedef const T* const_iterator;vector() {}~vector() {if (_start) {delete[]_start;_start = _finish = _end_of_storage = nullptr;}
}
private:iterator _start = nullptr;iterator _finish=nullptr;iterator _end_of_storage=nullptr;
};

这里我们采用初始化列表来进行默认构造,直接使用私有成员的缺省值,较为简便。

C++11前置生成默认构造

vector()=default;

2.丰富框架,实现接口方法

基本的迭代器实现

iterator begin() {return _start;
}
iterator end() {return _finish;
}
const_iterator begin()const {return _start;
}
const_iterator end() const{return _finish;
}

就是比较简单的返回开始和结束的指针。

数据的[]访问

T& operator[](size_t i) {assert(i < size());return _start[i];
}
const T& operator[](size_t i) const
{assert(i < size());return _start[i];
}

顾名思义就是相似于数组的下标访问

容量和数据空间的改变

void reserve(size_t n) {if (n > capacity()) {size_t oldsize = size();T* temp = new T[n];memcpy(tmp, _start, old_size * sizeof(T));
/*for (size_t i = 0; i < oldsize; i++) {temp[i] = _start[i];}
*/delete[]_start;_start = temp;_finish =temp + oldsize;_end_of_storage = temp+n;}
}

对于扩容操作,我们创建了一个新的数组,然后定义一个oldsize来存储以前的数据空间,来确定之后的_finish位置,因为我们在之前释放了原来的数组了,如果不想这样操作,不想定义一个oldsize,就要把delete操作放在_finish=temp+size()之后。

这里用了memcpy函数来转移数据,但是我们会发现有一个小小的问题,当数据类型为string时,程序会崩溃,这个后续会讲解的。

void resize(size_t n, T val = T()){if (n < size()){_finish = _start + n;}else{reserve(n);while (_finish < _start + n){*_finish = val;++_finish;}}}

resize函数的作用是调整容器的大小,使其能够容纳n个元素。如果n小于当前容器的大小,则容器会被截断;如果n大于当前容器的大小,则容器会被扩展,并且新增的元素会被初始化为val的值。

T val = T():表示一个默认参数,它是容器的元素类型T的默认构造函数生成的对象。如果调用resize时没有指定这个参数,就会使用元素类型的默认值。

如果n小于当前容器的大小
_finish = _start + n;:这条语句会截断容器,使其大小变为n。这里_start是指向容器第一个元素的指针,_finish是指向容器最后一个元素的下一个位置的指针。通过将`_finish`向前移动到_start + n的位置,容器的大小就被减少了。
 如果`n`大于或等于当前容器的大小
- reserve(n);:调用reserve函数确保容器的容量至少为n。如果当前容量小于n,reserve会重新分配内存以容纳至少n个元素。
- 默认构造函数`T()`必须存在,以便能够为新元素提供默认值。如果`T`没有默认构造函数,则这段代码在尝试使用默认参数时会出错。

vector空间大小的返回与判空

size_t size()const {return _finish - _start;
}
size_t capacity()const {return _end_of_storage - _start;
}
bool empty()const {return _start == _finish;
}

大小返回就是几个指针加减即可

数据的增删

对于数据的增删,模拟尾插,尾删,指定位置的插入,删除(后两者都与迭代器iterator相结合)

void push_back(const T& x) {if (_finish == _end_of_storage) {reserve(capacity() == 0 ? 4 : capacity() * 2);}*_finish = x;++_finish;
}
void pop_back() {assert(!empty());--_finish;
}
void erase(iterator pos) {assert(pos >= _start);assert(pos <= _finish);iterator it = pos + 1;while (it != end()) {*(it - 1) = *it;it++;}_finish--;
}
iterator insert(iterator pos,const T&x) {assert(pos >= _start  && pos<=_finish);if (_finish == _end_of_storage) {size_t len = pos - _start;reserve(capacity() == 0 ? 4 : capacity() * 2);pos = _start + len;}iterator end = _finish + 1;while (end > pos) {*end = *(end - 1);end--;}*pos = x;_finish++;return pos;
}

对于插入数据的操作都要进行扩容的判断操作,对于数据的挪动我们可以采用依次赋值,就像代码中的*end=*(end-1);end--//*(it-1)=*it;it++;

通过画图可以更加清楚的理解。

数据打印

template<class T>
void print_vector(const vector<T>& v) {//typename vector<T>::const_iterator it = v.begin();auto it = v.begin();while (it != v.end()) {cout << *it << " ";it++;}cout << '\n';for  (auto e:v) {cout << e << " ";}cout << endl;
}
template<class Container>
void print_container(const Container& v) {auto it = v.begin();while (it != v.end()) {cout << *it << " ";it++;}cout << '\n';for (auto e : v) {cout << e << " ";}cout << endl;
}

规定,没有实例化的类模板里面取东西,编译器不能区分这里const_iterator是类型还是静态成员变量,所以在注释的部分我们看见前面加了个typename.

//typename vector<T>::const_iterator it = v.begin();

print_vector 函数专门用于打印 std::vector<T> 类型的容器。

print_container 函数是一个更通用的模板函数,可以用于打印任何符合容器概念的类型。

 

void vector5()
{vector<string> v;v.push_back("11111111111111111111");v.push_back("11111111111111111111");v.push_back("11111111111111111111");v.push_back("11111111111111111111");print_container(v);v.push_back("11111111111111111111");print_container(v);
}

拷贝构造和赋值重载

vector(const vector<T>& v) {
reserve(v.size());
for (auto e : v) {push_back(e);
}
}
vector<T>operator=(const vector<T>& v) {
if (this != v) {reserve(v.size());for (auto e : v) {push_back(e);}
}
return this;}

这一种思路是开设新空间,然后将数据一个个尾插到创建的对象中。

void swap(vector<T>& v){std::swap(_start, v._start);std::swap(_finish, v._finish);std::swap(_end_of_storage, v._end_of_storage);}// v1 = v3//vector& operator=(vector v)vector<T>& operator=(vector<T> v){swap(v);return *this;}

这种思路是交换的方式,通过调用官方库中的函数,指针交换,将v的地址给了创建的对象。

template <class InputIterator>
vector(InputIterator first, InputIterator last) {while (first != last) {push_back(*first);first++;}
}

通过传需要拷贝的对象的数据范围给新对象(迭代器区间),是个函数模版,可以用任意的迭代器初始化,类型匹配即可 。

模板构造函数,用于构造一个std::vector,该构造函数接受两个迭代器firstlast,它们定义了要复制到新vector中的元素的范围。

3.扩展延伸,深度理解代码

在VS环境下,比较严格,在迭代器方面比较严格,特别是失效迭代器的访问。

迭代器失效问题

在测试接口的过程中,有个bug就是迭代器失效问题

我们知道迭代器的主要作用就是让算法能够不用关心底层数据结构,其底层实际就是一个指针,或者是对指针进行了封装,比如:vector的迭代器就是原生态指针T*

因此迭代器失效,实际就是迭代器 底层对应指针所指向的空间被销毁了,而使用一块已经被释放的空间,造成的后果是程序崩溃(即 如果继续使用已经失效的迭代器,程序可能会崩溃)。

1.对vector进行扩容操作,像resize,reserve等操作

还有就是在insert,push_back操作过程中涉及了扩容

#include <iostream>
using namespace std;
#include <vector>
int main()
{vector<int> v{ 1,2,3,4,5,6 };auto it = v.begin();// 将有效元素个数增加到100个,多出的位置使用8填充,操作期间底层会扩容// v.resize(100, 8);// reserve的作用就是改变扩容大小但不改变有效元素个数,操作期间可能会引起底层容//量改变v.reserve(100);// 插入元素期间,可能会引起扩容,而导致原空间被释放v.insert(v.begin(), 0);v.push_back(8);// 给vector重新赋值,可能会引起底层容量改变//v.assign(100, 8);/*出错原因:以上操作,都有可能会导致vector扩容,也就是说vector底层原理旧空间被释放掉,而在打印时,it还使用的是释放之间的旧空间,在对it迭代器操作时,实际操作的是一块已经被释放的空间,而引起代码运行时崩溃。解决方式:在以上操作完成之后,如果想要继续通过迭代器操作vector中的元素,只需给it重新赋值即可。*/while (it != v.end()){cout << *it << " ";++it;}cout << endl;return 0;
}

修改后的代码 

#include <iostream>
#include <vector>
using namespace std;int main()
{vector<int> v{ 1,2,3,4,5,6 };// 在修改vector之后,重新获取迭代器auto it = v.begin();v.reserve(100);v.insert(v.begin(), 0);v.push_back(8);// 如果使用v.assign(100, 8);,也需要在之后重新获取迭代器// 重新获取迭代器,因为之前的操作可能会改变vector的内存it = v.begin();while (it != v.end()){cout << *it << " ";++it;}cout << endl;return 0;
}

还有一点就是insert数据过后,即使没有扩容,指向容器中插入点之后的所有迭代器、指针和引用都可能失效。

所以当我们继续访问修改p的位置数据,已经失效了,需要更新失效的迭代器。

由于数据挪动,p的指向改变了,所以我们认为迭代器也失效了。

v.insert(p, 40);

p=v.insert(p, 40);
(*(p+1)) *= 10; 

 2.erase的删除导致的迭代器失效问题

	void test_vector3(){std::vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);print_container(v);// 删除所有的偶数auto it = v.begin();while (it != v.end()){if (*it % 2 == 0){v.erase(it);}else{++it;}}print_container(v);}
}

当我们用VS std中的接口时,会发现直接报错

所以我们也要进行重新的更新

it=v.erase(it); 

#include <iostream>
using namespace std;
#include <vector>int main(){int a[] = { 1, 2, 3, 4 };vector<int> v(a, a + sizeof(a) / sizeof(int));// 使用find查找3所在位置的iteratorvector<int>::iterator pos = find(v.begin(), v.end(), 3);// 删除pos位置的数据,导致pos迭代器失效。v.erase(pos);cout << *pos << endl; // 此处会导致非法访问return 0;}

使用memcpy的拷贝问题

当我们想拷贝几个字符串时,就会出现问题了。

 问题分析:

1. memcpy是内存的二进制格式拷贝,将一段内存空间中内容原封不动的拷贝到另外一段内存空间中。

2. 如果拷贝的是自定义类型的元素,memcpy既高效又不会出错,但如果拷贝的是自定义类型元素,并且自定义类型元素中涉及到资源管理时,就会出错,因为memcpy的拷贝实际是浅拷贝。

void reserve(size_t n) {if (n > capacity()) {size_t oldsize = size();T* temp = new T[n];memcpy(tmp, _start, old_size * sizeof(T));
/*for (size_t i = 0; i < oldsize; i++) {temp[i] = _start[i];}
*/delete[]_start;_start = temp;_finish =temp + oldsize;_end_of_storage = temp+n;}
}

当我们使用这个memcpy版本进行扩容插入时,程序会出现问题

测试代码

void vector5() {vector<string> v;v.push_back("11111111111111111111");v.push_back("11111111111111111111");v.push_back("11111111111111111111");v.push_back("11111111111111111111");print_container(v);v.push_back("11111111111111111111");print_container(v);
}

memcpy是浅拷贝,temp和原来的v指向了同一块空间,当调用了delete[]时,11111111...字符串被析构了,空间释放,变成随机值,后面又delete,free _start ,这时候temp指向的是释放的空间。

所以我们可以调用赋值,就可以解决问题,本质调用string的赋值,其他类型赋值一样的。

旧空间释放就不会影响新空间。

for (size_t i = 0; i < oldsize; i++) {
            temp[i] = _start[i];

4.完整代码复现

#pragma once
#include <iostream>
#include <assert.h>
#include <vector>
#include <string>
using namespace std;
namespace my_vector {template<class T>class vector {public:typedef T* iterator;typedef const T* const_iterator;vector(){}vector(const vector<T>& v) {reserve(v.size());for (auto e : v) {push_back(e);}}/*vector<T>operator=(const vector<T>& v) {if (this != v) {reserve(v.size());for (auto e : v) {push_back(e);}}return this;}*/template <class InputIterator>vector(InputIterator first, InputIterator last) {while (first != last) {push_back(*first);first++;}}void swap(vector<T>& v){std::swap(_start, v._start);std::swap(_finish, v._finish);std::swap(_end_of_storage, v._end_of_storage);}// v1 = v3//vector& operator=(vector v)vector<T>& operator=(vector<T> v){swap(v);return *this;}void clear() {_finish = _start;}~vector() {if (_start) {delete[]_start;_start = _finish = _end_of_storage = nullptr;}}iterator begin() {return _start;}iterator end() {return _finish;}const_iterator begin()const {return _start;}const_iterator end() const {return _finish;}void reserve(size_t n) {if (n > capacity()) {size_t oldsize = size();T* temp = new T[n];memcpy(temp, _start, oldsize*sizeof(T));/*for (size_t i = 0; i < oldsize; i++) {temp[i] = _start[i];}*/delete[]_start;_start = temp;_finish = temp + oldsize;_end_of_storage = temp + n;}}void resize(size_t n, T val = T()) {if (n < size()) {_finish = _start + n;}else {reserve(n);while (_finish < _start + n) {*_finish = val;_finish++;}}}void push_back(const T& x) {if (_finish == _end_of_storage) {reserve(capacity() == 0 ? 4 : capacity() * 2);}*_finish = x;++_finish;}void pop_back() {assert(!empty());--_finish;}void erase(iterator pos) {assert(pos >= _start);assert(pos <= _finish);iterator it = pos + 1;while (it != end()) {*(it - 1) = *it;it++;}_finish--;}iterator insert(iterator pos, const T& x) {assert(pos >= _start && pos <= _finish);if (_finish == _end_of_storage) {size_t len = pos - _start;reserve(capacity() == 0 ? 4 : capacity() * 2);pos = _start + len;}iterator end = _finish + 1;while (end > pos) {*end = *(end - 1);end--;}*pos = x;_finish++;return pos;}size_t size()const {return _finish - _start;}size_t capacity()const {return _end_of_storage - _start;}bool empty()const {return _start == _finish;}T& operator[](size_t i) {assert(i < size());return _start[i];}const T& operator[](size_t i) const{assert(i < size());return _start[i];}private:iterator _start = nullptr;iterator _finish = nullptr;iterator _end_of_storage = nullptr;};template<class T>void print_vector(const vector<T>& v) {//typename vector<T>::const_iterator it = v.begin();auto it = v.begin();while (it != v.end()) {cout << *it << " ";it++;}cout << '\n';for (auto e : v) {cout << e << " ";}cout << endl;}template<class Container>void print_container(const Container& v) {/*auto it = v.begin();while (it != v.end()) {cout << *it << " ";it++;}cout << '\n';*/for (auto e : v) {cout << e << " ";}cout << endl;}void vector1() {vector<int>v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);v.push_back(5);v.push_back(6);int x;cin >> x;auto p = find(v.begin(), v.end(), x);if (p != v.end()) {p = v.insert(p, 40);(*(p + 1)) *= 10;}//v.pop_back();//v.pop_back();//v.insert(v.begin() + 2, 5);//v.erase(v.begin() + 3);for (size_t i = 0; i < v.size(); i++) {cout << v[i] << endl;}}void vector2() {vector<int>v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);v.push_back(5);v.push_back(6);print_vector(v);vector<int>v1(v);vector <int>v2 = v;vector<int> v3(v1.begin(), v1.begin() + 3);print_container(v1);print_container(v2);print_container(v3);/*vector<double>v1;v1.push_back(1.1);v1.push_back(2.2);v1.push_back(3.3);v1.push_back(4.4);v1.push_back(5.5);v1.push_back(6.6);print_container(v1);*/}void vector3() {vector<int> v;v.resize(10, 1);v.reserve(20);print_container(v);cout << v.size() << endl;cout << v.capacity() << endl;v.resize(15, 2);print_container(v);v.resize(25, 3);print_container(v);v.resize(5);print_container(v);}void vector4() {vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);v.push_back(4);v.push_back(5);print_container(v);// 删除所有的偶数auto it = v.begin();while (it != v.end()){if (*it % 2 == 0){v.erase(it);}else {//不加else,不会删除连续的偶数,会++两次++it;}}print_container(v);}void test_vector3(){std::vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);print_container(v);// 删除所有的偶数auto it = v.begin();while (it != v.end()){if (*it % 2 == 0){it=v.erase(it);}else{++it;}}print_container(v);}void vector5() {vector<string> v;v.push_back("11111111111111111111");v.push_back("11111111111111111111");v.push_back("11111111111111111111");v.push_back("11111111111111111111");print_container(v);v.push_back("11111111111111111111");print_container(v);}
}

结束语

本期博客就到此结束啦,相信通过自己对vector的实现,大家对vector有了更深的了解。

最后希望友友们给小编点点赞吧,感谢各位友友的支持!!! 


http://www.ppmy.cn/news/1525241.html

相关文章

1.单例模式

目录 简介 饿汉式 懒汉式 双重检测锁式 静态内部类式 枚举单例 测试 测试单例模式&#xff1a; 测试五种单例模式在多线程环境下的效率 问题&#xff08;拓展&#xff09; 例&#xff1a;反射破解单例模式 例&#xff1a;反序列化破解单例模式 总结&#xff1a;如何…

【PyCharm】常用快捷键

此篇文章内容会不定期更新&#xff0c;仅作为学习过程中的笔记记录 PyCharm的所有快捷键&#xff0c;其实均可以自定义&#xff0c;在位于Settings -> Keymap的目录下&#xff08;如图&#xff09;&#xff0c;可以自行改写为自己熟悉的键位组合。 若更改为PyCharm已存在的键…

GESP等级考试 C++二级-if语句

if语句是C中的选择语句&#xff0c;通过if语句程序可以在一种可能、二种可能或者多种可能中做出选择&#xff0c;对于不同的可能进行不同的处理。 1 一种可能 使用if语句对一种可能进行处理的格式如下所示&#xff1a; if (表达式) {语句; } 其中&#xff0c;if后面跟一个圆…

虹科方案 | 精准零部件测试!多路汽车开关按键功能检测系统

欢迎关注虹科&#xff0c;为您提供最新资讯&#xff01; #LIN/CAN总线 #零部件测试 #CAN数据 导读 在汽车制造业中&#xff0c;零部件的安全性、功能性和可靠性是确保车辆整体性能的关键。虹科针对车辆零部件的LIN/CAN总线仿真测试&#xff0c;提出了基于虹科Baby-LIN系列产…

基于单片机的人脸识别的智能门禁系统设计

文章目录 前言资料获取设计介绍功能介绍设计清单核心代码具体实现截图参考文献设计获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师&#xff0c;一名热衷于单片机技术探索与分享的博主、专注于 精通51/STM32/MSP430/AVR等…

【Redis入门到精通一】什么是Redis?

目录 Redis 1. Redis的背景知识 2.Redis特性 3.Redis的使用场景 4.Ubuntu上安装配置Redis Redis Redis在当今编程技术中的地位可以说非常重要&#xff0c;大多数互联网公司内部都在使用这个技术&#xff0c;熟练使用Redis已经成为开发人员的一个必备技能。本章将带领读者…

oracle select字段有子查询会每次执行子查询吗

Oracle在执行SELECT语句时&#xff0c;如果子查询被嵌套在主查询中&#xff0c;子查询会被执行多次&#xff0c;这是因为子查询的结果不会被缓存。每次主查询需要用到子查询的结果时&#xff0c;子查询都会被重新执行。这种行为可能会导致性能问题&#xff0c;特别是当子查询结…

计算机网络:概述 - 计算机网络概述

目录 一. 互联网概述 1.1 网络 1.2 互联网 1.3 因特网 二. 互联网发展的三个阶段 三. 互联网的标准化工作 四. 互联网的组成 五. 计算机网络的类别 5.1 计算机网络的定义 5.2 计算机网络的不同类别 一. 互联网概述 起源于美国的互联网现如今已…

golang学习笔记13——golang的错误处理深度剖析

推荐学习文档 golang应用级os框架&#xff0c;欢迎star基于golang开发的一款超有个性的旅游计划app经历golang实战大纲golang优秀开发常用开源库汇总golang学习笔记01——基本数据类型golang学习笔记02——gin框架及基本原理golang学习笔记03——gin框架的核心数据结构golang学…

简单了解 JVM

目录 ♫什么是JVM ♫JVM的运行流程 ♫JVM运行时数据区 ♪虚拟机栈 ♪本地方法栈 ♪堆 ♪程序计数器 ♪方法区/元数据区 ♫类加载的过程 ♫双亲委派模型 ♫垃圾回收机制 ♫什么是JVM JVM 是 Java Virtual Machine 的简称&#xff0c;意为 Java虚拟机。 虚拟机是指通过软件模…

新手制作视频用什么软件好?五款剪辑工具分享!

在数字时代&#xff0c;视频制作已成为许多人表达创意、记录生活的重要方式。但对于新手而言&#xff0c;面对琳琅满目的视频编辑软件&#xff0c;往往会感到无所适从。今天&#xff0c;我们就来推荐五款适合新手的视频制作软件&#xff0c;它们分别适用于不同的操作系统平台&a…

探索 Linux:开源操作系统的璀璨世界

摘要&#xff1a;本文围绕 Linux 展开深入探讨。从历史来看&#xff0c;20 世纪 90 年代初 Linus Torvalds 发布 Linux 内核源代码开启新纪元&#xff0c;开源模式使内核不断成长。Linux 的核心概念包含内核、文件系统、进程和线程等&#xff0c;其中内核管理硬件资源与提供系统…

Win10安装.net FrameWork3.5失败解决方法

win10安装.net FrameWork3.5失败解决方法 已经好久没有来投稿了,实在最近业务缠身,忙的焦头烂额(呵~多么伟大的牛马) 但最近开发使用windows11实在是拉胯的不行,升级完就后悔,所以就一怒之下,重装了win10 可是,好家伙,我重装完遇到一个问题,就是在使用.Net Framework3.5,按照Mi…

物理学基础精解【5】

文章目录 质点的速度一、平均速度二、瞬时速度三、速度质点的速度计算一、平均速度的计算二、瞬时速度的计算三、速度矢量的标量分量性质、定义、计算、例子和例题性质定义计算例子例题 质点的瞬时速度的方向的计算、性质、例子和例题计算性质例子例题 质点的速度 本题主要考察…

可对画面进行平台传输,实时查看监控的智慧交通开源了

智慧交通视觉监控平台是一款功能强大且简单易用的实时算法视频监控系统。它的愿景是最底层打通各大芯片厂商相互间的壁垒&#xff0c;省去繁琐重复的适配流程&#xff0c;实现芯片、算法、应用的全流程组合&#xff0c;从而大大减少企业级应用约95%的开发成本。用户只需在界面上…

C语言字符函数与字符串函数

目录 1. 字符函数 1.1 字符分类函数 1.2 字符转换函数 2. 字符串函数 2.1 strlen 函数 2.2 strcpy 函数 2.3 strcat 函数 2.4 strcmp 函数 2.5 strncpy 函数 2.6 strncat 函数 2.7 strncmp 函数 2.8 strstr 函数 结语 1. 字符函数 在C语言标准库中提供了一系列用于…

RocketMQ 消费方式

在消息传递系统中&#xff0c;“推&#xff08;Push&#xff09;”和“拉&#xff08;Pull&#xff09;”是两种不同的消息消费方式&#xff0c;RocketMQ 也支持这两种模式。下面是对这两种模式的详细解释&#xff1a; 1. 推模式&#xff08;Push Model&#xff09; 模式简介…

【机器学习(五)】分类和回归任务-AdaBoost算法-Sentosa_DSML社区版

文章目录 一、算法概念一、算法原理&#xff08;一&#xff09;分类算法基本思路1、训练集和权重初始化2、弱分类器的加权误差3、弱分类器的权重4、Adaboost 分类损失函数5、样本权重更新6、AdaBoost 的强分类器 &#xff08;二&#xff09;回归算法基本思路1、最大误差的计算2…

智慧环保平台建设方案

智慧环保平台建设方案摘要 政策导向与建设背景 背景&#xff1a;全国生态环境保护大会提出坚决打好污染防治攻坚战&#xff0c;推动生态文明建设&#xff0c;目标是在2035年实现生态环境质量根本好转。构建生态文明体系&#xff0c;包括生态文化、生态经济、目标责任、生态文明…

【每日一题】LeetCode 2398.预算内的最多机器人数目(滑动窗口、数组、二分查找、前缀和、堆(优先队列))

【每日一题】LeetCode 2398.预算内的最多机器人数目&#xff08;滑动窗口、数组、二分查找、前缀和、堆&#xff08;优先队列&#xff09;&#xff09; 题目描述 给定两个整数数组 chargeTimes 和 runningCosts&#xff0c;分别代表 n 个机器人的充电时间和运行成本。再给定一…