施磊老师c++(七)

news2025/3/17 20:46:55

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容器

  1. vector— 向量容器
    底层数据结构—>动态开辟的数组, 每次以原来空间大小的2倍进行扩容

  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: //交换两个元素
        
    
  3. 代码:
    注意区分 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; // 0
    
    	vec.resize(20); // 会放入元素0
    	cout << vec.empty() << endl; // 0
    	cout << vec.size() << endl; // 0
    
    	for (int i = 0; i < 20; ++i)
    	{
    		vec.push_back(rand() % 100+1);
    	}
    	cout << vec.empty() << endl; //0   0
    	cout << vec.size() << endl;//20    40
    
    
    	#if 0
    	int 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;
    
    	#endif
    
    	return 0;
    }
    
    

3.deque和list

二者 比vector 多出来的 常用方法: push_front, pop_front

deque–双端队列容器

  1. 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处
    
    
  2. 常用方法:

    #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–链表容器

  1. list–链表容器
    底层数据结构–双向的循环链表 (pre,data,next)

  2. 常用方法:

    #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. 不要只学习表面, 多看看底层

  2. 特点回顾

    1.vector特点:
    	动态数组,内存是连续的, 2倍的方式进行扩容, vector<int> vec; reserve和resize区别
    
    2.deque特点:
    	动态开辟的二维数组, 第一维数组个数从2开始, 以2倍方式扩容, 每次扩容后, 原来第二维的数组, 从新的第一维数组的下标oldsize/2开始存放, 方便首尾元素添加
            
            
            
    

面经问题

  1. deque的底层内存是不是连续的? – 分块存储
    并不是, 每一个 第二维的是 连续的, 但是 第二维之间 不是连续的

  2. 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.造成元素移动要慢, 
    
    
  3. vector 与 list 区别?

    1.  底层数据结构不同  list是双向循环链表
    //一般的数组和链表,  数组:增加删除O(n), 查询O(n), 随机访问(1)
    //链表, 增删本身是O(1), 但是还要查询,O(n), 没有随机访问
    
    
    

5.详解容器适配器–stack, queue, priority_queue

容器适配器

  1. 注意区别 容器适配器和容器空间配置器

    stack,queue,priority_queue之所以叫做适配器,是因为它们没有自己底层的数据结构,是依赖另外的容器来实现的功能,它们的方法,也是通过它们依赖的容器相应的方法实现的。
    
  2. 怎么理解适配器? –有一种设计模式就是适配器模式
    底层没有自己的数据结构, 他是对另外容器的封装, 他的方法 全部由 底层依赖 的容器 进行实现

    stack  源码, 底层用的就是deque
    _EXPORT_STD template <class _Ty, class _Container = deque<_Ty>>
    
    
    
  3. 容器适配器:stack, queue, priority_queue ---- 重点, 使用频率高

stack-栈

  1. 常用方法: ===>依赖deque

    1. push---入栈
    2. pop--出栈
    3. top--查看栈顶元素
    4. empty--判空
    5. size--返回元素个数
    
    
    
  2. 代码:

    #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-队列

  1. 常用方法: fifo 先入先出 ===>依赖deque

    1. push---入队列
    2. pop--出队列, 队头出
    3. front--查看队头
    4. back--查看队尾
    5. empty--判空
    6. size--返回元素个数
    
    
    

priority_queue-优先级队列

  1. 常用方法: ===>依赖vector 默认大根堆

    1. push---入队列
    2. pop--出队列
    3. top--查看队顶元素
    4. empty--判空
    5. size--返回元素个数
    
    
    

总结

  1. 代码:

    #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;
    }
    
    
  2. 为什么有的依赖 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

  1. 链式哈希表作为底层数据结构无序关联容器有:: — unordered_…
    增删查–O(1)

    unordered_set   单重集合   单重就是不允许数重复
    unordered_multiset  多重集合   // #include<unordered_set>
    unordered_map
    unordered_multimap        // #include<unordered_map>
    
    
    
  2. 红黑树作为底层数据结构的有序关联容器有:
    增删查–O(log2 n) 2是底数,树的高度

    set
    multiset    // #include<set>
    map
    multimap    // #include<map>
    
  3. 关联容器 与 vector,deque, list的 函数使用注意点
    与 vector,deque, list不同,这些的insert是两个参数, 因为是线性表, 需要指定位置
    但是 由于关联容器 底层是 哈希表 或 红黑树, 插入的位置不是随机的,一个是按哈希公式, 一个是根据红黑树性质

    unordered_set<int> set1;
    
    set1.insert(20) 
    
    

unordered_set

  1. 切记: 单重集合不存储 重复元素!!!

  2. find()------有则返回迭代器, 不存在则返回末尾迭代器

  3. c++11 的 foreach 正规名叫 基于范围的 for 循环(range-based for loop)

  4. 关联容器常用方法:

    增: insert
    删: erase(key), erase(it)  --- key和迭代器都行
    遍历: iterator, find(key)
    
  5. 代码:

    #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

  1. map是存储键值对[key, val], set只存储 key

  2. first->key, second->val ===> 依赖于 pair 类

  3. operator[] 要注意, 看代码

  4. 代码:

    #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;  //2
    	cout << map[1000] << endl;
    	cout << map[1003] << endl;   // []重载,不仅能查询, 而且key不存在时, 会添加这个键值对, string() , 实际打印出来就什么也没有, []还能修改
    	cout << map.size() << endl;  //3 
    
    
    	// 遍历map表1
    	auto it = map.begin();    // 迭代器是 打包的 pair对象
    	for (; it != map.end(); ++it)
    	{
    		cout << "key:" << it->first << " value:" << it->second << endl;
    	}
    	// 遍历map表2
    	for (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;
    }
    

应用

  1. 无序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;
    }
    
  2. 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;
    }
    
  3. 哈希桶? – 不是很明白这个到底是啥

    #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

  1. 单重set, 不重复元素, 但有序
  2. 常用方法 与 unordered 一模一样

set

  1. 自定义类型呢? 需要手动 提供自定义类型的 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

  1. 代码:

    #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注意

  1. 元素访问, 可以是 it->first, 也可以是 (*it).first, 一般使用第一个, 更方便

8.迭代器iterator

iterator, const_iterator, reverse_iterator, const_reverse_iterator

  1. 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;
    }
    
  2. 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;
    }
    
  3. 为什么 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; }
    };
    
  4. 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.函数对象–仿函数

  1. 函数对象–> 就是 c里面的 函数指针
    函数对象(Function Object),也称为仿函数(Functor),是 C++ 中的一个重要概念。它是一个类或结构体,通过重载 operator() 运算符,使得该类的对象可以像函数一样被调用。

  2. 对比下面两个:

    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);
    
  3. 关于内联和函数指针代码:

    #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);
    }
    
  4. 使用函数对象(仿函数)解决函数指针调用开销问题

    #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;
    }
    
  5. 函数对象好处

    1. 通过函数对象调用operator(), 可以省略函数的调用开销, 比通过函数指针调用函数(不能使用内联)效率高
    2. 因为函数对象是用类生成的, 所以可以添加 相关的 成员变量, 用于记录函数对象使用时的更多信息
    
    
    
  6. 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

  7. using

    using 是 C++ 中一个非常强大的关键字,主要用途包括:
    
    类型别名:定义类型别名,类似于 typedef。
    
    模板别名:为模板定义别名。
    
    命名空间成员引入:引入命名空间中的特定成员。
    
    命名空间整体引入:引入整个命名空间。
    
    基类成员引入:在派生类中引入基类的成员。
    
    构造函数继承:继承基类的构造函数。
    
    模板中使用:在模板中定义类型别名。
    
    函数中使用:在函数内部定义类型别名。
    

10.泛型算法和绑定器

  1. 泛型算法头文件

    #include <algorithm>
    
  2. 泛型算法特点: — c++ primer书里有很多 泛型算法

    1. 接收的都是 迭代器
    	sort, find, find_if:有条件的find, binary_search:二分查找, for_each
    
    2. 还能接受函数对象
    
    3.模板实现的+迭代器+函数对象
    
  3. 绑定器:

    bind1st: 把二元函数对象的operator()的第一个形参绑定起来
    bind2nd:把二元函数对象的operator()的第二个形参绑定起来
        
    #include <functional>  包含函数对象和绑定器
    

    绑定器+二元函数对象==>一元函数对象

  4. 代码:

    #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]); // 计算数组大小
    
        // 使用范围构造函数将数组元素放入 vector
        vector<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 element
        return _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;
    }
    

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

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

相关文章

C++初阶——类和对象(三) 构造函数、析构函数

C初阶——类和对象&#xff08;三&#xff09; 上期内容&#xff0c;我们围绕类对象模型的大小计算&#xff0c;成员存储方式&#xff0c;this指针&#xff0c;以及C实现栈和C语言的比较&#xff0c;进一步认识了C的封装特性。本期内容&#xff0c;我们开始介绍类的默认成员函…

【Function】使用托管身份调用Function App触发器,以增强安全性

推荐超级课程: 本地离线DeepSeek AI方案部署实战教程【完全版】Docker快速入门到精通Kubernetes入门到大师通关课AWS云服务快速入门实战目录 1. 背景介绍2. 设置3. 使用Web应用调用Function App触发器(Node.js示例)4. 执行结果此方法允许您使用托管身份(Managed Identity)调…

x012-MSP430F249智能步进电动百叶窗_proteus_光敏电阻_步进电机_仿真

https://www.dong-blog.fun/post/1997 46 、智能步进电动百叶窗 基本要求&#xff1a; 用一台步进电机控制百叶窗叶片的旋转&#xff08;正转/反转&#xff09; 用 LED 数码管显示旋转角度 设置按键&#xff1a; 手动/自动切换、手动正转和手动反转&#xff0c;停止/启动键 用一…

牛客周赛85 题解 Java ABCDEFG

A小紫的均势博弈 判断输入的 n 是奇数还是偶数 import java.io.*; import java.math.*; import java.util.*;public class Main {static IoScanner sc new IoScanner();static final int mod(int) (1e97);static void solve() throws IOException {int nsc.nextInt();if(n%2…

# RAG 框架 # 一文入门 全链路RAG系统构建与优化 —— 架构、策略与实践

本文全面阐述了RAG系统从数据收集、数据清洗&#xff08;包括领域专有名词处理&#xff09;、智能数据分块与QA对生成&#xff0c;到向量化、向量数据库选择与配置&#xff0c;再到检索方式及重排序&#xff0c;直至整合输出、监控反馈和安全保障的全流程。通过这一完整方案&am…

【Golang】第二弹-----变量、基本数据类型、标识符

笔上得来终觉浅,绝知此事要躬行 &#x1f525; 个人主页&#xff1a;星云爱编程 &#x1f525; 所属专栏&#xff1a;Golang &#x1f337;追光的人&#xff0c;终会万丈光芒 &#x1f389;欢迎大家点赞&#x1f44d;评论&#x1f4dd;收藏⭐文章 目录 一、变量 1.1基本介绍…

linux系统CentOS 7版本搭建NFS共享存储

一、什么是NFS共享存储方式 NFS共享存储方式 是一种分布式文件系统协议&#xff0c;允许客户端通过网络访问远程服务器上的文件&#xff0c;就像访问本地文件一样。 二、 NFS的基本概念 &#xff08;1&#xff09;服务器端&#xff1a;提供共享存储的机器&#xff0c;负责导…

Matlab 基于SVPWM的VF三电平逆变器异步电机速度控制

1、内容简介 略 Matlab 167-基于SVPWM的VF三电平逆变器异步电机速度控制 可以交流、咨询、答疑 2、内容说明 略 3、仿真分析 略 4、参考论文 略

(一)微服务初见之 Spring Cloud 介绍

微服务架构简介 从单体应用架构发展到SOA架构&#xff0c;再到微服务架构&#xff0c;应用架构经历了多年的不断演进。微服务架构不是凭空产生的&#xff0c;而是技术发展的必然结果&#xff0c;分布式云平台的应用环境使得微服务代替单体应用成为互联网大型系统的架构选择。目…

架构思维:软件建模与架构设计的关键要点

文章目录 1. 软件建模的核心概念2. 七种常用UML图及其应用场景类图时序图组件图部署图用例图状态图活动图 3. 软件设计文档的三阶段结构4. 架构设计的关键实践1. 用例图&#xff1a;核心功能模块2. 部署图&#xff1a;架构演进阶段3. 技术挑战与解决方案4. 关键架构图示例5. 架…

【RNN神经网络】序列模型与RNN神经网络

前言 清库存。正式切入大模型后&#xff0c;打算把基础知识都梳理一遍&#xff0c;然后写了两篇就发现写不动了&#xff0c;后面就捡重要的记录。RNN知识仅此一篇记录&#xff0c;扫盲记录。 【自然语言处理】 &#xff08;Natural Language Processing&#xff0c;NLP&#xf…

Python文件管理

目录 一、文本文件读写 1、相关函数 2、读写文件 3、使用readline读取一行 4、读写文件的异常处理 5、添加内容 二、文本文件的编码 1、常见的编码 2、Python程序的编码 3、指定编码 三、文件的路径 1、相对路径 2、绝对路径 3、路径的改变 四、文件夹操作 五、…

vue3 前端路由权限控制与字典数据缓存实践(附Demo)

目录 前言1. 基本知识2. Demo3. 实战 前言 &#x1f91f; 找工作&#xff0c;来万码优才&#xff1a;&#x1f449; #小程序://万码优才/r6rqmzDaXpYkJZF 从实战中出发&#xff1a; 1. 基本知识 Vue3 和 Java 通信时如何进行字典数据管理 需要了解字典数据的结构。通常&#x…

基于javaweb的SpringBoot精美物流管理系统设计与实现(源码+文档+部署讲解)

技术范围&#xff1a;SpringBoot、Vue、SSM、HLMT、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容&#xff1a;免费功能设计、开题报告、任务书、中期检查PPT、系统功能实现、代码编写、论文编写和辅导、论…

DeepSeek进阶应用(二):结合Kimi制作PPT(双AI协作教程)

&#x1f31f;引言&#xff1a; DeepSeek作为国产AI大模型&#xff0c;以强大的逻辑推理和结构化内容生成能力著称&#xff0c;擅长根据用户需求生成PPT大纲或Markdown文本&#xff1b;Kimi的PPT助手则能解析结构化内容并套用模板快速生成美观的PPT&#xff0c;两者结合实现“内…

SpringBoot——Maven篇

Spring Boot 是一个用于快速开发基于 Spring 框架的应用程序的工具。它具有许多特性&#xff0c;其中一些重要的特性包括&#xff1a; 1. 自动配置&#xff1a;Spring Boot 提供了自动配置的机制&#xff0c;可以根据应用程序的依赖和环境自动配置应用程序的各种组件&#xff…

卷积神经网络(知识点)

一、为了使特征图变小&#xff1a; 由两种方法&#xff1a;1.增大步长&#xff1a;卷积的时候不是一次一步&#xff0c;而是一次多步&#xff0c;类似一张图片&#xff0c;在原来的像素基础上&#xff0c;每隔一个取一个像素点。 其中S就是步长 注意&#xff1a;扩大步长不经…

Vision Transformer (ViT):将Transformer带入计算机视觉的革命性尝试(代码实现)

Vision Transformer (ViT)&#xff1a;将Transformer带入计算机视觉的革命性尝试 作为一名深度学习研究者&#xff0c;如果你对自然语言处理&#xff08;NLP&#xff09;领域的Transformer架构了如指掌&#xff0c;那么你一定不会对它在序列建模中的强大能力感到陌生。然而&am…

特殊 IP 地址

文章目录 特殊IP地址概述受限广播地址&#xff08;Limited Broadcast Address&#xff09;直接广播地址&#xff08;Directed Broadcast Address&#xff09;多播地址&#xff08;Multicast Address&#xff09;环回地址&#xff08;Loopback Address&#xff09;本网络本主机&…

数学——A. K-divisible Sum + D. Exam in MAC

A. K-divisible Sum 题目&#xff1a; 思路&#xff1a; 以下 “[xxx]” 符号均代表向上取整 我们假设总和是sum&#xff0c;那么就有sum k * cnt 要想最大值最小&#xff0c;肯定是要让sum尽可能小&#xff0c;这样每个元素都能变小 最小情况是 sum 恰好等于 n 时&#…