【C++初阶】vector容器

news2024/11/23 8:54:16

在这里插入图片描述

👦个人主页:@Weraphael
✍🏻作者简介:目前学习C++和算法
✈️专栏:C++航路
🐋 希望大家多多支持,咱一起进步!😁
如果文章对你有帮助的话
欢迎 评论💬 点赞👍🏻 收藏 📂 加关注✨


目录

  • 一、什么是vector
  • 二、vector初始化
      • 2.1 默认构造函数(常见)
      • 2.2 构造函数将n个元素拷贝给本身
      • 2.3 拷贝构造函数(常见)
      • 2.4 区间拷贝
      • 2.5 数组方式
  • 三、迭代器的使用
      • 3.1 begin + end(常见)
      • 3.2 rbegin + rend
  • 四、遍历
      • 4.1 operator[]
      • 4.2 迭代器遍历
      • 4.3 范围for
  • 五、空间增长问题
      • 5.1 size
      • 5.2 empty
      • 5.3 resize
      • 5.4 reserve
      • 5.5 swap
  • 六、vector的插入与删除
      • 6.1 push_back - 尾插
      • 6.2 pop_back - 尾删
      • 6.3 insert - 插入
      • 6.4 erase - 删除pos位置的数据
      • 6.5 clear - 清空所有数据
  • 七、几个常用算法
      • 7.1 sort - 排序
      • 7.2 reverse
      • 7.3 find
  • 八、迭代器失效问题
      • 8.1 什么是迭代器失效
      • 8.2 为什么string不存在迭代器失效问题
      • 8.3 几个常见的迭代器失效样例
      • 8.4 如何解决迭代器失效问题

一、什么是vector

  • vector容器和数组非常相似,与普通数组的区别:数组是静态空间,而vector可以动态扩展
  • 动态扩展并不是在原空间之后续接新空间,而是找更大的内存空间,然后将原数据拷贝新空间,释放原空间
  • 使用vector容器需要包含头文件:#include <vector>
  • vector可以存储多种不同的数据类型,是因为它是一个模板容器
    在这里插入图片描述
    通过使用模板参数,我们可以在vector中指定要存储的数据类型,例如:
vector<int> vi // 整型容器
vector<char> vc // 字符型容器
vector<double> vd // 浮点型容器
vector<string> vs // string类型容器
vector<vector<int>> // 本质是二维数组
// 等等...

类模板实例化与普通类的实例化不同,类模板实例化需要在类模板名字后跟<>,然后将实例化的类型放在<>中即可,而普通类的类名就是类型。

注意:需要注意数据类型的统一性。例如,如果创建了一个存储整数的vector,就应该只向其中存储整数类型的数据,否则可能会出现类型错误或数据损坏的问题。

二、vector初始化

2.1 默认构造函数(常见)

#include <iostream>
#include <vector>
using namespace std;
int main()
{
	// 默认构造函数
	vector<int> v;

	return 0;
}

默认构造的对象,sizecapacity都为0。

在这里插入图片描述

2.2 构造函数将n个元素拷贝给本身

【函数原型】

vector<T> v(n,elem);

【代码示例】

#include <iostream>
#include <vector>
using namespace std;

int main()
{
	// 构造函数将n个元素拷贝给本身
	// 将3个100拷贝给本身
	vector<int> v(3, 100);

	for (int i = 0; i < v.size(); i++)
	{
		cout << v[i] << ' ';
	}
	cout << endl;

	return 0;
}

【输出结果】

在这里插入图片描述

2.3 拷贝构造函数(常见)

【函数原型】

vector (const vector& x);

【代码示例】

#include <iostream>
#include <vector>
using namespace std;

int main()
{
	// 构造函数将3个100拷贝给本身
	vector<int> v1(3, 100);

	cout << "v1:";
	for (int i = 0; i < v1.size(); i++)
	{
		cout << v1[i] << ' ';
	}
	cout << endl;

	// 拷贝构造函数
	// v2是v1的副本
	vector<int> v2(v1);

	cout << "v2:";
	for (int i = 0; i < v2.size(); i++)
	{
		cout << v2[i] << ' ';
	}
	cout << endl;

	return 0;
}

【输出结果】

在这里插入图片描述

2.4 区间拷贝

【函数原型】

vector(v.begin(),v.end());

【代码示例】

#include <iostream>
#include <vector>
using namespace std;

int main()
{
	// 构造函数将n个元素拷贝给本身
	// 将5个100拷贝给本身
	vector<int> v1;

	// 写入1 2 3 4 5
	v1.push_back(1);
	v1.push_back(2);
	v1.push_back(3);
	v1.push_back(4);
	v1.push_back(5);

	// 区间拷贝
	// 拷贝v1对象中的2、3、4、5
	vector<int> v2(v1.begin() + 1, v1.end());
	cout << "v2:";
	for (int i = 0; i < v2.size(); i++)
	{
		cout << v2[i] << ' ';
	}
	cout << endl;

	return 0;
}

【输出结果】

在这里插入图片描述

2.5 数组方式

#include <iostream>
#include <vector>
#include <string>
using namespace std;

int main()
{
	vector<int> v1{ 1,2,3,4,5,6 };
	vector<char> v2{ 'h','e','l','l','o' };
	vector<string> v3{ "hello", "vector" };

	return 0;
}

【输出结果】

在这里插入图片描述

三、迭代器的使用

3.1 begin + end(常见)

大家可以认为迭代器是指针。begin指向第一个数据的位置,end指向最后一个数据的下一个位置

在这里插入图片描述

【代码示例】

#include <iostream>
#include <vector>
using namespace std;

int main()
{
	vector<char> v;
	// 插入abcdefg
	v.push_back('a');
	v.push_back('b');
	v.push_back('c');
	v.push_back('d');
	v.push_back('e');
	v.push_back('f');
	v.push_back('g');

	vector<char>::iterator begin = v.begin();

	// begin指向第一个元素,对齐解引用就能得到
	cout << "第一个元素为:" << *begin << endl;

	vector<char>::iterator end = v.end();
	// end指向最后一个元素的下一个位置
	cout << "最后一个元素为:" << *(end - 1) << endl;

	return 0;
}

【输出结果】

在这里插入图片描述

3.2 rbegin + rend

rbeginrend是反着来的。rbegin指向的是最后一个元素,rend指向的是第一个元素的前一个位置
在这里插入图片描述

【代码示例】

#include <iostream>
#include <vector>
using namespace std;

int main()
{
	vector<char> v;
	// 插入abcdefg
	v.push_back('a');
	v.push_back('b');
	v.push_back('c');
	v.push_back('d');
	v.push_back('e');
	v.push_back('f');
	v.push_back('g');

	// vector<char>::iterator 如果认为太长
	// 可用auto
	auto vci = v.rbegin();
	// vci指向最后一个元素
	cout << "最后一个元素:" << *vci << endl;

	auto vcc = v.rend();
	// vcc指向第一个元素的前一个位置
	// 对其-1。就指向第一个元素
	cout << "第一个元素" << *(vcc - 1) << endl;

	return 0;
}

【输出结果】

在这里插入图片描述

四、遍历

4.1 operator[]

在这里插入图片描述

vector底层重载了下标访问操作符[],因此可以像数组一样变量

#include <iostream>
#include <vector>
using namespace std;
int main()
{
	vector<char> v;
	// 插入abcdefg
	v.push_back('a');
	v.push_back('b');
	v.push_back('c');
	v.push_back('d');
	v.push_back('e');
	v.push_back('f');
	v.push_back('g');
	
	// operator[]
	for (int i = 0; i < v.size(); i++)
	{
		cout << v[i] << ' ';
	}
	cout << endl;

	return 0;
}

【输出结果】

在这里插入图片描述

4.2 迭代器遍历

#include <iostream>
#include <vector>
using namespace std;
int main()
{
	vector<char> v;
	// 插入abcdefg
	v.push_back('a');
	v.push_back('b');
	v.push_back('c');
	v.push_back('d');
	v.push_back('e');
	v.push_back('f');
	v.push_back('g');
	
	// 迭代器
	vector<char>::iterator begin = v.begin();
	while (begin != v.end())
	{
		cout << *begin << ' ';
		begin++;
	}
	cout << endl;

	// 以上用for循环也是可以的
	for (vector<char>::iterator begin = v.begin(); begin != v.end(); begin++)
	{
		cout << *begin << ' ';
	}

	cout << endl;

	return 0;
}

【输出结果】

在这里插入图片描述

4.3 范围for

string类时,我们讲过 范围for的底层是迭代器。

#include <iostream>
#include <vector>
using namespace std;
int main()
{
	vector<char> v;
	// 插入abcdefg
	v.push_back('a');
	v.push_back('b');
	v.push_back('c');
	v.push_back('d');
	v.push_back('e');
	v.push_back('f');
	v.push_back('g');
	
	// 范围for
	for (auto x : v)
	{
		cout << x << ' ';
	}

	cout << endl;

	return 0;
}

【输出结果】

在这里插入图片描述

五、空间增长问题

5.1 size

功能:获取数据的有效个数

#include <iostream>
#include <vector>
using namespace std;

int main()
{
	vector<int> v{ 1,2,3,4 };
	cout << "个数为:" << v.size() << endl;

	for (int i = 0; i < v.size(); i++)
	{
		cout << v[i] << ' ';
	}
	cout << endl;

	return 0;
}

【输出结果】

在这里插入图片描述

5.2 empty

功能:判断容器是否为空。如果为空返回1,否则返回0

#include <iostream>
#include <vector>
using namespace std;

int main()
{
	// 默认构造默认有效个数size为0
	vector<int> vi;
	cout << "vi:" << vi.empty() << endl;

	vector<char> vc{ 'h', 'e', 'l', 'l','o' };
	cout << "vc:" << vc.empty() << endl;

	return 0;
}

【输出结果】

在这里插入图片描述

5.3 resize

【函数原型1】

resize(int num);

功能:重新指定容器的长度为num

情况1:若有效数据size > num,则会保留前num个,剩下的删除

#include <iostream>
#include <vector>
using namespace std;

int main()
{
	vector<int> v{ 1,2,3,4,5,6 };
	cout << "改变之前的长度:" << v.size() << endl;
	
	// 重新指定容器长度
	// 保留前3个
	v.resize(3);
	
	cout << "改变后的长度:" << v.size() << endl;
	cout << "改变后的内容为:";
	for (int i = 0; i < v.size(); i++)
	{
		cout << v[i] << ' ';
	}
	cout << endl;

	return 0;
}

【程序结果】

在这里插入图片描述

【函数原型2】

resize(int num, elem);

情况2:若num > size,则会增加有效长度。如果不指定第二个参数,默认增加的内容是0

#include <iostream>
#include <vector>
using namespace std;

int main()
{
	vector<int> v1{ 1,2,3,4 };
	v1.resize(6);
	// 增加了2个,不指定第二个参数默认是0
	for (auto x : v1)
	{
		cout << x << ' ';
	}
	cout << endl;

	vector<char> v2{ 'h','i' };
	v2.resize(6, 'x');
	for (auto x : v2)
	{
		cout << x << ' ';
	}
	cout << endl;

	return 0;
}

【输出结果】

在这里插入图片描述

5.4 reserve

功能:改变vector容器的容量。一般都是扩容。

#include <iostream>
#include <vector>
using namespace std;

int main()
{
	vector<int> v;
	int capacity = v.capacity();

	for (int i = 0; i < 100; i++)
	{
		// 插入100个数据
		v.push_back(i);
		// v.capacity是输出当前容量的
		if (capacity != v.capacity())
		{
			capacity = v.capacity();
			cout << "容量改变:" << capacity << '\n';
		}
	}
	return 0;
}

【输出结果】

在这里插入图片描述

通过以上代码我们发现:vs下的容量是按1.5倍增长的g++是按2倍增长的。

然后我们再把以上代码拿到Linux环境下测试:

在这里插入图片描述

我们发现:Linux下使用的vector是按照2倍方式扩容。

因此,如果已经确定vector中要存储元素大概个数,可以提前将空间设置足够就可以避免边插入边扩容导致效率低下的问题了。

#include <iostream>
#include <vector>
using namespace std;

int main()
{
	vector<int> v;
	int capacity = v.capacity();

	// 提前开好大小为100的容量
	v.reserve(100);

	for (int i = 0; i < 100; i++)
	{
		// 插入100个数据
		v.push_back(i);
		// v.capacity是输出当前容量的
		if (capacity != v.capacity())
		{
			capacity = v.capacity();
			cout << "容量改变:" << capacity << '\n';
		}
	}
	return 0;
}

【输出结果】

在这里插入图片描述

或者还能这样开空间:

在这里插入图片描述

5.5 swap

【函数原型】

swap(vec); // 将vec与本身的元素互换

【代码示例】

#include <iostream>
#include <vector>
using namespace std;

int main()
{
	vector<int> v1{1,2,3,4,5,6};
	v1.reserve(100);

	cout << "交换前:" << endl;
	cout << "v1:";
	for (auto x : v1)
	{
		cout << x << ' ';
	}
	cout << endl;

	vector<int> v2{ 7,8,9,10,11, 12 };
	cout << "v2:";
	for (auto x : v1)
	{
		cout << x << ' ';
	}
	cout << endl;

	cout << "v1的容量" << v1.capacity() << endl;
	cout << "v2的容量" << v2.capacity() << endl;

	// 交换
	v1.swap(v2);
	cout << "交换后:" << endl;

	cout << "v1:";
	for (auto x : v1)
	{
		cout << x << ' ';
	}

	cout << endl;

	cout << "v2:";
	for (auto x : v1)
	{
		cout << x << ' ';
	}
	cout << endl;

	cout << "v1的容量" << v1.capacity() << endl;
	cout << "v2的容量" << v2.capacity() << endl;

	return 0;
}

【输出结果】

在这里插入图片描述

通过以上发现:swap不仅可以交换容器内容,同时还达到实用的收缩内存效果

六、vector的插入与删除

6.1 push_back - 尾插

#include <iostream>
#include <vector>
using namespace std;

int main()
{
	vector<int> v;
	// 尾插1、2、3、4
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	for (auto x : v)
	{
		cout << x << ' ';
	}
	cout << endl;

	return 0;
}

【输出结果】

在这里插入图片描述

6.2 pop_back - 尾删

#include <iostream>
#include <vector>
using namespace std;

int main()
{
	vector<int> v;
	// 尾插1、2、3、4
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);

	for (auto x : v)
	{
		cout << x << ' ';
	}
	cout << endl;

	// 删掉4
	v.pop_back();

	for (auto x : v)
	{
		cout << x << ' ';
	}
	cout << endl;

	return 0;
}

【输出结果】

在这里插入图片描述

6.3 insert - 插入

【函数原型1】

iterator insert (iterator position, const value_type& val);

注意:insert是要配合迭代器使用的!

#include <iostream>
#include <vector>
using namespace std;

int main()
{
	vector<int> v;
	// 尾插1、2、3、4
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);

	for (auto x : v)
	{
		cout << x << ' ';
	}
	cout << endl;

	// 头插一个6
	v.insert(v.begin(), 6);

	for (auto x : v)
	{
		cout << x << ' ';
	}
	cout << endl;

	return 0;
}

【输出结果】

在这里插入图片描述

【函数原型2】

void insert (iterator position, size_type n, const value_type& val);

【代码样例】

#include <iostream>
#include <vector>
using namespace std;

int main()
{
	vector<int> v;
	// 尾插1、2、3、4
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);

	for (auto x : v)
	{
		cout << x << ' ';
	}
	cout << endl;

	// 尾插4个6
	v.insert(v.end(), 4, 6);

	for (auto x : v)
	{
		cout << x << ' ';
	}
	cout << endl;

	return 0;
}

【输出结果】

在这里插入图片描述

6.4 erase - 删除pos位置的数据

【函数原型1】

erase(const iterator pos);

注意:erase也是要配合迭代器使用的

#include <iostream>
#include <vector>
using namespace std;

int main()
{
	vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);

	// 删除最后一个数据
	v.erase(v.end() - 1);
	for (auto x : v)
	{
		cout << x << ' ';
	}
	cout << endl;

	return 0;
}

【输出结果】

在这里插入图片描述

【函数原型2】

erase(const iterator start,const iterator end)

功能:删除某个区间的数据

【代码示例】

#include <iostream>
#include <vector>
using namespace std;

int main()
{
	vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);

	// 清空数据
	v.erase(v.begin(), v.end());

	for (auto x : v)
	{
		cout << x << ' ';
	}
	cout << endl;
	return 0;
}

【输出结果】

在这里插入图片描述

除了以上方式可以清空数据,clear同样也行

6.5 clear - 清空所有数据

#include <iostream>
#include <vector>
using namespace std;

int main()
{
	vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);

	v.clear();

	for (auto x : v)
	{
		cout << x << ' ';
	}
	cout << endl;
	return 0;
}

【输出结果】

在这里插入图片描述

七、几个常用算法

注意:使用库里的算法需要加上头文件#include <algorithm>

7.1 sort - 排序

#include <iostream>
#include <vector>
using namespace std;
#include<algorithm>

int main()
{
	vector<int> a{ 4,7,1,0,5,3, 2 };
	
	// 从小到大
	sort(a.begin(), a.end());
	for (auto x : a)
	{
		cout << x << ' ';
	}
	cout << endl;
	
	// 从大到小
	sort(a.rbegin(), a.rend());
	for (auto x : a)
	{
		cout << x << ' ';
	}
	cout << endl;
	
	return 0;
}

【输出结果】

在这里插入图片描述

7.2 reverse

#include <iostream>
#include <vector>
using namespace std;
#include<algorithm>

int main()
{
	vector<int> a{ 1,2,3,4,5,6 };
	
	//逆置reverse
	reverse(a.begin(), a.end());
	for (auto x : a)
	{
		cout << x << ' ';
	}
	cout << endl;

	return 0;
}

【输出结果】

在这里插入图片描述

7.3 find

注意:vector是没有提供find接口的。而我们知道vector是一个类似于数组的容器,因此如果想找一个数据,直接遍历即可。但是算法库提供了find

#include <iostream>
#include <vector>
using namespace std;
#include<algorithm>

int main()
{
	vector<int> a{ 1,2,3,4,5,6 };

	// 查找4
	vector<int>::iterator pos = find(a.begin(), a.end(), 4);
	if (pos != a.end())
	{
		// 找到4就删掉
		a.erase(pos);
	}
	for (auto x : a)
	{
		cout << x << ' ';
	}
	cout << endl;
	
	return 0;
}

【输出结果】

在这里插入图片描述

八、迭代器失效问题

8.1 什么是迭代器失效

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

8.2 为什么string不存在迭代器失效问题

string是一个特殊的容器,它是由字符组成的连续序列,类似于C语言的字符串。string类会动态地管理内部存储区域,确保足够的容量容纳字符串。当我们向string中插入或删除字符时,并不会导致整个字符串被复制到新的内存位置,因此迭代器不会失效。

对比vector,它是一个动态数组,它使用连续的内存存储元素。当我们向vector中插入元素时,如果导致当前内存不足以容纳所有元素,vector会重新分配更大的内存空间,并将所有元素复制到新的内存中。这样一来,原来指向旧内存中的元素的迭代器就会失效,因为它们指向的位置已经改变了。

8.3 几个常见的迭代器失效样例

当涉及到插入或删除操作时,我们需要注意vector迭代器的失效问题

  • 会引起其底层空间改变的操作,都有可能是迭代器失效,比如:resizereserveinsertpush_back
#include <iostream>
#include <vector>
using namespace std;

int main()
{
	vector<int> v{ 1, 2, 3 };
	
	// 1. 将有效元素个数增加到100个
	//   多出的位置使用6填充,操作期间底层会扩容
	v.resize(100, 6);
	
	// 2. reserve的作用就是改变扩容大小但不改变有效元素个数,
	// 操作期间可能会引起底层容量改变
	v.reserve(100);
	
	// 3. insert和尾插期间
	// 可能会引起扩容,而导致原空间被释放
	v.insert(v.begin(), 0);
	v.push_back(10);
	
	return 0;
}

以上操作可能会导致vector扩容,扩容就会导致旧空间被释放掉,而返回的迭代器是指向被释放的空间,如果再对迭代器进行使用,会引起代码崩溃。

  • 指定位置元素的删除操作
#include <iostream>
#include <vector>
using namespace std;

int main()
{
	int a[] = { 1, 2, 3, 4, 5 };
	vector<int> v(a, a + sizeof(a) / sizeof(int));

	// 使用find查找4所在位置
	auto pos = find(v.begin(), v.end(), 4);
	// 删除pos位置的数据,
	v.erase(pos);
	// 预测打印5
	cout << *pos << endl;

	return 0;
}

【输出结果】

理论上删除了4*pos应该是5,可是为什么没有打印出来呢?

我们可以通过调试来观察:

在这里插入图片描述

当我再按F10,发现pos的地址变了:

在这里插入图片描述

理论上删除4后,5应该占据4的空间,然而地址却变了。因此导致了迭代器失效了。

那如果是删除最后一个数据,那么结果更加明显:

在这里插入图片描述

8.4 如何解决迭代器失效问题

在使用前,对迭代器重新赋值即可。

#include <vector>
#include <iostream>
using namespace std;
int main()
{
	vector<int> v{ 1,2,3,4,5,6,7,8,9,10};

	// 保留1 2 3
	// 剩下全删除
	vector<int>::iterator it = v.begin() + 3;

	while (it != v.end())
	{
		// 在使用之前重新赋值
		it = v.erase(it);
	}
	
	for (auto& x : v)
	{
		cout << x << ' ';
	}
	cout << endl;

	return 0;
}

在这里插入图片描述

【输出结果】

在这里插入图片描述

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

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

相关文章

0007Java程序设计-jsp问卷调查系统设计与实现

摘 要 随着社会不断进步与发展&#xff0c;生活节奏不断加快&#xff0c;信息已经成为我们生活中不可缺少的一部分&#xff0c;很多企业需要掌握大量的信息来了解特定用户的需求&#xff0c;传统的做法是组织大量的人力物力对用户散发调查表&#xff0c;然后对收集的信息进行统…

python 基础篇 day 1 初识变量和数据类型

文章目录 变量变量作用——用于存储和表示数据。变量命名规则命名法大驼峰小驼峰下划体n j i a x 通常作为临时变量使用 建议 变量种类全局变量&#xff08;Global Variables&#xff09;局部变量&#xff08;Local Variables&#xff09;静态变量&#xff08;Static Variables…

linux常用基础命令与文件结构汇总

1 学习目标 说出Linux下的目录结构和常见目录的作用熟练使用Linux下的相对路径和绝对路径熟练使用Linux下常用文件和目录操作相关的命令熟练使用修改用户权限、用户和用户组相关的命令熟练使用文件的查找和检索相关的命令熟练掌握Ubuntu下的软件安装和卸载熟练使用压缩工具完成…

七夕节日表白:七大网页风格与其适用人群

&#x1f337;&#x1f341; 博主猫头虎 带您 Go to New World.✨&#x1f341; &#x1f984; 博客首页——猫头虎的博客&#x1f390; &#x1f433;《面试题大全专栏》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33a; &a…

YOLOv5源码中的参数超详细解析(5)— 验证部分val(test).py

前言&#xff1a;Hello大家好&#xff0c;我是小哥谈。YOLOv5项目代码中&#xff0c;val.py 是一个代表验证&#xff08;validation&#xff09;的 Python 脚本文件名。通常在机器学习或深度学习的任务中&#xff0c;我们会将数据集分为训练集和验证集&#xff0c;使用训练集来…

请说人话!如何理解基本分页存储管理

一、默认设定 &#xff08;一&#xff09;按字节编制一个房间可以装8只猪猪 现在的计算机一般都是按字节编址的。这个不理解的话&#xff0c;可以看我的文章为什么20位地址总线决定寻址空间是1MB“http://t.csdn.cn/Eo2nE” &#xff08;二&#xff09;内存采用非连续分配方…

法线矩阵推导

法线矩阵推导 https://zhuanlan.zhihu.com/p/72734738 https://juejin.cn/post/7113952418613690382 https://blog.csdn.net/wangjianxin97?typeblog 1、为什么需要法线矩阵 vec3 normalEyeSpace modelViewMatrix * normal;如果模型矩阵执行了非等比缩放, 顶点的改变会导致法…

基于蝴蝶算法优化的BP神经网络(预测应用) - 附代码

基于蝴蝶算法优化的BP神经网络&#xff08;预测应用&#xff09; - 附代码 文章目录 基于蝴蝶算法优化的BP神经网络&#xff08;预测应用&#xff09; - 附代码1.数据介绍2.蝴蝶优化BP神经网络2.1 BP神经网络参数设置2.2 蝴蝶算法应用 4.测试结果&#xff1a;5.Matlab代码 摘要…

sNMFcross-entropyK

0.简单介绍 稀疏非负矩阵&#xff08;sNMF&#xff09;和最小二乘优化来产生祖先比例估计数的祖先推断算法&#xff0c;这个算法呢与admixture来说差别不是很大&#xff0c;但是优点就是快&#xff0c;运算速度可以快到10-30倍左右。 1.安装 这一步不必多说&#xff0c;下载…

tailscale使用教程(远程连接服务器)

tailscale&#xff1a;将多个设备放在同一局域网下&#xff0c;实现异地组网。 首先进入tailscale官网&#xff0c;根据系统需求进行下载 需要远程的设备和被远程的设备都需要下载。 然后两个设备均登录同一账号即可 注&#xff1a;这里重点讲一下linux操作系统上的操作&…

PID输出PWM温度控制(PID输出PWM的各种方法介绍)

这篇博客主要介绍PID的输出如何和PWM输出进行绑定,PID控制算法和源代码大家自行查看PID专栏,这里不再赘述。常用链接如下: 位置式PID(S7-200SMART 单自由度、双自由度梯形图源代码)_RXXW_Dor的博客-CSDN博客有关位置型PID和增量型PID的更多详细介绍请参看PID专栏的相关文章…

读SQL学习指南(第3版)笔记02_数据类型

1. 命令行工具 1.1. mysql -u root -p; 1.2. mysql&#xff1e; show databases; 1.3. mysql&#xff1e; use sakila; 1.4. mysql&#xff1e; SELECT now(); 1.4.1. now()是MySQL的内建函数 1.4.2. 返回当前日期和时间 1.5. mysql&#xff1e; SELECT now() FROM dual…

DFT计算入门新手坑:能带不连续

新手在学习DFT计算时&#xff0c;在熟悉了基本的操作和VASP输入文件后&#xff0c;首先就会学习到结构优化、自洽计算和能带的计算。 而笔者学习DFT计算这些年来看到太多新手学者踩到大大小小的坑&#xff0c;其中能带看起来不连续或者能带不连续则是几乎必踩的坑之一。 这些初…

【HCIP】04.VRRP与BFD

VRRP VRRP基本概念 VRRP路由器 运行VRRP协议的路由器&#xff0c;VRRP是配置在路由器的接口上的&#xff0c;而且也是基于接口来工作的。 VRID 一个VRRP组由多台协同工作的路由器&#xff08;的接口&#xff09;组成&#xff0c;使用相同的VRID&#xff08;Virtual Router…

Spring统一功能处理

1. AOP存在的问题 获取参数复杂AOP的规则相对简单 2. 拦截器 2.1. 应用(以登录为例) 2.1.1. 自定义拦截器 新建interceptor文件夹 import org.springframework.web.servlet.HandlerInterceptor;import javax.servlet.http.HttpServletRequest; import javax.servlet.http…

Blend for Visual Studio:提升用户界面设计的专业工具

随着软件行业的迅速发展&#xff0c;用户界面设计变得越来越重要。一个好的用户界面能够吸引用户的注意力&#xff0c;提供良好的用户体验&#xff0c;并增加应用程序的成功率。在这个背景下&#xff0c;Blend for Visual Studio作为一款专业的用户界面设计工具&#xff0c;为开…

SpringCloud学习笔记(二)_Eureka注册中心

一、Eureka简介 Eureka是一项基于REST&#xff08;代表性状态转移&#xff09;的服务&#xff0c;主要在AWS云中用于定位服务&#xff0c;以实现负载均衡和中间层服务器的故障转移。我们称此服务为Eureka Server。Eureka还带有一个基于Java的客户端组件Eureka Client&#xff…

从电子表格到纸张:Excel转PDF的神奇变身之旅!

当你需要将Excel文件转换为PDF时&#xff0c;可以使用Python编程语言和一些流行的库来实现这个任务。在本篇博客中&#xff0c;我将介绍如何使用wxPython、pandas和PyMuPDF库创建一个简单易用的图形用户界面(GUI)工具来完成这项工作。 C:\pythoncode\new\excelexportpdf.py …

SCCB与IIC的异同及FPGA实现的注意事项

文章目录 前言一、信号线二、SCCB数据传输格式三、SCCB写&#xff08;与IIC完全一致&#xff09;四、SCCB读五、SCCB和IIC的区别 前言 IIC接口有比较广泛的应用&#xff0c;而SCCB&#xff08;Serial Camera Control Bus&#xff0c;串行摄像头控制总线&#xff09;是由OV&…

发现一款免费WEB在线使用的AI对话+绘画

这是一个优秀的golang开发作者 免费开放给大家使用的 简单上手 注册就能使用 多个AI角色多模型自由选择 下面是使用效果 链接地址在文末链接地址:目前免费体验