施磊老师c++(七)
STL组件
文章目录
- STL组件
- 1.整体学习内容
- 2.vector容器
- 3.deque和list
- deque--双端队列容器
- list--链表容器
- 4.vector,deque,list对比
- 主要内容
- 面经问题
- 5.详解容器适配器--stack, queue, priority_queue
- 容器适配器
- stack-栈
- queue-队列
- priority_queue-优先级队列
- 总结
- 6.无序关联容器
- 关联容器
- unordered_set
- unordered_map
- 应用
- 7.有序关联容器 - set,map
- set
- map
- pair注意
- 8.迭代器iterator
- 9.函数对象--仿函数
- 10.泛型算法和绑定器
1.整体学习内容
一、标准容器 c++11里提供了array forword_list
1. 顺序容器
vector
deque
list
2. 容器适配器
stack
queue
priority queue
3. 关联容器
无序关联容器 链式哈希表 增删查O(1)
unordered_set
unordered_multiset
unordered_map
unordered_multimap
有序关联容器 红黑树 增删查O(log2n) 2是底数(树的层数,树的高度)
set
multiset
map
multimap二、近容器
数组, string, bitset(位容器)三、迭代器
iterator和const iterator
reverse_iterator 和 const_reverse_iterator四、函数对象(类似c的函数指针)
greater, less五、泛型算法
sort,find,find_if,binary_search,for_each
2.vector容器
-
vector— 向量容器
底层数据结构—>动态开辟的数组, 每次以原来空间大小的2倍进行扩容 -
常用方法介绍:
vector<int> vec; 1. 增加 vec.push_back(20); //末尾添加元素-O(1)-可能导致容器扩容-------回顾空间配置器allocator vec.insert(it, 20); //指定位置增加元素--O(n)--因为后续的元素都要移动2. 删除 vec.pop_back(); // 末尾删除-O(1) vec.erase(it); //删除指定位置元素 O(n)3.查询: operator[] 下标随机访问: vec[5] -- O(1) iterator迭代器遍历 find, for_each --- 泛型查询 foreach ==> 通过迭代器实现的注意: 对容器进行连续插入或者删除(insert,erase), 一定要更新迭代器, 否则会导致容器迭代器失效----回顾容器迭代器失效常用方法: 1.size() 2.empty() //判断是否为空 , true(1)空, 0为非空 3.reserve(20) //给vector预留空间, 只开辟空间, 并不添加元素, 容器依然是空的, 元素是0, size()是0, empty()是1 4.resize(20) //重置大小,扩容 5.swap: //交换两个元素
-
代码:
注意区分 reserve和resize 的区别
注意 insert和erase的逻辑问题#include <iostream> using namespace std; #include <vector>int main() {vector<int> vec;//vec.reserve(20); // 预留空间//cout << vec.empty() << endl; // 1//cout << vec.size() << endl; // 0vec.resize(20); // 会放入元素0cout << vec.empty() << endl; // 0cout << vec.size() << endl; // 0for (int i = 0; i < 20; ++i){vec.push_back(rand() % 100+1);}cout << vec.empty() << endl; //0 0cout << vec.size() << endl;//20 40#if 0int size = vec.size();for (int i = 0; i < size; ++i){cout << vec[i] << " ";}cout << endl;//删除所有偶数auto it2 = vec.begin();//for (; it2 != vec.end(); ++it2)//{// if (*it2 % 2 == 0)// {// vec.erase(it2); //删除全部, 需要更新迭代器// //break; //只删除一个, it2不用管了// }//}for (; it2 != vec.end(); ){if (*it2 % 2 == 0){it2 = vec.erase(it2); //删除全部, 需要更新迭代器//break; //只删除一个, it2不用管了}else // 注意逻辑问题{ ++it2; //由于更新了, 要判断一下当前位置}}auto it = vec.begin();for (; it != vec.end(); ++it){cout << *it << " ";}cout << endl;//给vector容器前所有奇数前面都添加一个小于奇数1的偶数for (auto it1 = vec.begin(); it1 != vec.end(); ++it1){if (*it1 % 2 != 0) {it1 = vec.insert(it1, *it1 - 1);++it1; // 注意逻辑问题}}it = vec.begin();for (; it != vec.end(); ++it){cout << *it << " ";}cout << endl;#endifreturn 0; }
3.deque和list
二者 比vector 多出来的 常用方法: push_front, pop_front
deque–双端队列容器
-
deque–双端队列容器–分块存储 — 默认第二维开辟 4096/sizeof(int) = 1024个位置
底层数据结构—> 动态开辟的二维数组, 第一维数组个数从2开始, 以2倍方式扩容, 每次扩容后, 原来第二维的数组, 从新的第一维数组的下标oldsize/2开始存放, 方便首尾元素添加第一维:中央映射表(指针数组),存储指向各个缓冲区的指针。第二维:每个缓冲区(固定大小的数组),存储实际的数据元素. (std::deque(双端队列)的底层实现可以理解为一个动态的二维数组,但这种描述需要进一步澄清。实际上,deque的底层数据结构是一个分段连续的空间,由多个固定大小的数组(称为缓冲区或块)组成,并通过一个中央映射表(通常是一个指针数组)来管理这些缓冲区。)---deepseek中央映射表 (Map) +---+---+---+---+---+---+---+ | | | | | | | | +---+---+---+---+---+---+---+| | | |v v v v +---+ +---+ +---+ +---+ | B | | B | | B | | B | <- 缓冲区 (Buffer) +---+ +---+ +---+ +---+| | | |v v v v +---+ +---+ +---+ +---+ | 1 | | 2 | | 3 | | 4 | <- 缓冲区中的元素 | 2 | | 3 | | 4 | | 5 | |...| |...| |...| |...| +---+ +---+ +---+ +---+一般first和last是在最中间, 因为是双端队列, 两边都能加元素 扩容后, 复制过去的旧数据也将会在中间位置(oldsize/2--用于计算放入的位置) 2->4 2/2=1 0,1,2,3 放在1,2处 4->8 4/2=2 0,1,2,3,4,5,6,7 放到 2,3,4,5处
-
常用方法:
#include<deque> deque<int> deq; 1. 增加 deq.push_back(20); // last 末尾添加 - O(1) deq.push_front(20); // first 从首部添加 - O(1) //vec.insert(vec.begin(), 20) - O(n) deq.insert(it, 20); // O(n)2.删除 deq.pop_back(); //O(1) deq.pop_front(); //O(1) deq.erase(it); //O(n)3.查询搜索 iterator(连续的insert和erase一定要考虑迭代器失效) 无 operator[]
list–链表容器
-
list–链表容器
底层数据结构–双向的循环链表 (pre,data,next) -
常用方法:
#include<list> deque<int> mylist; 1. 增加 --- 与deque 一模一样, 除了insert时间复杂度 mylist.push_back(20); // last 末尾添加 - O(1) mylist.push_front(20); // first 从首部添加 - O(1) //vec.insert(vec.begin(), 20) - O(n) mylist.insert(it, 20); // O(1) --- 不涉及其他元素 //但是链表进行insert前 需要进行 query查询, 链表查询效率低2.删除 mylist.pop_back(); //O(1) mylist.pop_front(); //O(1) mylist.erase(it); //O(1)3.查询搜索 iterator(连续的insert和erase一定要考虑迭代器失效) 无 operator[]
4.vector,deque,list对比
主要内容
-
不要只学习表面, 多看看底层
-
特点回顾
1.vector特点:动态数组,内存是连续的, 2倍的方式进行扩容, vector<int> vec; reserve和resize区别2.deque特点:动态开辟的二维数组, 第一维数组个数从2开始, 以2倍方式扩容, 每次扩容后, 原来第二维的数组, 从新的第一维数组的下标oldsize/2开始存放, 方便首尾元素添加
面经问题
-
deque的底层内存是不是连续的? – 分块存储
并不是, 每一个 第二维的是 连续的, 但是 第二维之间 不是连续的 -
vector 与 deque 区别?
1. 底层数据结构不同 2. 前中后 插入删除元素的时间复杂度: 中间O(n)和结尾O(1)相同, 但是 前不同, deque-O(1) vector-O(n) 3. 对于内存的使用效率, vector需要的内存是连续的, deque 可以分块 进行数据存储, 不需要内存空间 必须连续 4.在中间进行insert或erase, vector和deque他们的效率谁能好一点? //虽然都是 都在一个量级O(n) vetor更好, deque差 //由于deque的第二维空间不是连续的, 所以在deque中间进行元素的insert或者earse.造成元素移动要慢,
-
vector 与 list 区别?
1. 底层数据结构不同 list是双向循环链表 //一般的数组和链表, 数组:增加删除O(n), 查询O(n), 随机访问(1) //链表, 增删本身是O(1), 但是还要查询,O(n), 没有随机访问
5.详解容器适配器–stack, queue, priority_queue
容器适配器
-
注意区别 容器适配器和容器空间配置器
stack,queue,priority_queue之所以叫做适配器,是因为它们没有自己底层的数据结构,是依赖另外的容器来实现的功能,它们的方法,也是通过它们依赖的容器相应的方法实现的。
-
怎么理解适配器? –有一种设计模式就是适配器模式
底层没有自己的数据结构, 他是对另外容器的封装, 他的方法 全部由 底层依赖 的容器 进行实现stack 源码, 底层用的就是deque _EXPORT_STD template <class _Ty, class _Container = deque<_Ty>>
-
容器适配器:stack, queue, priority_queue ---- 重点, 使用频率高
stack-栈
-
常用方法: ===>依赖deque
1. push---入栈 2. pop--出栈 3. top--查看栈顶元素 4. empty--判空 5. size--返回元素个数
-
代码:
#include <iostream> using namespace std; #include <vector> #include <stack>int main() {stack<int> s1;for (int i = 0; i < 20; ++i){s1.push(rand() % 100 + 1);}cout << s1.size() << endl;while (!s1.empty()){cout << s1.top() << " ";s1.pop();}return 0; }
queue-队列
-
常用方法: fifo 先入先出 ===>依赖deque
1. push---入队列 2. pop--出队列, 队头出 3. front--查看队头 4. back--查看队尾 5. empty--判空 6. size--返回元素个数
priority_queue-优先级队列
-
常用方法: ===>依赖vector 默认大根堆
1. push---入队列 2. pop--出队列 3. top--查看队顶元素 4. empty--判空 5. size--返回元素个数
总结
-
代码:
#include <iostream> using namespace std; #include <stack> #include <queue>int main() {stack<int> s1;for (int i = 0; i < 20; ++i){s1.push(rand() % 100 + 1);}cout << s1.size() << endl;while (!s1.empty()){cout << s1.top() << " ";s1.pop();}cout << endl;cout << "------------------------------------------" << endl;queue<int> qe;for (int i = 0; i < 20; ++i){qe.push(rand() % 100 + 1);}cout << qe.size() << endl;while (!qe.empty()){cout << qe.front() << " ";qe.pop();}cout << endl;cout << "------------------------------------------" << endl;priority_queue<int> pqe;for (int i = 0; i < 20; ++i){pqe.push(rand() % 100 + 1);}cout << pqe.size() << endl;while (!pqe.empty()){cout << pqe.top() << " ";pqe.pop();}return 0; }
-
为什么有的依赖 deque(queue, strack), 有的依赖 vector(priority_queue)
1. 为什么选择deque?首先vector初始内存使用效率低, 没有deque好, vector是 0-1-2-4-8慢慢扩容, deque则是先开辟好大的, 虽然vector有reserve函数, 但是有修改成本其次, queue需要支持 尾部插入, 头部删除, 因此在这两个操作上,需要时间复杂度要求, 而deque正好是O(1), vector却是O(n)和O(1)vector需要大片的连续内存, 而deque只需要分段内存, 当存储大数据时, deque内存利用率更高2. 为什么选择vector?priority_queue 底层默认是大根堆结构, 使用 类似奇数和偶数下标的形式(了解二叉树应该明白这个), 来查找访问元素. 就像二叉树 是用数组存储的, 下标很重要-----------deque则不行, 第二维的数组,不同的块, 内存都不是连续的
6.无序关联容器
关联容器
关联容器分为: 无序和有序 关联容器
集合set, 映射表map
-
以链式哈希表作为底层数据结构的无序关联容器有:: — unordered_…
增删查–O(1)unordered_set 单重集合 单重就是不允许数重复 unordered_multiset 多重集合 // #include<unordered_set> unordered_map unordered_multimap // #include<unordered_map>
-
以红黑树作为底层数据结构的有序关联容器有:
增删查–O(log2 n) 2是底数,树的高度set multiset // #include<set> map multimap // #include<map>
-
关联容器 与 vector,deque, list的 函数使用注意点
与 vector,deque, list不同,这些的insert是两个参数, 因为是线性表, 需要指定位置
但是 由于关联容器 底层是 哈希表 或 红黑树, 插入的位置不是随机的,一个是按哈希公式, 一个是根据红黑树性质unordered_set<int> set1;set1.insert(20)
unordered_set
-
切记: 单重集合不存储 重复元素!!!
-
find()------有则返回迭代器, 不存在则返回末尾迭代器
-
c++11 的 foreach 正规名叫 基于范围的
for
循环(range-based for loop) -
关联容器常用方法:
增: insert 删: erase(key), erase(it) --- key和迭代器都行 遍历: iterator, find(key)
-
代码:
#include <iostream> #include <unordered_set> #include <string> using namespace std;int main() {// 不允许key重复 改成unordered_multiset自行测试unordered_set<int> set1; // ---- 注意只有一个参数for (int i = 0; i < 100; ++i){set1.insert(rand() % 100 + 1); }cout << set1.size() << endl; // 65, 不是100, 单重集合不存储 重复元素/*=============================================================================*/unordered_multiset<int> mulset1; // ---- 注意只有一个参数for (int i = 0; i < 100; ++i){mulset1.insert(rand() % 100 + 1); }cout << mulset1.size() << endl; // 100/*=============================================================================*/auto it1 = set1.begin();for (;it1 != set1.end();++it1){cout << *it1 << " ";}cout << endl;//c++11有 foreach形式用于遍历for (auto v : set1){cout << v << " ";}cout << endl;/*=============================================================================*/set1.erase(20); //按key值删除元素/*=============================================================================*/// 寻找是否存在20并删除it1 = set1.find(20); // 有则返回迭代器, 不存在则返回末尾迭代器if (it1 != set1.end()){set1.erase(it1);}// count返回set中有几个50=》最多是1个cout << set1.count(50) << endl;return 0; }
unordered_map
-
map是存储键值对[key, val], set只存储 key
-
first->key, second->val ===> 依赖于 pair 类
-
operator[] 要注意, 看代码
-
代码:
#include <iostream> #include <unordered_map> #include <string> using namespace std;int main() {// 定义一个无序映射表unordered_map<int, string> map;// 无序映射表三种添加[key,value]的方式map.insert({ 1000, "aaa" }); // 注意打包键值对map.insert(make_pair(1001, "bbb"));map[1002] = "ccc"; // operator[] 添加//删除map.erase(1002);//查询cout << map.size() << endl; //2cout << map[1000] << endl;cout << map[1003] << endl; // []重载,不仅能查询, 而且key不存在时, 会添加这个键值对, string() , 实际打印出来就什么也没有, []还能修改cout << map.size() << endl; //3 // 遍历map表1auto it = map.begin(); // 迭代器是 打包的 pair对象for (; it != map.end(); ++it){cout << "key:" << it->first << " value:" << it->second << endl;}// 遍历map表2for (pair<const int, string>& pair : map) // 这里是const, map里key不可修改, 但是, 实际可以用auto更方便{cout << "key:" << pair.first << " value:" << pair.second << endl;}查找key为1000的键值对,并且删除//auto it1 = map.find(1000);//if (it1 != map.end())//{// map.erase(it1);//}return 0; }
应用
-
无序map的一个应用: 海量数据查重
#include <iostream> #include <unordered_map> #include <string> using namespace std;int main() {const int ARR_LEN = 100;int arr[ARR_LEN] = { 0 };for (int i = 0; i < ARR_LEN; ++i){arr[i] = rand() % 20 + 1;}unordered_map<int, int> map1;for (int k : arr){//auto it = map1.find(k);//if (it == map1.end())//{// map1.insert({ k,1 });//}//else//{// it->second++; //出现过, 就增加次数//}map1[k]++; // 初始是[k,int()]即[k,0]}for (auto& pair : map1){if (pair.second > 1){cout << "key: " << pair.first << " count: " << pair.second << endl;}}return 0; }
-
set应用, 去重代码:
#include <iostream> #include <unordered_map> #include <string> #include <unordered_set> using namespace std;int main() {const int ARR_LEN = 100;int arr[ARR_LEN] = { 0 };for (int i = 0; i < ARR_LEN; ++i){arr[i] = rand() % 20 + 1;}//去重打印unordered_set<int> set1;for (int k : arr){set1.insert(k);}for (int v : set1){cout << v << " ";}cout << endl;return 0; }
-
哈希桶? – 不是很明白这个到底是啥
#include <iostream> #include <string> #include <unordered_map> int main() {std::unordered_map<std::string, std::string> mymap ={{ "house", "maison" },{ "apple", "pomme" },{ "tree", "arbre" },{ "book", "livre" },{ "door", "porte" },{ "grapefruit", "pamplemousse" }};unsigned n = mymap.bucket_count(); //获取哈希桶的个数std::cout << "mymap has " << n << " buckets.\n";for (unsigned i = 0; i < n; ++i) // 逐个遍历哈希桶中的链表{std::cout << "bucket #" << i << " contains: ";for (auto it = mymap.begin(i); it != mymap.end(i); ++it)std::cout << "[" << it->first << ":" << it->second << "] ";std::cout << "\n";}return 0; }
7.有序关联容器 - set,map
- 单重set, 不重复元素, 但有序
- 常用方法 与 unordered 一模一样
set
-
自定义类型呢? 需要手动 提供自定义类型的 operator<.
//不加入自己的 重载时, 会报以下错误 //二进制“<”:“const _Ty”不定义该运算符或到预定义运算符可接收的类型的转换#include <iostream> #include <string> #include <set> using namespace std;class Student { public:Student(int id, string name): _id(id), _name(name){}bool operator< (const Student& stu)const //不修改成员变量, 只访问, 尽量写常方法{return _id < stu._id;} private:int _id;string _name;friend ostream& operator<< (ostream& out, const Student& stu); };ostream& operator<< (ostream& out, const Student& stu) {out << "id: " << stu._id << " name: " << stu._name << endl;return out; }int main() {set<Student> set1;set1.insert(Student(1001, "hzh2"));set1.insert(Student(1000, "hzh1"));for (auto v : set1){cout << v ;}cout << endl;return 0; }
map
-
代码:
#include <iostream> #include <string> #include <set> #include <map> using namespace std;class Student { public:Student(int id=0, string name="hzh") : _id(id), _name(name){}private:int _id;string _name;friend ostream& operator<< (ostream& out, const Student& stu); friend ostream& operator<< (ostream& out, const pair<int, Student>& p); };ostream& operator<< (ostream& out, const Student& stu) {out << "id: " << stu._id << " name: " << stu._name << endl;return out; }ostream& operator<< (ostream& out, const pair<int, Student>& p) {out << "id: " << p.second._id << " name: " << p.second._name << endl;return out; }int main() {map<int, Student> map1; // map按key就可以自动排map1.insert({ 1001, Student(1001, "hzh1") });map1.insert({ 1000, Student(1000, "hzh0") });cout << map1[1000] << endl; // 这样的 operator[] 需要有默认的构造函数, 不使用[], 是可以不需要 默认构造函数的for (auto& v : map1){cout << v ; // 对应第二个<<重载}cout << endl;//还有迭代器方式for (auto it = map1.begin(); it != map1.end(); ++it){cout << "key: " << it->first << "value: " << it->second << endl; //后面的用到了<<重载 }return 0; }
pair注意
- 元素访问, 可以是 it->first, 也可以是 (*it).first, 一般使用第一个, 更方便
8.迭代器iterator
iterator, const_iterator, reverse_iterator, const_reverse_iterator
-
iterator 是 普通的 正向迭代器 ---- 不仅能读,还能修改
#include <iostream> #include <string> #include <vector> using namespace std;int main() {vector<int> vec;for (int i = 0; i < 20; ++i){vec.push_back(rand() % 100 + 1);}//vector<int>::iterator it = vec.begin(); // 这个要会auto it = vec.begin();for (; it != vec.end(); ++it){cout << *it << " "; if (*it % 2 == 0){*it = 0;}}cout << endl;it = vec.begin();for (; it != vec.end(); ++it){cout << *it << " ";}return 0; }
-
const_iterator常量的正向迭代器 ----- 只能读, 不能写
#include <iostream> #include <string> #include <vector> using namespace std;int main() {vector<int> vec;for (int i = 0; i < 20; ++i){vec.push_back(rand() % 100 + 1);}vector<int>::const_iterator it = vec.begin(); // 这个要会for (; it != vec.end(); ++it){cout << *it << " "; //if (*it % 2 == 0)//{// *it = 0;//}}cout << endl;it = vec.begin();for (; it != vec.end(); ++it){cout << *it << " ";}return 0; }
-
为什么 iterator转化为const_iterator是对的?
vector<int>::const_iterator it = vec.begin();实际上, 源码里, const_iterator是iterator的基类 class const_iterator { public:const T& operator*() { return *_ptr; } };class iterator : public const_iterator { public:T& operator*() { return *_ptr; } };
-
reverse_iterator 反向迭代器, 搭配rbegin()
同样有 const_reverse_iterator
rbegin()返回的最后一个元素的 反向迭代器 rend()返回的首元素前驱位置的 反向迭代器#include <iostream> #include <string> #include <vector> using namespace std;int main() {vector<int> vec;for (int i = 0; i < 20; ++i){vec.push_back(rand() % 100 + 1);}vector<int>::reverse_iterator it = vec.rbegin(); // 这个要会for (; it != vec.rend(); ++it) // 这里还是++{cout << *it << " "; //if (*it % 2 == 0)//{// *it = 0;//}}cout << endl;return 0; }
9.函数对象–仿函数
-
函数对象–> 就是 c里面的 函数指针
函数对象(Function Object),也称为仿函数(Functor),是 C++ 中的一个重要概念。它是一个类或结构体,通过重载operator()
运算符,使得该类的对象可以像函数一样被调用。 -
对比下面两个:
int sum(int a, int b) {return a + b; }int ret = sum(10, 20);
class Sum { public:int operator() (int a, int b){return a + b;} };Sum sum; int ret = sum(10, 20);
-
关于内联和函数指针代码:
#include <iostream> using namespace std;template<typename T> bool mygreater(T a, T b) {return a > b; }template<typename T> bool myless(T a, T b) {return a < b; }// compare是C++的库函数模板 template<typename T, typename Compare> bool compare(T a, T b, Compare comp) {// 通过函数指针调用函数,是没有办法内联的,效率很低, 因为有函数调用开销return comp(a, b); }int main() {cout << compare(10, 20, mygreater<int>) << endl;cout << compare(10, 20, myless<int>) << endl;return 0; }
这段代码里, 如果把 myless和mygreater 换成内联函数, 编译阶段是comp识别不了用哪个函数的 因为 这是使用函数指针间接调用的, 运行时才会去找下面这个是可以识别的, 编译阶段会展开 inline bool func() {...; } bool compare(T a, T b, Compare comp) {func();return comp(a, b); }
-
使用函数对象(仿函数)解决函数指针调用开销问题
#include <iostream> using namespace std;//c++函数对象的版本 template<typename T> class mygreater { public:bool operator() (T a,T b) // ()重载的两个参数叫做 二元函数对象, 一个参数就叫做一元函数对象{return a > b;} };template<typename T> class myless { public:bool operator() (T a, T b){return a < b;} };template<typename T, typename Compare> bool compare(T a, T b, Compare comp) {// 通过函数指针调用函数,是没有办法内联的,效率很低, 因为有函数调用开销return comp(a, b); }int main() {cout << compare(10, 20, mygreater<int>()) << endl;cout << compare(10, 20, myless<int>()) << endl;return 0; }
-
函数对象好处
1. 通过函数对象调用operator(), 可以省略函数的调用开销, 比通过函数指针调用函数(不能使用内联)效率高 2. 因为函数对象是用类生成的, 所以可以添加 相关的 成员变量, 用于记录函数对象使用时的更多信息
-
priority_queue默认是大根堆, 即从大到小排列, 改为小根堆
#include <iostream> #include <queue> #include <vector> using namespace std;int main() {// 最大堆---默认的priority_queue<int> maxHeap;for (int i = 0; i < 10; ++i) {maxHeap.push(rand() % 100);}cout << "Max Heap: ";while (!maxHeap.empty()) {cout << maxHeap.top() << " ";maxHeap.pop();}cout << endl;// 最小堆 -- 看一下源代码参数, 改一下less//template <class _Ty, class _Container = vector<_Ty>, class _Pr = less<typename _Container::value_type>>using MinHeap = priority_queue<int, vector<int>, greater<int>>;MinHeap minHeap;for (int i = 0; i < 10; ++i) {minHeap.push(rand() % 100);}cout << "Min Heap: ";while (!minHeap.empty()) {cout << minHeap.top() << " ";minHeap.pop();}cout << endl;return 0; }
同理, set也行
stl里 这种一般都是 less 和 greater -
using
using 是 C++ 中一个非常强大的关键字,主要用途包括:类型别名:定义类型别名,类似于 typedef。模板别名:为模板定义别名。命名空间成员引入:引入命名空间中的特定成员。命名空间整体引入:引入整个命名空间。基类成员引入:在派生类中引入基类的成员。构造函数继承:继承基类的构造函数。模板中使用:在模板中定义类型别名。函数中使用:在函数内部定义类型别名。
10.泛型算法和绑定器
-
泛型算法头文件
#include <algorithm>
-
泛型算法特点: — c++ primer书里有很多 泛型算法
1. 接收的都是 迭代器sort, find, find_if:有条件的find, binary_search:二分查找, for_each2. 还能接受函数对象3.模板实现的+迭代器+函数对象
-
绑定器:
bind1st: 把二元函数对象的operator()的第一个形参绑定起来 bind2nd:把二元函数对象的operator()的第二个形参绑定起来#include <functional> 包含函数对象和绑定器
绑定器+二元函数对象==>一元函数对象
-
代码:
#include <iostream> #include <vector> #include <algorithm> #include <functional> using namespace std;int main() {int arr[] = { 1, 2, 5, 4, 3 };size_t size = sizeof(arr) / sizeof(arr[0]); // 计算数组大小// 使用范围构造函数将数组元素放入 vectorvector<int> vec(arr, arr + size);// 输出 vector 中的元素for (int val : vec) {cout << val << " ";}cout << endl;sort(vec.begin(), vec.end());// 输出 vector 中的元素for (int val : vec) {cout << val << " ";}cout << endl;if (binary_search(vec.begin(), vec.end(), 5)){cout << "5 is yes" << endl;}//从大到小sort(vec.begin(), vec.end(), greater<int>()); // 这个可比自己写快多了for (int val : vec) {cout << val << " ";}cout << endl;//有序的容器, 使用二分查找是更快的 log2 n 二分查找默认是在升序的容器里找if (binary_search(vec.begin(), vec.end(), 5, greater<int>())){cout << "5 is yes" << endl;}/*_EXPORT_STD template <class _FwdIt, class _Ty, class _Pr> _NODISCARD _CONSTEXPR20 bool binary_search(_FwdIt _First, _FwdIt _Last, const _Ty& _Val, _Pr _Pred) {// test if _Val equivalent to some element_STD _Adl_verify_range(_First, _Last);auto _UFirst = _STD _Get_unwrapped(_First);const auto _ULast = _STD _Get_unwrapped(_Last);_UFirst = _STD lower_bound(_UFirst, _ULast, _Val, _STD _Pass_fn(_Pred));return _UFirst != _ULast && !_Pred(_Val, *_UFirst); }_EXPORT_STD template <class _FwdIt, class _Ty> _NODISCARD _CONSTEXPR20 bool binary_search(_FwdIt _First, _FwdIt _Last, const _Ty& _Val) {// test if _Val equivalent to some elementreturn _STD binary_search(_First, _Last, _Val, less<>{}); }*/// 使用find() ,, 二分效率高auto it = find(vec.begin(), vec.end(), 4);if (it != vec.end()){cout << "4 is yes--find" << endl;}// find_if 有条件的find, 一元函数对象 greater和less是二元函数对象// 将4插入到vec里, 找第一个小于 4的(降序) // 使用绑定器 找第一个小于4的 //greater表示大于, 则绑定第一个为4 即 4>b//less表示小于, 则绑定第二个为4 即 a<4/*auto it2 = find_if(vec.begin(), vec.end(), bind1st(greater<int>(), 4));vec.insert(it2, 4);*/auto it2 = find_if(vec.begin(), vec.end(), bind2nd(less<int>(), 4));vec.insert(it2, 4);for (int val : vec) {cout << val << " ";}cout << endl;//c++11提供了 比绑定器和函数对象更简便的 lamda表达式---底层就是函数对象// []表示捕获外部变量,val就是捕获的 bool是返回值类型auto it3 = find_if(vec.begin(), vec.end(), [](int val)->bool {return val < 6; });vec.insert(it3, 6);for (int val : vec) {cout << val << " ";}cout << endl;//for_each 可以遍历容器所有元素, 可以自行添加合适的元素对象, 可以过滤元素// 打印偶数for_each(vec.begin(), vec.end(), [](int val)->void{if (val % 2 == 0){cout << val << " ";}});cout << endl;return 0; }
相关文章:
施磊老师c++(七)
STL组件 文章目录 STL组件1.整体学习内容2.vector容器3.deque和listdeque--双端队列容器list--链表容器 4.vector,deque,list对比主要内容面经问题 5.详解容器适配器--stack, queue, priority_queue容器适配器stack-栈queue-队列priority_queue-优先级队列总结 6.无序关联容器关…...
Codeforces 158B. Taxi
题目 题目链接:https://codeforces.com/problemset/problem/158/B time limit per test:3 seconds;memory limit per test:256 megabytes After the lessons n groups of schoolchildren went outside and decided to visit Polycarpus to celebrate his birthda…...
hadoop伪分布式搭建--启动过程中如果发现某个datanode出现问题,如何处理?
一、问题定位: (1)检查DataNode日志: DataNode日志通常位于$HADOOP_HOME/logs/或/var/log/hadoop-hdfs/目录下,文件名为hadoop-hdfs-datanode-<hostname>.log。重点关注以下错误类型: ——Incompa…...
MySQL(事物上)
目录 示例: 一 引入事物 1. 概念 2. 事物的4大特性 3. 为什么要有事物? 二 事物操作 1. 查看存储引擎支持的事物 2. 事物的提交方式 2.1 查看事物的默认提交方式 2.2 设置事物的默认提交方式 2.3 查看事物的全局隔离级别 2.4 验证事物的回滚…...
人工智能 Day06 pandas库进阶
1.处理缺失数据 总体流程是这样的, 归根在于如何处理NAN,接下来详细赘述 1.1. 处理缺失值的相关函数 判断缺失值 pd.isnull(df):用于判断 DataFrame df 中的元素是否为缺失值(NaN ),返回一个与df 形状相同…...
C# --- LINQ
C# --- LINQ 什么是LINQFluent Syntax 和 SQL-Like QueryLINQ Operations 什么是LINQ LINQ的全称为Language Integrated Query, 为各种查询(包括对象查询,数据库查询,XML查询) 提供了统一模型.LINQ源于SQL,但比SQL更加强大,更加灵…...
C语言之 条件编译和预处理指令
条件编译 在编译⼀个程序的时候我们如果要将⼀条语句(⼀组语句)编译或者放弃是很⽅便的。因为我们有条件编译指令。 ⽐如说: 调试性的代码删除可惜,保留⼜碍事,所以我们可以选择性的编译。 #define M 1 int main() …...
JVM常用概念之锁省略
问题 synchronized(同步-重量级锁)会解除所有编译器优化吗? 基础知识 使用当前的 Java 内存模型,未观察到的锁不一定会产生任何内存效应。除其他情况外,这意味着对非共享对象进行同步是徒劳的,因此运行时不必在那里做任何事情。这给编译优…...
[网络][tcp协议]:tcp报头
tcp(传输控制协议)是一种面向字节流的传输层协议,相较于udp协议,tcp能保证传输数据的可靠性与准确性,tcp也是目前最常见的传输层协议 本文主要介绍tcp报头各个字段的含义与用途 注:保留6位和6位标记位是目前最普遍的写法,在我查资料时,发现有一些拓展情况,会在后文细说 最简单的…...
传输层自学
传输实体:完成传输层任务的硬件或软件 可能位于: 操作系统内核独立的用户进程绑定在网络应用中的链接库网络接口卡 1.功能: 网络层与传输层作用范围比较? 网络层负责把数据从源机送达到目的机 传输层负责把数据送达到具体的应…...
FFmpeg —— 各系统下ffmpeg硬件加速和API支持情况(文内表格形式详细阐述)
介绍 FFmpeg 作为一款功能强大的多媒体处理工具,支持多种硬件加速技术,能够显著提升视频编解码的效率,尤其是在处理高分辨率、高码率视频时表现尤为突出。不同操作系统下,FFmpeg 的硬件加速实现方式和支持的 API 各有特点。 在 Windows 系统上,FFmpeg 主要依赖 DirectX Vi…...
RUOYI框架在实际项目中的应用二:Ruoyi前后端分离版本
如需观看Ruoyi框架的整体介绍,请移步:RUOYI框架在实际项目中的应用一:ruoyi简介 一、Ruoyi前后端分离版本-RuoYi-Vue 1、官方资料 1:代码地址:https://gitee.com/y_project/RuoYi-Vue.git 2:文档介绍地址…...
2.12[A]distribute sys
在分布式训练中,特别是使用3D并行(数据并行、流水线并行和模型并行)时,不同阶段的GPU可能因为通信或数据依赖而出现空闲时间,这些空闲时间就是所谓的“气泡”。这些气泡会降低整体的训练效率,导致GPU资源的…...
R语言的移动应用开发
R语言的移动应用开发 在数据科学和统计分析的大潮中,R语言因其强大的数据处理和可视化能力而备受青睐。然而,R语言对移动应用开发的适用性并未得到广泛关注。本文将探讨R语言在移动应用开发中的潜力及其工具,并提供一些实践示例,…...
解决 Redis 后台持久化失败的问题:内存不足导致 fork 失败
文章目录 解决 Redis 后台持久化失败的问题:内存不足导致 fork 失败问题背景与成因解决方案修改内核参数 vm.overcommit_memory增加系统内存或 Swap 空间调整 Redis 配置 stop-writes-on-bgsave-error 在 Docker 环境中的注意事项总结 解决 Redis 后台持久化失败的问…...
交换机控制软件的实现步骤猜测
一、主要目的 提出对交换机软件控制逻辑的猜测。 二、交换机控制软件的组成 (一)背景 1、交换机有很多的RJ45水晶头端口。 2、每个端口支持同时发送和接收字节数据。 3、每个端口接收的数据需要查表后才能转发给目标端口。 (二)端口状态扫描线程 负责扫描每个端口的状态&#x…...
100.HarmonyOS NEXT跑马灯组件教程:实际应用与场景示例
温馨提示:本篇博客的详细代码已发布到 git : https://gitcode.com/nutpi/HarmonyosNext 可以下载运行哦! HarmonyOS NEXT跑马灯组件教程:实际应用与场景示例 文章目录 HarmonyOS NEXT跑马灯组件教程:实际应用与场景示例1. 跑马灯组…...
【计算机网络】2物理层
物理层任务:实现相邻节点之间比特(或)的传输 1.通信基础 1.1.基本概念 1.1.1.信源,信宿,信道,数据,信号 数据通信系统主要划分为信源、信道、信宿三部分。 信源:产生和发送数据的源头。 信宿:接收数据的终点。 信道:信号的传输介质。 数据和信号都有模拟或数字…...
2.3 滑动窗口专题:最大连续1的个数 III(LeetCode 1004)
1. 题目链接 1004. 最大连续1的个数 III - 力扣(LeetCode)https://leetcode.cn/problems/max-consecutive-ones-iii/ 2. 题目描述 给定一个二进制数组 nums 和一个整数 k,允许将最多 k 个 0 翻转为 1,求翻转后最长的连续 1 …...
怎么解决在Mac上每次打开文件夹都会弹出一个新窗口的问题
在Mac上每次打开文件夹都会弹出一个新窗口的问题,可以通过以下方法解决 调整Finder设置: 打开Finder,点击“Finder”菜单,选择“偏好设置”。在偏好设置中,选择“通用”标签。取消勾选“在标签页中打开文件夹”或…...
Python异常处理
异常处理 概述 在Python中,在处理可能会引发异常的代码块时,使用try和except语句。可以帮助我们捕获并处理异常, 而不是让程序因为一个未处理的异常而完全崩溃。 try-except try-except-finally try-finally try-except-else try-except-…...
VSTO(C#)Excel开发8:打包发布安装卸载
初级代码游戏的专栏介绍与文章目录-CSDN博客 我的github:codetoys,所有代码都将会位于ctfc库中。已经放入库中我会指出在库中的位置。 这些代码大部分以Linux为目标但部分代码是纯C的,可以在任何平台上使用。 源码指引:github源…...
ImportError: cannot import name ‘genai‘ from ‘google‘ (unknown location) 问题如何处理
这个错误通常发生在没有正确安装Google的生成式AI库。需要安装官方的google-generativeai库: pip install google-generativeai如果代码中使用的导入方式与新版SDK不兼容,可能需要调整导入语句。根据当前代码上下文,正确的导入方式应该是&am…...
Advanced Intelligent Systems 软体机器手助力截肢者玩转鼠标
随着科技的不断进步,假肢技术在改善截肢者生活质量方面取得了显著成就。然而,截肢群体在就业方面仍面临巨大困难,适龄截肢群体的就业率仅为健全群体的一半。现有的肌电控制假肢手在与计算机交互时存在诸多挑战,特别是截肢者在使用…...
kubernetes对于一个nginx服务的增删改查
1、创建 Nginx 服务 1.1、创建 Deployment Deployment 用于管理 Pod 副本和更新策略。 方式一:命令式创建 kubectl create deployment nginx-deployment --imagenginx:latest --replicas3 --port80--replicas3:指定副本数为 3 --port80:容…...
我的世界1.20.1forge模组进阶开发教程生物篇(1)——生成
生物生成 生物生成Alexmob介绍:**1. 核心功能与技术实现****2. 项目结构与代码质量****3. 社区协作与维护****4. 扩展性与开发挑战****5. 开发者学习价值**食蚁兽一、实体属性与行为控制(`EntityAnteater`类)二、实体注册与生成规则(`AMEntityRegistry`类)三、全局生成逻辑…...
1.5 Spring Boot项目打包和运行
本文介绍了如何使用Spring Boot进行项目打包和运行。首先,讲解了如何将Spring Boot项目打包为可执行的JAR包,并直接运行,无需部署到外部Web服务器。接着,介绍了如何将项目打包为WAR包,以便部署到Web容器中,…...
287. 寻找重复数
由于题目规定数组中的数的范围是1-n,因此可以构造出下标n和值nums[n]的映射f(n),然后构成一个链表,当有重复数字时,链表存在环,找到重复数字即找到链表环的入口,参考142. 环形链表II。 class Solution {pu…...
如何高效解决 Java 内存泄漏问题方法论
目录 一、系统化的诊断与优化方法论 二、获取内存快照:内存泄漏的第一步 (一)自动生成 Heap Dump (二)手动生成 Heap Dump 三、导入分析工具:MAT 和 JProfiler (一)MAT (Memor…...
【Agent】OpenManus 项目架构分析
这是我录制的一个视频,主要是描述我理解的 OpenManus 的思维逻辑,通过这个小的思维逻辑的复现,为后面要再分析其他 Agent 的实现做一个准备。 1. 项目概述 OpenManus 是一个基于大语言模型的智能体框架,旨在提供一个无需邀请码的…...
hive-进阶版-1
第6章 hive内部表与外部表的区别 Hive 是一个基于 Hadoop 的数据仓库工具,用于对大规模数据集进行数据存储、查询和分析。Hive 支持内部表(Managed Table)和外部表(External Table)两种表类型,它们在数据…...
规模效应的三重边界:大白话解读-deepseek为例
前言:当Scaling Laws遇见边际递减效应 在人工智能的狂飙突进中,大语言模型如同不断膨胀的星体,吞噬着海量算力与数据。OpenAI于2020年揭开的Scaling Laws,曾为这场盛宴指明方向:模型性能随参数规模(N&…...
考研系列-408真题计算机网络篇(18-23)
写在前面 此文章是本人在备考过程中408真题计算机网络部分(2018年-2023年)的易错题及相应的知识点整理,后期复习也常常用到,对于知识提炼归纳理解起到了很大的作用,分享出来希望帮助到大家~ # 2018 1.停止-等待协议的…...
windows协议不再续签,华为再无windows可用,将于四月发布鸿蒙PC
大家好,我是国货系创始人张云泽,最近不少小伙伴在后台问:“听说Windows协议要到期了?我的电脑会不会变砖?”还有人说:“华为笔记本以后用不了Windows了?鸿蒙系统能用吗?”今天咱们就…...
【二分算法】-- 点名
文章目录 1. 题目2. 题目解析3. 代码 1. 题目 在线oj 2. 题目解析 前四种解决方法: 哈希表直接遍历找结果位运算数学(高斯求和公式) 这四种方法的时间复杂度都是0(N) 第五种解决方法: 【二段性】&…...
强化学习 - PPO控制无人机
PPO(Proximal Policy Optimization,近端策略优化)是一种强化学习算法,用于训练智能体(无人机)如何在环境中做出决策。它本质上是 策略梯度(Policy Gradient)方法 的一种改进…...
【AHE数据集】 NCAR Anthropogenic Heat Flux (AHF) 数据集
数据概述 数据集由 美国国家大气研究中心(NCAR, National Center for Atmospheric Research) 的 气候与全球动力学实验室(CGD, Climate & Global Dynamics Laboratory) 提供。NCAR 由 美国国家科学基金会(NSF, National Science Foundation) 资助,并由 大学大气研究…...
Part1:基于国内源完成Kubernetes集群部署
集群规划 操作系统:CentOS7 内核版本:5.4(需升级) 组件版本说明操作系统内核5.4RPM方式升级docker26.1.4yum安装cri-docker0.3.16二进制安装kubeadm1.30.11yum安装kubealet1.30.11yum安装kubectl1.30.11yum安装kubectl1.30.11yu…...
强化学习的一些概念
目录 强化学习 打个比方 核心要素 State Action Reward 几个代码demo 学习目标 强化学习 强化学习(Reinforcement Learning, RL)是机器学习的一个分支,旨在让智能体(Agent)通过与环境的交互学习最优策略,以…...
花生好车:重构汽车新零售生态的破局者
在传统汽车零售行业面临消费升级与渠道变革的双重压力下,花生好车以颠覆性的商业模式在短短九年内崛起为行业独角兽。这家成立于2015年的汽车新零售平台,通过重构供应链体系、创新融资租赁模式、深耕下沉市场三大战略维度,正在重塑中国汽车消…...
K8S下nodelocaldns crash问题导致域名请求响应缓慢
前言 最近做项目,有业务出现偶发的部署导致响应很慢的情况,据了解,业务使用域名访问,相同的nginx代理,唯一的区别就是K8S重新部署了。那么问题大概率出现在容器平台,毕竟业务是重启几次正常,偶…...
实现悬浮按钮拖动,兼容h5和微信小程序
h5用js写,微信小程序用 代码里面没有完全实现吸附边缘的功能,需要吸附边缘的话还得自己再完善下(h5的吸附边缘是可以的,小程序的还有点问题) 主要功能是:图片上写文字的悬浮按钮,文字使用的是…...
SLC跨头协作机制
SLC跨头协作机制 SLC(Self-attention with Local Communication,或类似跨头协作机制)在Transformer架构中通过以下逻辑帮助注意力头优化分布: 1. 多头注意力的「独立-协作」平衡 传统多头注意力中,每个头独立计算注意力(如Query/Key/Value的线性变换),捕捉不同语义模…...
全国医院数据可视化分析系统
【大数据】全国医院数据可视化分析系统 (完整系统源码开发笔记详细部署教程)✅ 目录 一、项目简介二、项目界面展示三、项目视频展示 一、项目简介 🏥 项目名:医疗导航神器!——《基于大数据的微医挂号网医院数据可视…...
Flash Attention原理讲解
目录 前言0. 简述1. self-attention2. roofline model3. 矩阵分块4. softmax分块5. FlashAttention结语参考 前言 看了几个视频和几篇文章学习了下 Flash Attention,记录下个人学习笔记,仅供自己参考😄 refer1:Flash Attention 为…...
python二级复习(1)
临近计算机二级考试了,开始python的复习 python语言基础: 1.用缩进表示代码块:一般用四个空格或者一个tab 2.代码的注释方法: 单行注释用“#”表示注释开始;多行注释是用三个英文的单引号“‘’”或双引号““”"”作为注释的开始和结束符号。 03. 标识符命…...
基于cat1的贵重物品的状态和位置小型监控系统特色解析
一 项目需求 团队研发出来一款搭载多传感器的无线cat1定位和状态监控的设备。该设备主要面对的贵重物品运输过程中的状态监控,比如,是否被打开过,有没有激烈碰撞,位置信息等。主要应用场景是医疗,安防等贵重物品的状态…...
Python 魔法方法介绍
在 Python 中,魔法方法(Magic Methods)是一类特殊的内置方法,它们通常以双下划线开头和结尾(例如 __init__、__str__、__add__ 等)。这些方法通常用于实现特定的语法或操作符行为,或者用于定义对象的行为。它们是 Python 面向对象编程中的一个重要特性,可以让类的行为更…...
golang time包和日期函数
1.简介 在程序中日期和时间是我们经常会用到的,在go中time包提供了时间的显示和测量函数。 2.获取当前时间 通过time.Now()函数获取当前时间对象,然后获取时间对象的年月日时分秒等值。 now : time.Now()fmt.Printf("now%v type%T\n", now…...
第7章 站在对象模型的尖端2: 异常处理
1.异常处理(Exception Handling) C的异常处理由三个主要组成部分构成:throw表达式、catch子句和try块。当异常被抛出时,程序控制权会转移,并且会沿着调用堆栈回溯,直到找到匹配的catch子句。在此过程中,局部对象的析构…...