【C++第三阶段】Set Map容器 员工分组案例

以下内容仅为当前认识,可能有不足之处,欢迎讨论!


文章目录

  • Set容器
    • 构造和赋值
    • 大小和交换
    • 插入和删除
      • 一次性迭代器(可能迅速失效的迭代器)
      • 长久保留的迭代器
      • 如何判断迭代器是否一次性
    • 查找和统计
    • set和multiset的区别
    • pari对组的创建
    • 内置类型指定排序规则
    • 自定义数据类型指定排序规则
  • Map容器
    • 概念构造和赋值
    • 大小和交换
    • 插入和删除
    • 查找和统计
    • 排序
  • 员工分组


Set容器

集合容器,元素放入其中会被排序。

set/multiset ∈ \in 关联式容器,底层结构用二叉树实现。

set V S VS VS multiset区别:

  • set不允许容器中有重复的元素
  • multiset允许容器中有重复的元素

构造和赋值

2种构造方式

目的方式
默认构造函数set<T> st;
拷贝构造函数set(const set &st);

一种赋值方式——重载等号操作符

set & operator=(const set &st);返回值set类型,等号重写。

示例代码:

void test0429a() {
	set<int> st;
	//由于set容器没有先后顺序,所以直接insert插入即可
	st.insert(10);
	st.insert(40);
	st.insert(20);
	st.insert(10);
	st.insert(30);

	cout << "st的元素有↓" << endl;
	//看起来插入了5个元素,实际上因为set集合容器的特性,所以只有4个。
	print(st);
	cout << endl;

	cout << "拷贝构造方式后,st2的元素有↓" << endl;
	//拷贝构造方式
	set<int> st2(st);
	print(st2);
	cout << endl;
	
	cout << "等号赋值操作后,st3的元素有↓" << endl;
	//=号赋值构造方式
	set<int> st3 = st2;
	print(st2);
	cout << endl;
}

打印函数说明:

template <typename T>
void print(const set<T>& st) {
	for (typename set<T>::iterator st_begin = st.begin(); st_begin != st.end(); st_begin++) {
		cout << *st_begin;
		cout << " ";
	}
	cout << endl;
}

运行结果:

image-20240429161546086

大小和交换

目的:统计set容器元素个数(原文说统计set容器大小),以及交换set容器。

目的函数
容器中元素个数size();
判断容器是否为空empty();
交换两个set容器swap();

示例代码:

void test0429b() {
	set<int> st;
	set<int> ts;

	st.insert(10 );
	st.insert(40 );
	st.insert(20 );
	st.insert(30 );

	ts.insert(1);
	ts.insert(5);
	ts.insert(2);
	ts.insert(3);
	ts.insert(4);

	cout << "st的元素如下↓" << endl;
	print(st);

	cout << "ts的元素如下↓" << endl;
	print(ts);

	cout << endl;

	cout << "st的元素个数:" << st.size() << "." << endl;
	cout << "ts的元素个数:" << ts.size() << "." << endl;

	cout << "st是空容器吗?1为是,0为否。\t" <<st.empty()<< endl;
	cout << "ts是空容器吗?1为是,0为否。\t" <<ts.empty()<< endl;
	cout << endl;
	
	cout << "交换st和ts两个容器" << endl;
	cout << "交换前,st=";
	print(st);
	cout << "交换前,ts=";
	print(ts);
	cout << endl;

	st.swap(ts);

	cout << "交换后,st=";
	print(st);
	cout << "交换后,ts=";
	print(ts);
	cout << endl;
}

运行结果:

image-20240429165005296

插入和删除

目的:set容器进行插入数据和删除数据

目的函数
容器中插入元素insert(elem);
删除position迭代器所指的元素,返回下一个元素的迭代器erase(pos);
删除区间[begin , end)的所有元素,返回下一个元素的迭代器erase(begin , end);
删除容器中值为elem的元素(只能删除一个,因为不重复)erase(elem);
清除所有元素clear();

示例代码:

void test0429c() {
	//容器中插入一定数量的元素后,删除第二个元素。
	//删除第三个元素到倒数第二个元素。
	//删除第一个元素,此时指定第一个元素值为10。
	//清空所有元素,并判断容器是否为空。

	set<int> st;
	st.insert(-100);
	for (int i = 0; i < 9; i++) {
		st.insert(i * i  - 2 + i/2);
	}
	cout << "st = ";
	print(st);
	cout << endl;

	set<int>::iterator st_ptr = st.begin();

	//删除第二个元素
	advance(st_ptr, 1);
	st.erase(st_ptr);
	cout << "删除第二个元素后,st = ";
	print(st);
	cout << endl;

	//删除第三个元素到倒数第二个元素区间
	set<int>::iterator st_ptr2 = st.begin();
	advance(st_ptr2, 2);
	st.erase(st_ptr2, --(st.end()));
	cout << "删除第三个元素—倒数第二个元素区间后,st = ";
	print(st);
	cout << endl;

	//由于事先指定第一个元素为-100,所以删除-100。
	st.erase(-100);
	cout << "删除-100后,st = ";
	print(st);
	cout << endl;

	//清空st容器
	st.clear();
	cout << "清空st容器后,此时st为空吗?1为是,0为否==》" << st.empty() << "." << endl;
	cout << endl;
}

运行结果:

根据GPT的回答以及黑马老师的讲解。回答该问题:C++ 中提供了容器这一特性,容器中有迭代器。什么容器的迭代器是一次性的?什么容器的迭代器是长久保留的?如何判断容器的迭代器是一次性的还是长久保留的?

黑马老师回答:

不会造成原有迭代器的失效,在vector中是不允许的。

为什么?因为vector,deque,stack,string是连续的内存空间。而list,set,multiset,map容器不是连续的内存空间。迭代器指向的是地址,由于是链表结构,其数据包含指针域—地址。所以不会失效。

对于同样的问题,询问GPT后:

在C++标准库中,容器的迭代器可以大致分为两类:那些在容器被修改后仍然保持有效的迭代器,和那些在特定修改操作后可能失效的迭代器。这两类迭代器的行为依赖于它们所属的容器类型。

一次性迭代器(可能迅速失效的迭代器)

  1. 向量(vector

    • 当向vector中添加或删除元素时,特别是当涉及到动态内存重新分配以增加容量时,迭代器可能会失效。例如,使用push_back()可能导致容器大小超过当前容量,从而触发重新分配内存,这将使所有现有迭代器失效。
  2. 字符串(string

    • vector相似,string的迭代器在修改字符串内容或长度时(如添加更多字符导致内存重新分配)也可能失效。
  3. 双端队列(deque

    • 对于deque,在队列的首部或尾部添加或删除元素不会影响其他元素的迭代器,但如果在deque的中间插入或删除元素,则可能导致所有迭代器失效。

长久保留的迭代器

  1. 列表(list

    • list的迭代器在添加或删除元素时都保持有效,因为list通常实现为双向链表,各元素之间通过指针连接,不依赖于连续的内存空间。
  2. 集合(set)、多重集合(multiset

    • 这些基于红黑树的容器允许迭代器在元素插入或删除时保持有效,除非是指向被删除元素的迭代器。
  3. 映射(map)、多重映射(multimap

    • setmultiset相似,这些容器也是基于树的结构,其迭代器在修改操作后依然有效,除非操作直接涉及到迭代器当前指向的那个元素。

如何判断迭代器是否一次性

要确定一个容器的迭代器是否是一次性的,你可以参考下面的准则:

  1. 内部结构:了解容器的内部实现。例如,依赖于连续内存空间的容器(如vectorstring)更可能在扩容时使迭代器失效。

  2. 操作类型:某些操作(如resize(), push_back()等)可能导致内存重新分配,特别是在vectorstring中。了解特定的操作是否影响容器的内部存储结构。

  3. 官方文档:查阅C++标准库的文档,了解不同类型的容器和它们的迭代器行为。这是最可靠的方式。

理解和遵守这些规则可以帮助你编写出更安全、更健壮的C++代码,尤其是在涉及复杂容器操作时。

但实际使用时,仍然是一次性的迭代器,所以,我新建一个。

结果:

image-20240429173824696

查找和统计

目的:

查找某个元素在set容器中的位置。返回其迭代器,如果找不到,则返回最后一个元素。

示例代码:

void findElement(const set<int>& st, int elem) {
	set<int>::iterator result = st.find(elem);
	if (result != st.end()) {
		cout << "哦耶,"<<elem<<"在st中找到啦!" << endl;
		return;
		//return result;
	}
	cout << "芜湖," << elem << "没在st中找到……" << endl;
	return;
	//return result;
}

void test0429d() {
	set<int> st;
	for (int i = 0; i < 5; i++) {
		st.insert(i + 1);
	}
	//查找某个元素
	//查找1
	findElement(st, 1);
	//查找100
	findElement(st, 100);

	//统计元素个数
	cout << "2在st中总共出现了" << st.count(2) << "次。" << endl;
}

运行结果:

image-20240429181743441

set和multiset的区别

区别:

区别setmultiset
是否可以插入重复数据?不可以可以
插入数据是否返回结果?插入返回结果,表示插入是否成功不会检测数据,因此可以插入重复数据

set插入数据会返回插入结果,第一个是迭代器,第二个是bool值,返回是否插入成功的bool,而multiset则不会。

查看源码:

image-20240429231238681

void test0429e() {
	set<int> st;
	for (int i = 0; i < 5; i++) {
		st.insert(i * 10);
	}
	cout << "st当前的元素为:";
	print(st);

	//set的插入返回一个pair,包括一个迭代器,以及bool结果。
	pair<set<int>::iterator, bool> st_result = st.insert(20);
	if (st_result.second) {
		cout << "20插入成功." << endl;
	}
	else {
		cout << "20插入失败." << endl;
	}
	multiset<int> mst;

	for (int i = 0; i < 5; i++) {
		mst.insert(i * 10);
		mst.insert(i * 10);
	}
	cout << "mst当前的元素为:";
	print(mst);
}

执行结果:

image-20240429231453086

pari对组的创建

目的,将两个数据进行打包提取,作为一对。是否可以作为映射?

目的函数
构建方式1,构造函数创建pair<type_name1 , type_name2> pair_name (type_value1 , type_value2);
构建方式2,方法创建pair<type_name1 , type_name2> pair_name = make_pair(type_value1,type_value2);

示例代码:

void test0430a() {
	pair<string, int> Jack("Jack", 30);
	cout << Jack.first << "年龄为" << Jack.second << "." << endl;
	cout << endl;

	pair<string, int > Ross;
	Ross = make_pair("Ross", 28);
	cout << Ross.first << "年龄为" << Ross.second << "." << endl;
	cout << endl;

}

运行结果:

image-20240430095821588

内置类型指定排序规则

set容器排序怎么自定义?==》掌握如何改变排序规则。

技术点:利用仿函数。

与之前list的排序不同,仿函数在此时是类里面对()重载。

代码示例:

class MyCompare {
public:
	bool operator()(int it1, int it2){
		return it1 > it2;
	}
};

template <typename T>
void print(set<T,MyCompare>& st) {
	for (typename set<T, MyCompare>::iterator st_begin = st.begin(); st_begin != st.end(); st_begin++) {
		cout << *st_begin;
		cout << " ";
	}
	cout << endl;
}
void test0430b(){
	//内置数据类型的自定义排序
	set<int> st;
	for (int i = 0; i < 5; i++) {
		st.insert(i * i);
	}
	cout << "默认排序后,st为↓" << endl;
	print(st);

	cout << "创建一个元素和st一致的ts" << endl;
	set<int, MyCompare> ts;
	for (int i = 0; i < 5; i++) {
		ts.insert(i * i);
	}
	cout << "自定义排序后,ts为↓" << endl;
	print(ts);
	
}

运行结果:

image-20240430102544543

原先代码中,出现问题,在compare中没有对operator添加const属性,报错:C3848 具有类型“const MyCompare”的表达式会丢失一些 const-volatile 限定符以调用“bool MyCompare::operator ()(int,int)”,直接搜索CSDN后得到:博客园参考链接。原因是,因为std::not1用的是const Predicate&,所以我的predicate到not1之中以后就是const Predicate&了,所以在调用operator()的时候要求operator()也具有const属性,否则就会导致丢失const限定符的错误,因为我是重写了操作运算符(),需要加上const属性。

自定义数据类型指定排序规则

首先得重写对应数据类型的打印方法。其次得在重写的打印方法中对person也加上const修饰。

class Person {
public:
	Person(string name, int age):person_name(name),person_age(age){}
public:
	string person_name;
	int person_age;
};
class PersonCompare {
public:
	bool operator()(const Person& per, const Person& son) const {
		return per.person_age > son.person_age;
	}
};

ostream& operator<<(ostream& out, const Person& person) {
	out << person.person_name << "年龄为:" << person.person_age << "." << endl;
	return out;
}

template <typename T,typename C>
void print(const set<T,C>& st) {
	for (typename set<T, C>::iterator st_begin = st.begin(); st_begin != st.end(); st_begin++) {
		//Person* temp = st_begin;
		cout << *st_begin << endl;
		//cout << " ";
	}
	cout << endl;
}

void test0430c() {
	//自定义数据类型的自定义排序
	set<Person,PersonCompare> person_st;
	Person p("张三", 23);
	Person e("李四", 24);
	Person r("王五", 25);
	Person s("赵六", 16);
	Person o("侯七", 17);
	Person n("周八", 18);
	person_st.insert(p);
	person_st.insert(e);
	person_st.insert(r);
	person_st.insert(s);
	person_st.insert(o);
	person_st.insert(n);

	cout << "person_st为" << endl;
	print(person_st);
}

运行结果:

image-20240430105902715

Map容器

概念构造和赋值

map相当于映射,容器内的每一个元素有其键值对。每一个元素都是pair元素,因为它是有键值对的。

和set一致,map本身是按照键来看的,所以map不能有重复的key值,value值无所谓。

但multimap就不同,它允许出现重复key值。

简介:map中所有元素都是pair,第一个元素为key,起到索引作用,第二个元素为value。所有元素会根据元素的键值自动排序。

本质:map/multimap属于关联式容器,底层结构用二叉树实现。

优点:可以根据key值快速找到value值。

目的函数
默认构造函数map<T1,T2> mp;
拷贝构造函数map(const map &mp);
赋值重载等号运算符map& operator=(const map &mp);

示例代码:

void print(map<int, string> &mp) {
	for (map<int, string>::iterator mp_elem = mp.begin(); mp_elem != mp.end(); mp_elem++) {
		cout << mp_elem->first<<"值为"<<mp_elem->second<<".";
		cout << endl;
	}
	cout << endl;
}

void test0430a() {
	map<int,string> mp;
	for (int i = 0; i < 5; i++) {
		mp.insert(pair<int, string>(i, to_string(i * i)));
	}
	cout << "mp为↓" << endl;
	print(mp);

	map<int, string> mp1(mp);
	cout << "拷贝构造时,mp1为↓" << endl;
	print(mp);

	map<int, string> mp2;
	mp2 = mp;
	cout << "mp等号赋值时,mp2↓" << endl;
	print(mp2);

}

运行结果:

image-20240430144148972

大小和交换

目的:统计map容器大小以及交换map容器

目的函数
返回容器中元素个数
判断容器是否为空
交换两个容器

如果两者容器内元素数据类型不同,是无法交换的。pair必须相同才可以。

示例代码:

运行结果:

image-20240430150907448

问题:此时我想用模板打印,但是没有实现成功。

插入和删除

目的:对map容器插入数据或删除数据。

目的函数
容器中插入元素
清空所有元素
删除迭代器所指元素,返回下一个元素的迭代器。
删除区间的所有元素,返回下一个元素的迭代器。
删除容器中键为key的元素。此时,只能按照键来删除,不能按照值来删除。

注意,此时insert有三种方式,除了pair<type a , type b>(value1 , value2);还有make_pair(value1,value2);以及map<typea , typeb >::value_type(value1 , value2);

最后一种方式不建议使用,因为如果键不对,或自动有一个默认数据加入其中。

示例代码:

void test0430c() {
	map<int, string> mp;
	//第一种插入方式
	mp.insert(pair<int, string>(1, to_string(1)));
	mp.insert(pair<int, string>(5, to_string(5)));

	//第二种插入方式
	mp.insert(make_pair(2, to_string(2)));
	mp.insert(make_pair(6, to_string(6)));

	//第三种插入方式
	mp.insert(map<int, string>::value_type(3, to_string(3)));
	mp.insert(map<int, string>::value_type(7, to_string(7)));

	//第四种插入方式
	mp[4] = to_string(4);

	print(mp);

	cout << "删除第二个元素" << endl;
	mp.erase(++mp.begin());
	print(mp);

	cout << "删除第三个元素到倒数第二个元素" << endl;
	mp.erase(++(++mp.begin()), --mp.end());
	print(mp);

	cout << "删除键为1的元素" << endl;
	mp.erase(1);
	print(mp);

	cout << "清空mp" << endl;
	mp.clear();
	cout << "此时mp是否为空?1为是,0为否==》" << mp.empty() << "." << endl;
}

运行结果:

image-20240430152933080

查找和统计

目的:对map容器中的内容,进行查找数据以及统计数据。

目的函数
查找key是否存在,如果存在,返回该键元素的迭代器,如果不存在,返回set.end()find(key);
统计key元素个数count(key);

示例代码:

void test0430d() {
	map<int, string> mp;
	for (int i = 0; i < 5; i++) {
		mp.insert(pair<int, string>(i, to_string(i)));
	}
	cout << "此时mp=" << endl;
	print(mp);

	map<int, string>::iterator result = mp.find(10);
	if (result != mp.end()) {
		cout << "找到了!" << endl;
	}
	else {
		cout << "没找到……" << endl;
	}

	cout << "mp的2个数有:" << mp.count(2) << "." << endl;
}

运行结果:

image-20240430154036005

排序

按照人员年龄来排序,要记住,所有的排序都是按照键来排序,所以要想对年龄排序,键需要是Person类。

代码示例:

class MyCompare {
public:
	bool operator()(int pa, int ir) const{
		return pa > ir;
	}
};

class Person {
public:
	Person() {
		this->person_age = 0;
		this->person_name = "";
	}
	Person(string name, int age) :person_name(name), person_age(age) {}
public:
	string person_name;
	int person_age;
};

class PersonCompare {
public:
	bool operator()(Person per, Person son) const{
		return per.person_age > son.person_age;
	}
};

void print(map<int, string,MyCompare>& mp) {
	for (map<int, string, MyCompare>::iterator mp_elem = mp.begin(); mp_elem != mp.end(); mp_elem++) {
		cout << mp_elem->first << "值为" << mp_elem->second << ".";
		cout << endl;
	}
	cout << endl;
}

ostream& operator<<(ostream& out,const Person& person) {
	cout << person.person_name << "\t年龄:" << person.person_age << "." << endl;
	return out;
}

void print(map< Person,int, PersonCompare>& mp) {
	for (map< Person,int, PersonCompare>::iterator mp_elem = mp.begin(); mp_elem != mp.end(); mp_elem++) {
		cout << "编号:" << mp_elem->second << endl;
		cout << mp_elem->first << endl;
		//cout << endl;
	}
	cout << endl;
}
void test0430e() {
	map<int, string,MyCompare> mp;
	for (int i = -3; i < 4; i++) {
		mp.insert(pair<int, string>(i * i * i, to_string(i)));
	}
	print(mp);

}

void test0430f() {
	map< Person,int, PersonCompare> person_mp;
	person_mp.insert(pair< Person,int>( Person("张三", 18),1));
	person_mp.insert(pair< Person,int>( Person("李四", 24),2));
	person_mp.insert(pair< Person,int>( Person("王五", 19),3));
	person_mp.insert(pair< Person,int>( Person("赵六", 20),4));
	person_mp.insert(pair< Person,int>( Person("郑八", 17),5));

	print(person_mp);
}

运行结果:

内置数据类型排序:

image-20240430160435332

自定义数据类型排序:

image-20240430160408727

员工分组

案例代码:

#include<iostream>
#include<string>
using namespace std;
#include<map>
#include<vector>
#define ART 0
#define PLAN 1
#define RD 2
#include<ctime>

/*
* 公司招聘了10个员工,进入公司后,需要指派公司在哪个部门工作。员工信息有:姓名,工资组成;部门分为:策划,美术,研发
* 随机给10名员工分配部门和工资。
* 通过multimap进行信息插入 ,key-部门编号,value-员工
* 分部门显示员工信息。 
* 
* 创建10名员工,放入vector中。
* 遍历vector,取出每个员工,进行随机分组。
* 分组后,员工部门编号作为key,具体员工作为value,放入到multimap容器中
* 分部门显示员工信息。
* 
* ---
* 
* √,完成——员工,用类表示。
* 随机分组?怎么随机分组?使用随机种子。
* 
*/

//员工类信息
class Staff {
public:
	Staff() {};
	Staff(string name, int salary) {
		//cout << "员工姓名:" << name << "\t, 员工薪资:" << salary << " ." << endl;
		set_staff_name(name);
		set_staff_salary(salary);
	};
	//员工姓名和工资不可以公开。
public:
	void set_staff_name(string name){
		this->staff_name = name;
	}
	string get_staff_name() {
		return this->staff_name;
	}
	void set_staff_salary(int salary) {
		this->staff_salary = salary;
	}
	int get_staff_salary() {
		return this->staff_salary;
	}
private:
	string staff_name = "????";
	int staff_salary=-1;
};

vector<Staff> setStaffVector(vector <Staff>& vt) {
	string staffs = "ABCDEFGHIJ";
	for (int i = 0; i < staffs.size(); i++) {
		int salary = rand() % 10000 + 10000;
		string name = "员工";
		name += staffs[i];
		vt.push_back(Staff(name, salary));
	}
	return vt;
}

//给员工分配不同的部门,其中每个部门的人数不确定。
//对于每一个员工,分配其0-2的部门。

multimap<int,Staff> setStaffDepartment(vector <Staff>& vt) {
	multimap<int, Staff> mtm;
	for (vector<Staff>::iterator staff = vt.begin(); staff != vt.end(); staff++) {
		int partment_number = rand() % 3;
		mtm.insert(make_pair(partment_number, *staff));
	}
	return mtm;
}

void printStaff(multimap<int, Staff> mtm) {
	for (multimap<int, Staff>::iterator staff = mtm.begin(); staff != mtm.end(); staff++) {
		int result = (*staff).first;
		switch (result) {
		case ART:
			cout << "部门:美术\t" << "员工姓名:" << (*staff).second.get_staff_name() << "\t员工薪资:" << (*staff).second.get_staff_salary() << "." << endl;
			break;
		case PLAN:
			cout << "部门:策划\t" << "员工姓名:" << (*staff).second.get_staff_name() << "\t员工薪资:" << (*staff).second.get_staff_salary() << "." << endl;
			break;
		case RD:
			cout << "部门:研发\t" << "员工姓名:" << (*staff).second.get_staff_name() << "\t员工薪资:" << (*staff).second.get_staff_salary() << "." << endl;
			break;
		default:
			cout << "部门:????\t" << "员工姓名:????\t员工薪资:????." << endl;
			break;
		}
	}
}

void test0506() {
	srand((unsigned int)time(NULL));
	vector <Staff> vt;
	vt = setStaffVector(vt);
	multimap<int, Staff> mtm = setStaffDepartment(vt);
	//创建员工时,可以按照字符串形式创建。
	printStaff(mtm);
}

int main() {
	test0506();
	system("pause");
	return 0;
}

运行结果:

image-20240506152856581


以上是我的学习笔记,希望对你有所帮助!
如有不当之处欢迎指出!谢谢!

学吧,学无止境,太深了

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【notes2】并发,IO,内存

文章目录 1.线程/协程/异步&#xff1a;并发对应硬件资源是cpu&#xff0c;线程是操作系统如何利用cpu资源的一种抽象2.并发&#xff1a;cpu&#xff0c;线程2.1 可见性&#xff1a;volatile2.2 原子性&#xff08;读写原子&#xff09;&#xff1a;AtomicInteger/synchronized…

239 基于matlab的EKF(扩展卡尔曼滤波)_UKF(无迹卡尔曼滤波)_PF(粒子滤波)三种算法的估计结果比较

基于matlab的EKF(扩展卡尔曼滤波)_UKF(无迹卡尔曼滤波)_PF&#xff08;粒子滤波&#xff09;三种算法的估计结果比较&#xff0c;输出估计误差&#xff0c;并单独对粒子滤波进行估计及其置信区间可视化。程序已调通&#xff0c;可直接运行。 239 EKF(扩展卡尔曼滤波) - 小红书 …

Unity | Shader基础知识(第十三集:编写内置着色器阶段总结和表面着色器的补充介绍)

目录 前言 一、表面着色器的补充介绍 二、案例viewDir详解 1.viewDir是什么 2.viewDir的作用 3.使用viewDir写shader 前言 注意观察的小伙伴会发现&#xff0c;这组教程前半部分我们在编写着色器的时候&#xff0c;用的是顶点着色器和片元着色器的组合。 SubShader{CGPRO…

Java转Kotlin

Kotlin 是一种静态编程语言 2011JetBrains开始开发Kotlin&#xff0c;用于多平台应用&#xff08;能脱离虚拟机&#xff0c;直接编译成可以在win,mac,linux运行的二进制代码&#xff09; 2017获得谷歌官方支持 语法简洁&#xff08;减少了大量的样板代码&#xff0c;语法糖&…

【Python深度学习(第二版)(2)】深度学习之前:机器学习简史

文章目录 一. 深度学习的起源1. 概率建模--机器学习分类器2. 早期神经网络--反向传播算法的转折3. 核方法 -- 忽略神经网络4. 决策树、随机森林和梯度提升机5. 神经网络替代svm与决策树 二. 深度学习与机器学习有何不同 可以这样说&#xff0c;当前工业界所使用的大部分机器学习…

服务器端口怎么查,服务器端口查看方法详解

服务器端口是网络通信的关键组件&#xff0c;对于网络管理员和系统管理员来说&#xff0c;了解和掌握如何查看服务器端口是非常重要的。接下来介绍两种常用的方法来查看服务器端口。 方法一&#xff1a;使用命令提示符&#xff08;CMD&#xff09; 1. 首先&#xff0c;点击电脑…

JavaScript百炼成仙自学笔记——12

函数七重关之五&#xff08;自执行函数&#xff09; 什么时候用它&#xff1f; 很多时候&#xff0c;我们只想执行一个函数&#xff0c;却无所谓这个函数叫什么名字。那么这种情况下就可以考虑使用自执行函数。 {function(){console.log(123);} }(); 这就是一个简单的自执行的…

视频剪辑:视频文件元数据修改工具,批量操作提升效率和准确性

在视频剪辑和后期处理的过程中&#xff0c;除了对视频本身的编辑和修改&#xff0c;元数据的管理和修改同样重要。元数据&#xff0c;如标题、艺术家、专辑封面等&#xff0c;不仅提供了视频文件的基本信息&#xff0c;还有助于更好地组织、搜索和共享视频内容。而针对视频文件…

dumpsys meminfo 流程中细节

源码基于&#xff1a;Android U 参考&#xff1a; dumpsys meminfo 详解(R) dumpsys meminfo 详解(U) 1. 命令入口 MemBinder frameworks/base/services/core/java/com/android/server/am/AMS.javastatic class MemBinder extends Binder {ActivityManagerService mActivity…

原型模式和建造者模式

1、原型模式 1.1 概念 用一个已经创建的实例作为原型&#xff0c;通过复制该原型对象来创建一个和原型对象相同的新对象。 1.2 结构 原型模式包含如下角色&#xff1a; 抽象原型类&#xff1a;规定了具体原型对象必须实现的的 clone() 方法。 具体原型类&#xff1a;实现抽…

链表经典面试题01

目录 引言 面试题01:返回倒数第k个节点 题目描述: 思路分析: 代码展示: 面试题02:链表的回文结构 题目描述: 描述 思路分析: 代码展示: 面试题03:相交链表 题目描述: 思路分析: 代码展示: 小结: 引言 这次的题均来自力扣和牛客有关链表的经典面试题,代码只会展示…

二.Django--创建多个APP路由映射

目录 1-创建项目 2-创建多个APP 3-注册APP 4-创建"前端页面"并做路由映射 各个APP里面的views.py写视图函数等等 1-创建项目 django-admin startproject 项目名 django-admin startproject my_project 2-创建多个APP python manage.py startapp app名 pyth…

HttpCilent进行Post请求form-data接口,服务方接收不到参数

结论先行 生成分隔标识boundary在HttpPost中设置Header时带上boundary创建MultipartEntity时需要设置boundary 实现代码如下 /*** param url 调用接口的地址* param paramMap 调用接口传入的方法体参数*/ public static String postDataByFormData(String url, Map<Strin…

【参赛总结】第二届云原生编程挑战赛-冷热读写场景的RocketMQ存储系统设计 - Nico

关联比赛: 2021第二届云原生编程挑战赛1&#xff1a;针对冷热读写场景的RocketMQ存储系统设计 引子 在一个浑浑噩噩的下午&#xff0c;百无聊赖的我像往常一样点开了划水交流群&#xff0c;细细品味着老哥们关于量子力学的讨论。嬉戏间&#xff0c;平常水不拉几的群友张三忽…

【毕业设计】基于SSM的运动用品商城的设计与实现

1.项目介绍 在这个日益数字化和信息化的时代&#xff0c;随着人们购物习惯的转变&#xff0c;传统的实体商店已经无法满足人们日益增长的在线购物需求。因此&#xff0c;基于SSM&#xff08;Spring Spring MVC MyBatis&#xff09;框架的运动用品商城项目应运而生&#xff0…

OpenGL 入门(二)—— 渲染摄像头采集的预览画面

本篇主要内容&#xff1a; 将摄像头采集到的图像通过 OpenGL 绘制到屏幕上FBO 离屏渲染 在开始上述流程前&#xff0c;我们有必要对 SurfaceTexture 做一个简单了解&#xff0c;因为 OpenGL 需要通过它获取要绘制的图像。 1、认识 SurfaceTexture SurfaceTexture 是 Androi…

【XR806开发板试用】XR806与鸿蒙,创建任务,串口转发TCPServer收到的数据

很荣幸获得评测开发板的机会&#xff0c;XR806的程序资料做的还是挺不错的。 目标&#xff1a; 1、学习用鸿蒙创建2个任务&#xff1b; 2、创建TCP Server收发数据。 任务ledThread&#xff1a;LED每秒亮灭一次&#xff0c;代表程序在运行。 任务MainThread&#xff1a;创建TCP…

Leetcode—377. 组合总和 Ⅳ【中等】

2024每日刷题&#xff08;124&#xff09; Leetcode—377. 组合总和 Ⅳ 算法思想 实现代码 class Solution { public:int combinationSum4(vector<int>& nums, int target) {vector<unsigned long long>dp(target 1);dp[0] 1;for(int i 1; i < target;…

echarts柱状图实现左右横向对比

实现效果如上图 其实是两组数据&#xff0c;其中一组数据改为负数&#xff0c;然后 在展示的时候&#xff0c;在将负数取反 第一处修改坐标轴 xAxis: [{type: value,axisLabel: {formatter: function (value) {if (value < 0) {return -value;}else{return value;}}}}], 第…

如何修改图片大小?调整图片大小的几个方法介绍

当我们在不同的应用场景中使用图片的时候&#xff0c;常常会需要去调整图片尺寸来适应不同的要求&#xff0c;还有图片体积大小也会有要求&#xff0c;这时候就需要用到我们今天分享的这款图片在线处理工具了&#xff0c;不管是图片改大小或者图片压缩它都能快速解决&#xff0…