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.mzph.cn/news/882235.shtml

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

相关文章

React Componet类组件详解(老项目)

React类组件是通过创建class继承React.Component来创建的&#xff0c;是React中用于构建用户界面的重要部分。以下是对React类组件的详细解释&#xff1a; 一、定义与基本结构 类组件使用ES6的class语法定义&#xff0c;并继承自React.Component。它们具有更复杂的功能&#…

流量PID控制(开度前馈量计算+辅助PID)

和流体流速(瞬时流量)相关的计算请参考下面文章链接: 1、PLC通过伯努利方程近似计算水箱流量 PLC通过伯努利方程近似计算水箱流量(FC)-CSDN博客文章浏览阅读1.6k次。本文介绍了如何使用PLC通过伯努利方程近似计算水箱中的液体流量,主要涉及流量计算、模型验证、梯形图编程及…

C++学习路线(二十)

项目 模块划分 推箱子游戏 地图初始化 热键控制 推箱子控制 游戏结束 地图初始化 坐标系&#xff08;650&#xff0c;650&#xff09; 地图表示&#xff1a; 使用二维数组 游戏道具展示&#xff08;墙 箱子 箱子目的地 小人 地板&#xff09; 判断游戏…

java基于SpringBoot+Vue+uniapp微信小程序的自助点餐系统的详细设计和实现(源码+lw+部署文档+讲解等)

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

【H∞鲁棒控制】状态反馈、输出反馈、混合灵敏度H∞控制器设计

最近在学H∞鲁棒控制&#xff0c;因为后面项目中可能要用到此控制方法&#xff0c;所以提前进行了学习&#xff0c;刚开始接触感觉看不太懂&#xff0c;涉及的数学专业知识较深&#xff0c;而且网上资料也比较零星且局限&#xff0c;摸爬了好几天才搞懂了点&#xff0c;现自己总…

「Java服务」快速接入SkyWalking方法指南

一、背景 背景&#xff1a;Apache SkyWalking 是一个开源的分布式应用性能监控&#xff08;APM&#xff09;系统&#xff0c;主要用于监控微服务、云原生和容器化应用的性能。接入SkyWalking可以排查以智能投放服务为主的服务响应问题 技术架构 SkyWalking 的核心架构包括以…

银河麒麟V10系统+Windows10双系统启动顺序正确修改方法

***正确可行方法***&#xff0c;测试OK且稳定&#xff1b; 银河麒麟桌面操作系统V10是一款适配国产软硬件平台并深入优化和创新的新一代图形化桌面操作系统&#xff0c;同源支持国内外主流处理器架构&#xff0c;并不断使能GPU、桥片、网卡等各种新硬件&#xff0c;提供更优的软…

vue3学习之插槽slot

关于slot web组件内部的占位符&#xff0c;可以使用自己的标记填充这个占位符 &#xff0c;具名插槽就是在slot标签上添加name属性&#xff08;https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/slot&#xff09; vue3官方文档&#xff1a;https://cn.vuejs.org/gui…

深入解析Java 22:专栏介绍

深入解析Java 22&#xff1a;专栏介绍 Java&#xff0c;作为一种广泛使用的编程语言&#xff0c;一直以来都在不断地发展和改进。2024年3月19日&#xff0c;Java 22的GA版本正式发布&#xff0c;带来了众多令人瞩目的新特性和性能优化。本专栏将深入解析Java 22&#xff0c;带…

AD9680(adc直采芯片)使用说明

写这篇文章之前我是没有使用过AD9680的芯片&#xff0c;但是使用过GMS011芯片&#xff08;是国内24S&#xff09;下的公司出来的芯片&#xff0c;寄存器和管脚全对标。 在这里我就大概说一下芯片的说用方法 一、硬件设计 该芯片支持双通道射频直采 支持协议JESD204B 14位 采样…

leetcode二叉树(五)-二叉树层序遍历

题目 102.二叉树的层序遍历 给你二叉树的根节点 root &#xff0c;返回其节点值的 层序遍历 。 &#xff08;即逐层地&#xff0c;从左到右访问所有节点&#xff09;。 示例 1&#xff1a; 输入&#xff1a;root [3,9,20,null,null,15,7] 输出&#xff1a;[[3],[9,20],[15,7…

【网络篇】计算机网络——网络层详述(笔记)

目录 一、网络层 1. 网络传输流程简述 2. 转发和路由选择 3. 控制平面&#xff1a;SDN 方法 二、路由器工作原理 1. 概述 &#xff08;1&#xff09;输入端口 &#xff08;2&#xff09;交换结构 &#xff08;3&#xff09;输出端口 &#xff08;4&#xff09;路由选…

CAS详谈---无锁的锁机制

假设有多个线程想要操作同一个资源对象&#xff0c;我们首先想到的是使用互斥锁&#xff0c;但是互斥锁是悲观的。 悲观&#xff0c;即操作系统会悲观的认为如果不严格同步线程调用&#xff0c;那么一定会产生异常&#xff0c;所以互斥锁会将资源锁定&#xff0c;只供一个线程…

【动手学深度学习】7.5 批量规范化(个人向笔记)

训练深层神经网络是十分困难的&#xff0c;特别是在较短的时间内使它们收敛更加棘手。而本节的批量规范化&#xff08;batch normalization&#xff09; 可以持续加速深层网络的收敛速度结合下节会介绍道德残差块&#xff0c;批量规范化使得研究人员能够训练100层以上的网络 1.…

nbsaas vue3管理后台框架

nbsaas vue3管理后台框架 一、项目概述 Nbsaas Admin Vue 是一个基于 Vue.js 3.0 构建的轻量级后台管理系统&#xff0c;结合了现代前端技术栈的最佳实践&#xff0c;旨在帮助开发者快速构建具有高可扩展性和良好用户体验的后台管理系统。该项目拥有简洁的 UI 设计&#xff0…

Hikyuu教程 | 滚动回测与滚动寻优系统

前面介绍了如何使用 hikyuu 进行策略回测参数优化&#xff0c;同时也提到了这种简单的参数优化本质其实是对历史数据的过拟合&#xff0c;通常并不具备直接使用的意义。那么有什么办法来减缓这种过拟合影响&#xff0c;让参数优化发挥实际的作用呢&#xff1f;答案是——使用滚…

源码编译方式安装htppd软件

一.源码编译安装httpd软件 1.安装阿帕奇的依赖&#xff0c;安装apr软件&#xff0c;阿帕奇正常运行的环境这个环境就是apr。 2.安装apr-util软件&#xff0c;主要提供针对apr环境的管理工具&#xff0c; 3.安装阿帕奇软件即httpd软件。 如上图所示&#xff0c;就是三个软件的…

E38.【C语言】练习:数据结构时间复杂度的计算

目录 1.二分法的时间复杂度 解: 2.求阶乘的时间复杂度 解: 3.递归实现斐波那契数,求时间复杂度 解: 4.时间复杂度的排名 备注:有关时间复杂度的讲解参见80.【C语言】数据结构之时间复杂度 1.二分法的时间复杂度 (代码来自E7.【C语言】练习&#xff1a;在一个有序数组中…

CentOS 7 yum失效的解决办法

文章目录 一、CentOS 7停止维护导致yum失效的解决办法解决方案 提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考 一、CentOS 7停止维护导致yum失效的解决办法 020 年&#xff0c;CentOS 项目与红帽联合宣布将全部投资转向 CentOS Stream&#xff0c;这是…