C++标准模板库--vector

vector

介绍

        vector(向量)是一种序列容器,表示为可以改变大小的数组。vector中的元素使用连续的存储位置,这意味着也可以使用指向其元素的常规指针+偏移量来访问任意元素,且与数组一样高效。但与数组不同的是,vector的大小可以动态变化,容器会自动处理他们的存储空间。在其内部,vector使用动态分配的数组来存储他们的元素。当插入新元素需要增大容量时,存储空间需要重新分配,这意味着需要分配一个新数组并将所有元素移动到其中。就处理时间而言,这是一个相对昂贵的任务,因此vector不会在每次想容器中添加元素时都重新分配。相反,vector容器可以分配一些额外的存储空间,以适应可能的增长,因此容器的实际容量肯呢个大于包含其元素严格所需的存储空间。因此与数组相比,向量消耗更多的内存,以换取更有效的方式管理存储和动态增长的能力。与其他动态序列容器(deque、列表、前向列表)相比,vector可以非常高效地访问其元素,并且可以相对高效地从其末端添加或删除元素。对于涉及在末尾以外的位置插入或删除元素的操作,他们的性能比其他操作差,并且迭代器引用的一致性不如列表和前向列表。

        以上是官方对于vector的介绍,从上面我们能够了解到vector本质上就是一种可动态增长的顺序表,其特性与数据结构中学习的顺序表相差无几,但vector的出现极大地简化了我们使用顺序表的工作量,且vector与其他容器有着较深的联系,因此学好vector有利于我们未来的学习。


vector的使用

         对于vector,在学习时我们一定要学会查看文档:vector文档,vector在实际中非常重要,我们要熟悉常见的接口,以应对未来的使用。

(constructor)构造函数

函数声明接口说明
vector();无参构造
vector(size_t n, const T& val = T());构造并初始化n个val
vector(const vector& x);拷贝构造
vector(inputiterator first, inputiterator last);使用迭代器进行区间初始化构造

代码演示:

	//构造5个int的空间vector<int> v1(5);//构造10个int空间,并初始化为1vector<int> v2(10, 1);//去掉v2的第一个和最后一个空间,使用剩余空间构造并用相同数值初始化vector<int> v3(++v2.begin(), --v2.end());//拷贝构造vector<int> v4 = v3;

iterator迭代器的使用

函数名称接口说明
begin+end获取第一个数据位置的iterator/const_iterator,获取最后一个数据的下一个位置的iterator/const_iterator.
rbegin+rend获取最后一个数据位置的reverse_iterator,获取第一个数据前一个位置的reverse_iterator

 代码演示:

vector<int> v(10, 1);
// []下标形式
for (size_t i = 0; i < v.size(); i++)
{cout << v[i] << " ";
}
cout << endl;//迭代器
vector<int>::iterator it = v.begin();
while (it != v.end())
{cout << *it << " ";it++;
}
cout << endl;//反向迭代器
vector<int>::reverse_iterator rit = v.rbegin();
while(rit != v.rend())
{cout << *rit << " ";rit++;
}
cout << endl;//范围for
for (auto& e : v)
{cout << e << " ";
}
cout << endl;

空间管理函数

函数名称接口说明
size

获取数据个数

capacity获取容量大小
empty判断是否为空
resize改变vector的_size
reserve改变vector的capacity

注意:

  • capacity函数在vs和g++下的扩容机制略有不同,vs下capacity是按1.5倍增长的,g++下是按2倍增长的。在面试中经常会考察这个问题,不要固化地认为vector的扩容都是2倍,具体增长多少是根据具体的需求来定义的。vs是PJ版本STL,g++是SGI版本STL。
  • reserve函数只负责开空间,如果明确知道需要开多少空间,reserve就可以一次性开好,缓解了多次扩容的代价。
  • resize在开空间的同时还会进行初始化,影响_size。

扩容机制测试代码:

// 测试vector的默认扩容机制
void TestVectorExpand()
{size_t sz;vector<int> v;sz = v.capacity();cout << "making v grow:\n";for (int i = 0; i < 100; ++i){v.push_back(i);if (sz != v.capacity()){sz = v.capacity();cout << "capacity changed: " << sz << '\n';}}
}

 内存管理函数代码演示:

    //reservevector<int> v(10, 1);cout << "size:" << v.size() << " " << "capacity:" << v.capacity() << endl;v.reserve(20);cout << "size:" << v.size() << " " << "capacity:" << v.capacity() << endl;v.reserve(15);cout << "size:" << v.size() << " " << "capacity:" << v.capacity() << endl;v.reserve(5);cout << "size:" << v.size() << " " << "capacity:" << v.capacity() << endl;//resizevector<int> v(10, 1);cout <<"size:" << v.size() << " " << "capacity:" << v.capacity() << endl;for (auto& e : v){cout << e << " ";}cout << endl;v.resize(15,2);cout << "size:" << v.size() << " " << "capacity:" << v.capacity() << endl;for (auto& e : v){cout << e << " ";}cout << endl;v.resize(25, 3);cout << "size:" << v.size() << " " << "capacity:" << v.capacity() << endl;for (auto& e : v){cout << e << " ";}cout << endl;v.resize(5);cout << "size:" << v.size() << " " << "capacity:" << v.capacity() << endl;for (auto& e : v){cout << e << " ";}cout << endl;

增删查改

函数名称接口说明
push_back尾插
pop_back尾删
find查找(不是vector的成员接口)
insert在pos之前插入val
erase

删除pos位置的数据

swap交换两个vector对象的数据空间
operator[]像数组一样访问

代码演示:

vector<int> v(5, 1);
for (auto& e : v)
{cout << e << " ";
}
cout << endl;
//尾删
v.pop_back();
for (auto& e : v)
{cout << e << " ";
}
cout << endl;
//尾插
v.push_back(5);
for (auto& e : v)
{cout << e << " ";
}
cout << endl;
//pos位置之前插入
v.insert(v.begin(), 4);
for (auto& e : v)
{cout << e << " ";
}
cout << endl;
//寻找第一个为1数据的位置
vector<int>::iterator pos = find(v.begin(),v.end(),1);
//删除pos位置的数据
v.erase(pos);
for (auto& e : v)
{cout << e << " ";
}
cout << endl;
// operator[]的调用
v[2] = 9;
for (auto& e : v)
{cout << e << " ";
}
cout << endl;
//swap交换函数
vector<int> v1(5, 2);
swap(v, v1);
for (auto& e : v)
{cout << e << " ";
}
cout << endl;

vector<vector<int>>

        vector<vector<int>>是C++中的动态二维数组结构,是一个vector的集合,其中每个元素都是一个vector<int>,用于存储整数。对比c语言中的二维数组,两者的存储方式与索引访问方法是一样的,不同指出在于:1、vector的动态性(vector<vector<int>>可以动态调整大小,支持在运行时添加或删除行),而二维数组的大小需要在编译时固定。2、内存管理方面,vector会自动管理内存,避免了内存泄漏,而二维数组需要手动管理(特别是在动态分配内存时)。3、便利性:vector中提供了许多遍历的成员函数(如size()、push_back()等),使用更加灵活。

例题:

        给定一个非负整数 numRows生成「杨辉三角」的前 numRows 行。在「杨辉三角」中,每个数是它左上方和右上方的数的和。

 C++的vector写法:

class Solution {
public:vector<vector<int>> generate(int numRows) {vector<vector<int>> vv(numRows);for(size_t i = 0; i < vv.size(); i++){vv[i].resize(i+1,1);}for(size_t i = 2; i < vv.size(); i++){for(size_t j = 1; j < vv[i].size()-1; j++){vv[i][j] = vv[i-1][j] + vv[i-1][j-1];}}return vv;}
};

        在使用vector<vector<int>>做这道题时,我们可以发现每一列的数组是动态开辟的,这样极大的简化了代码量,且在遍历访问数组赋值时,由于resize初始化可以给1,我们在下面的遍历赋值中可以省去第一二行和每一行的首尾的赋值操作。

vector底层的模拟实现

框架:

template<class T>
class vector
{
public:private:iterator _start = nullptr;iterator _finish = nullptr;iterator _end_of_storage = nullptr;
};

默认成员函数的实现:

//构造
/*vector()
{}*/
vector() = default;		// C++11 强制生成默认构造
vector(size_t n, const T& val = T())
{reserve(n);for (size_t i = 0; i < n; i++){push_back(val);}
}
vector(int n, const T& val = T())
{  //解决传单个整形参数不匹配问题reserve(n);for (size_t i = 0; i < n; i++){push_back(val);}
}
template<class InputIterator>
vector(InputIterator first, InputIterator last)
{while (first != last){push_back(*first);++first;}
}
//拷贝构造
vector(const vector<T>& v)
{reserve(v.size());for (auto& e : v){push_back(e);}
}
//swap
void swap(vector<T>& v)
{std::swap(_start, v._start);std::swap(_finish, v._finish);std::swap(_end_of_storage, v._end_of_storage);
}
//赋值运算符重载
vector<T>& operator=(vector<T> tmp)
{swap(tmp);return *this;
}
//析构函数
~vector()
{delete[] _start;_start = _finish = _end_of_storage = nullptr;
}

        上面的代码中实现了一个简单的 vector 类的构造函数、拷贝构造、析构函数和一些成员函数。

  1. 无参默认构造函数:使用 C++11 的特性生成一个默认构造函数。这里也可以写一个空的默认构造函数,我们只要在变量声明时给了缺省值,就无需再给默认值初始化。
  2. 带参默认构造函数:vector(size_t n, const T& val = T())vector(int n, const T& val = T()):这两个构造函数用于创建一个包含 n 个元素、初始值为 valvector。使用 reserve(n) 预留空间,随后通过 push_back(val)vector 中添加元素。
  3. 迭代器区间构造函数:接受两个迭代器,复制 [first, last) 范围内的元素到 vector 中。
  4. 拷贝构造函数:通过 reserve 分配足够的空间,然后逐个使用 push_back 复制原 vector 的元素。
  5. 交换函数:通过交换内部指针 _start_finish_end_of_storage 实现高效的资源交换。
  6. 赋值运算符重载:使用拷贝并交换的技术实现赋值运算符,避免多次资源管理,确保安全性和效率。
  7. 析构函数:清理分配的内存,将指针置为空,防止悬挂指针。

迭代器的实现:

//迭代器
typedef T* iterator;
typedef const T* const_iterator;
iterator begin()
{return _start;
}
iterator end()
{return _finish;
}
const_iterator begin() const
{return _start;
}
const_iterator end() const
{return _finish;
}

         上面的代码实现了一个简单的迭代器接口,允许用户遍历 vector 中的元素。

  1. iteratorconst_iterator 分别定义了指向元素的普通指针和常量指针类型,即普通迭代器和const迭代器。
  2. begin() 返回指向 vector 第一个元素的迭代器(_start)。
  3. end() 返回指向 vector 最后一个元素后一个位置的迭代器(_finish),用于表示迭代的结束。
  4. 常量版本的 begin()end() 允许在 const 对象上使用迭代器,保证不会修改 vector 中的元素。

空间管理函数:

//empty
bool empty()
{if (_start == _finish)return true;elsereturn false;
}// size和capacity
size_t size() const
{return _finish - _start;
}
size_t capacity() const
{return _end_of_storage - _start;
}
// reserve
void reserve(size_t n)
{if (n > capacity()){size_t old_size = size();T* tmp = new T[n];//memcpy(tmp, _start, old_size * sizeof(T));for (size_t i = 0; i < old_size; i++){tmp[i] = _start[i];}delete[] _start;_start = tmp;_finish = _start + old_size;_end_of_storage = _start + n;}
}
//resize
void resize(size_t n, T val = T())
{if (n < _finish){_finish = _start + n;}else{reserve(n);while (_finish < _start + n){*_finish = val;++_finish;}}
}

        上面代码实现了一些常用的 vector 成员函数,包括检查是否为空、获取大小和容量、以及调整容量和大小的功能。

  1. empty:检查 vector 是否为空,判断 begin()end() 是否相同。如果相同,返回 true,表示没有元素;否则返回 false
  2. size() :返回当前 vector 中元素的数量(_finish_start 的差)。
  3. capacity(): 返回当前分配的空间大小(_end_of_storage_start 的差),表示在不重新分配内存的情况下,最多能容纳多少元素。
  4.  reserve(n):用于调整 vector 的容量,如果 n 大于当前容量,重新分配内存并复制原有元素到新内存中,释放旧内存。这里需要注意如果对象中涉及到资源管理时,千万不能使用memcpy进行对象之间的拷贝,因为memcpy是浅拷贝,否则可能会引起内存泄漏甚至程序崩溃。
  5. resize(n, val): 调整 vector 的大小:如果新大小 n 小于当前大小,更新 _finish;如果 n 大于当前大小,首先调用 reserve(n) 确保有足够的容量,然后在新增加的元素位置填充 val

增删查改:

//尾插
void push_back(const T& x)
{if (_finish == _end_of_storage){reserve(capacity() == 0 ? 4 : 2 * capacity());}*_finish = x;++_finish;
}
//尾删
void pop_back()
{if (!empty());--_finish;
}
//insert
void insert(iterator pos, const T& x)
{assert(pos >= _start);assert(pos <= _finish);//扩容if (_finish == _end_of_storage){//解决迭代器失效的问题size_t len = pos - _start;reserve(capacity() == 0 ? 4 : 2 * capacity());pos = _start + len;}iterator end = _finish - 1;while (end >= pos){*(end + 1) = *end;end--;}*pos = x;++_finish;
}
//erase
void erase(iterator pos)
{assert(pos >= _start);assert(pos < _finish);iterator end = pos + 1;while (end < _finish){*(end - 1) = *end;end++;}_finish--;
}// operator[]
T& operator[](int i)
{return _start[i];
}
const T& operator[](int i) const
{return _start[i];
}

        上面代码实现了 vector 的一些基本操作,包括尾插、尾删、插入、删除元素,以及重载下标运算符。

  1. push_back(x):vector 的末尾添加一个元素 x。如果当前容量已满,调用 reserve() 扩大容量(初始为 4,之后每次扩展为当前容量的两倍),随后将新元素赋值到 _finish 指向的位置,然后递增 _finish
  2. pop_back() :从 vector 的末尾删除一个元素。先检查是否为空(条件判断有一个多余的分号,应去掉),然后递减 _finish即可。
  3. insert(pos, x): 在指定位置 pos 插入元素 x。先检查 pos 是否有效。如果当前容量已满,扩容并更新 pos 的指向。通过向后移动元素,为新元素留出空间,最后将 x 放入 pos 位置,并递增 _finish
  4. erase(pos): 删除指定位置的元素。先检查 pos 是否有效。将 pos 后面的元素向前移动,覆盖 pos 位置的元素,最后递减 _finish
  5. 提供对 vector 元素的随机访问。非常量版本返回可修改的元素引用,常量版本返回常量引用,保证元素不能被修改。

完整代码:

vector.h

#pragma once
#include<iostream>
#include<vector>
#include<assert.h>
using namespace std;// 模板不能把声明和定义分离,因此只定义了一个.h文件。namespace zy
{
//vctor<vector<int>>template<class T>class vector{public://构造/*vector(){}*/vector() = default;		// C++11 强制生成默认构造vector(size_t n, const T& val = T()){reserve(n);for (size_t i = 0; i < n; i++){push_back(val);}}vector(int n, const T& val = T()){  //解决传单个整形参数不匹配问题reserve(n);for (size_t i = 0; i < n; i++){push_back(val);}}template<class InputIterator>vector(InputIterator first, InputIterator last){while (first != last){push_back(*first);++first;}}//拷贝构造vector(const vector<T>& v){reserve(v.size());for (auto& e : v){push_back(e);}}//swapvoid swap(vector<T>& v){std::swap(_start, v._start);std::swap(_finish, v._finish);std::swap(_end_of_storage, v._end_of_storage);}//赋值运算符重载vector<T>& operator=(vector<T> tmp){swap(tmp);return *this;}//析构函数~vector(){delete[] _start;_start = _finish = _end_of_storage = nullptr;}//迭代器typedef T* iterator;typedef const T* const_iterator;iterator begin(){return _start;}iterator end(){return _finish;}const_iterator begin() const{return _start;}const_iterator end() const{return _finish;}//emptybool empty(){if (_start == _finish)return true;elsereturn false;}// size和capacitysize_t size() const{return _finish - _start;}size_t capacity() const{return _end_of_storage - _start;}// reservevoid reserve(size_t n){if (n > capacity()){size_t old_size = size();T* tmp = new T[n];//memcpy(tmp, _start, old_size * sizeof(T));for (size_t i = 0; i < old_size; i++){tmp[i] = _start[i];}delete[] _start;_start = tmp;_finish = _start + old_size;_end_of_storage = _start + n;}}//resizevoid resize(size_t n, T val = T()){if (n < _finish){_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 : 2 * capacity());}*_finish = x;++_finish;}//尾删void pop_back(){if (!empty());--_finish;}//insertvoid insert(iterator pos, const T& x){assert(pos >= _start);assert(pos <= _finish);//扩容if (_finish == _end_of_storage){//解决迭代器失效的问题size_t len = pos - _start;reserve(capacity() == 0 ? 4 : 2 * capacity());pos = _start + len;}iterator end = _finish - 1;while (end >= pos){*(end + 1) = *end;end--;}*pos = x;++_finish;}//erasevoid erase(iterator pos){assert(pos >= _start);assert(pos < _finish);iterator end = pos + 1;while (end < _finish){*(end - 1) = *end;end++;}_finish--;}// operator[]T& operator[](int i){return _start[i];}const T& operator[](int i) const{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){// 规定,不能在没有实例化的类模板里面取东西,编译器不能区分这里const_iterator是类型还是静态成员变量// 想取东西需要在开头加typename,或者使用auto自动判断类型//typename vector<T>::const_iterator it = v.begin();auto it = v.begin();while (it != v.end()){cout << *it << " ";it++;}cout << endl;}void vector_test1(){vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);//v.push_back(5);v.insert(v.begin() + 1, 6);print_vector(v);auto it = v.begin();while (it != v.end()){//使用insert和erase以后,就不能直接访问,需要先更新这个迭代器的值。//因为在vs的标准库中是会报错的,因为发生了迭代器失效if ((*it) % 2 == 0){//it = v.erase(it);v.erase(it);}else {++it;}}print_vector(v);}
};

vector迭代器失效问题

        迭代器的主要作用就是让算法能够忽略底层数据结构的差异,可直接对容器进行遍历。其底层实际就是一个指针,或者是对指针进行了封装,比如:vector的迭代器就是原生态指针T*。因此迭代器失效实际上就是迭代器底层对应的指针所指向的空间被销毁了或被修改了,导致迭代器指向的是一块已经被释放的空间或被修改后的空间,造成的后果是程序崩溃或取值不对。

对于vector可能会导致其迭代器失效的操作有:

  • 会引起其底层空间改变的操作,都有可能是迭代器失效,比如:resize、reserve、insert、assign、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;
}
  •  指定位置元素的删除操作--erase
#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;
}

        erase删除pos位置元素后,pos位置之后的元素会往前搬移,没有导致底层空间的改变,理论上讲迭代器不应该会失效,但是:如果pos刚好是最后一个元素,删完之后pos刚好是end的位置,而end位置是没有元素的,那么pos就失效了。因此删除vector中任意位置上元素时,vs就认为该位置迭代器失效了。

以本题为例:删除vector中所有的偶数。

#include <iostream>
using namespace std;
#include <vector>
int main()
{vector<int> v{ 1, 2, 3, 4 };auto it = v.begin();while (it != v.end()){if (*it % 2 == 0)v.erase(it);++it;} return 0;
}

        这样写就会导致迭代器失效的问题,在删除it位置的元素后,后面的元素会先前覆盖,也就是说此时 it 又指向了下一个元素,然后it++,导致跳过了一个元素,有可能导致数据删除的不干净。解决办法是每次删除元素后,返回一个迭代器给it,在删除元素时跳过++it,这样就避免了‘遍历时有元素被跳过。

#include <iostream>
using namespace std;
#include <vector>
int main()
{vector<int> v{ 1, 2, 3, 4 };auto it = v.begin();while (it != v.end()){if (*it % 2 == 0)it = v.erase(it);else++it;} return 0;
}

        值得注意的是:Linux下,g++编译器对迭代器失效的检测并不是非常严格,处理也没有vs下极端。且与vector类似,string在插入+扩容操作和erase之后,迭代器也会失效。

        迭代器失效解决办法是在使用前,对迭代器重新赋值即可。


 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/451159.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

网络空间安全之一个WH的超前沿全栈技术深入学习之路(一:渗透测试行业术语扫盲)作者——LJS

欢迎各位彦祖与热巴畅游本人专栏与博客 你的三连是我最大的动力 以下图片仅代表专栏特色 [点击箭头指向的专栏名即可闪现] 专栏跑道一 ➡️网络空间安全——全栈前沿技术持续深入学习 专栏跑道二➡️ 24 Network Security -LJS ​ ​ ​ 专栏跑道三 ➡️ MYSQL REDIS Advanc…

线性系统性能分析方法3——频率特性分析法(频域分析法)

一种图解的分析方法&#xff0c;不必直接求解系统输出的时域表达式&#xff0c;不需要求解系统的闭环特征根&#xff0c;具有较多的优点。如&#xff1a; ①根据系统的开环频率特性揭示闭环系统的动态性能和稳态性能&#xff0c;得到定性和定量的结论&#xff0c;可以简单迅速…

OpenCV高级图形用户界面(12)用于更改指定窗口的大小函数resizeWindow()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 cv::resizeWindow() 函数用于更改指定窗口的大小。这使得你可以根据需要调整窗口的宽度和高度。 注释 指定的窗口大小是指图像区域的大小。工具栏…

基于Java语言的培训平台+学习平台+在线学习培训系统+教育平台+教育学习系统+课程学习平台

简述 企业培训平台企业考试系统培训平台考试系统企业大学企业视频网站视频学习平台 介绍 企业培训平台支持企业培训考试全流程&#xff0c;在线学习、在线考试&#xff0c;成熟的企业培训考试解决方案&#xff0c;充分满足企业培训需求。 独立部署&#xff0c;仅内部员工登录…

Java 异步编程——常用线程池 ThreadPoolExecutor

文章目录 ThreadPoolExecutor核心参数详解线程池的处理流程线程池中使用的阻塞队列4 种任务拒绝策略4 种不同的线程池线程池的五种状态 Future 接口 ThreadPoolExecutor java.uitl.concurrent.ThreadPoolExecutor 类是线程池中最核心的一个类。 线程池底层示例图&#xff1a;…

代码随想录算法训练营第十六天|513. 找树左下角的值 112. 路径总和 106. 从中序与后序遍历序列构造二叉树

513. 找树左下角的值 题目 给定一个二叉树的 根节点 root&#xff0c;请找出该二叉树的 最底层 最左边 节点的值。 假设二叉树中至少有一个节点。 示例 1: 输入: root [2,1,3] 输出: 1 题目解析---迭代法 层序遍历&#xff0c;然后只需要记录最后一行第一个节点的数值就可…

xtu oj 原根

文章目录 回顾杂思路c 语言代码 回顾 AB III问题 H: 三角数问题 G: 3个数等式 数组下标查询&#xff0c;降低时间复杂度1405 问题 E: 世界杯xtu 数码串xtu oj 神经网络xtu oj 1167 逆序数&#xff08;大数据&#xff09; 杂 有一些题可能是往年的程设的题&#xff0c;现在搬到…

001 Hadoop安装、Spring整合测试

Hadoop安装、整合测试 文章目录 Hadoop安装、整合测试1.简介1.优点2.组成 2.安装1.安装jdk&#xff08;如已安装可跳过&#xff09;2.安装hadoop1.安装2. 修改配置文件core-site.xml3. 修改配置文件hdfs-site.xml4.启动hadoop5.启动yarn6.执行jps查看7.相关端口及配置位置8.访问…

Zilliz获Forrester报告全球第一;OB支持向量能力;Azure发布DiskANN;阿里云PG发布内置分析引擎

重要更新 1. Azure发布PostgreSQL向量索引扩展DiskANN&#xff0c;声称在构建HNSW/IVFFlat索引上&#xff0c;速度、精准度都超越pg_vector&#xff0c;并解决了pg_vector长期存在的偶发性返回错误结果的问题( [1] )。 2. 阿里云RDS PostgreSQL 发布AP加速引擎&#xff08;rds…

修改MYSQL库的默认字符集和校验规则

修改mysql的默认配置文件my.cnf。 vim /etc/my.cnf 如果没有这个文件就可能在这个路径&#xff1a;/etc/mysql/my.cnf 在 [mysqld] 部分下&#xff0c;添加或修改以下设置&#xff1a; character-set-server [要修改的字符集] collation-server [要修改的校验规则] 保存文…

Git客户端使用之命令行

一、git客户端命令行的使用 1、创建本地用户并绑定ssh-key到gitlab #在本地注册用户,这个用户随便创建它就是与git仓库连接的一个用户&#xff0c;不过最好喝git仓库用户一样有利于区分。 git config --global user.name "wenqiang1" git config --global user.ema…

SpringBoot+Vue+Uniapp智能社区服务小程序系统(源码+lw+部署文档+讲解等)

项目运行截图 技术框架 后端采用SpringBoot框架 Spring Boot 是一个用于快速开发基于 Spring 框架的应用程序的开源框架。它采用约定大于配置的理念&#xff0c;提供了一套默认的配置&#xff0c;让开发者可以更专注于业务逻辑而不是配置文件。Spring Boot 通过自动化配置和约…

web 0基础第一节 文本标签

学习web语言 首先推荐安装一个vs code 的软件 这个普及度更广一点 兼容性好 网上有很多下载的教程 这里就直接从 html5 的内容开始说了. 这是一个html文件的基本结构 在vs code 中使用英文的 ! 可快捷设置这样的结构 <!-- --> 是在html写注释的结构 以后的…

【Java数据结构】优先级队列(堆)

【本节目标】 1. 掌握堆的概念及实现 2. 掌握 PriorityQueue 的使用 一. 优先级队列 1 概念 前面学过队列&#xff0c;队列是一种先进先出 (FIFO) 的数据结构 &#xff0c;但有些情况下&#xff0c; 操作的数据可能带有优先级&#xff0c;一般出队 列时&#xff0c;可…

【前端】如何制作一个自己的网页(8)

以下内容接上文。 CSS的出现&#xff0c;使得网页的样式与内容分离开来。 HTML负责网页中有哪些内容&#xff0c;CSS负责以哪种样式来展现这些内容。因此&#xff0c;CSS必须和HTML协同工作&#xff0c;那么如何在HTML中引用CSS呢&#xff1f; CSS的引用方式有三种&#xff1…

【LeetCode算法笔记】Day1:动态规划基础

目录 动态规划简介动态规划的定义动态规划的核心思想动态规划的简单例子 动态规划特征最优子结构性质重复子问题性质无后效应 动态规划的基本思路 动态规划简介 动态规划的定义 简称DP,是一种求解多阶段决策过程最优化问题的方法。在动态规划中&#xff0c;通过把原问题分解为…

Golang | Leetcode Golang题解之第478题在圆内随机生成点

题目&#xff1a; 题解&#xff1a; type Solution struct {radius, xCenter, yCenter float64 }func Constructor(radius, xCenter, yCenter float64) Solution {return Solution{radius, xCenter, yCenter} }func (s *Solution) RandPoint() []float64 {r : math.Sqrt(rand.…

MySQL面试专题-索引

一、MySQL为什么要选择B树来存储索引&#xff1f; MySQL的索引选择B树作为数据结构来进行存储&#xff0c;其本质原因在于可以减少IO次数&#xff0c;提高查询效率&#xff0c;简单来说就是保证在树的高度不变的情况下可以存储更多的数据。 &#xff08;一&#xff09;IO角度 在…

约克VRF打造舒适绿色无污染的生活环境

在生活的各个方面&#xff0c;约克VRF都采取了多种措施助力碳中和。 采用国际领先的空气源热泵技术&#xff0c;只需少量电力就可将空气中的能量转化为室内热量&#xff0c;被称为“大自然的搬运工”&#xff01;COP能效值最高可达4.24&#xff08;每用一度电产生4.24度电热量&…

第 3 章:使用 Vue 脚手架

1. 初始化脚手架 1.1 说明 Vue 脚手架是 Vue 官方提供的标准化开发工具&#xff08;开发平台&#xff09;。最新的版本是 5.x。文档: https://cli.vuejs.org/zh/ 1.2 具体步骤 第一步&#xff08;仅第一次执行&#xff09;&#xff1a;全局安装vue/cli。 npm install -g vu…