week1-一周笔记及知识点补充:容器、迭代器、滑动窗口、sort()


文章目录

  • 前言
  • 一、总概括
  • 二、不熟悉的知识点补充
    • 1.容器相关
      • 1.1类模板
      • 1.2容器类模板和容器类
      • 1.3常见容器分类
      • 1.4 容器类的典型使用场景
      • 1.5容器类使用的性能优化技巧
      • 1.6容器适配器及使用方法
      • 1.7复杂容器
      • 1.8 其他使用过未提及的容器的成员函数
      • 1.9容器内插入、删除的优化建议
    • 2.迭代器
      • 基本概念
    • 3.滑动窗口
      • 3.1 核心思想
      • 3.2 典型场景
      • 3.3 关键技巧
    • 4.sort()函数
      • 4.1原型与参数
      • 4.2其他相关排序函数
  • 总结与感想


前言

记录一下我一周做了什么,以及查缺补漏。


一、总概括

认识并熟悉了滑动窗口的概念;接触了各类容器及其可以使用的方法;对算法有了初步了解。

二、不熟悉的知识点补充

注:以下补充来源基于大数据模型,不一定完全准确。

1.容器相关

1.1类模板

  • 类模板允许定义一个通用的类类型,具体行为由类型参数(Type Parameters)决定。
template<typename T>
class MyContainer {
    // 类定义...
};	
  • 实例化方法:
	MyContainer<int> myIntContainer;  // 实例化为具体类

与普通类的区别:

特性 类模板 普通类
泛型性 支持泛型编程,可针对不同类型生成具体类 固定类型,无法动态改变行为
编译时实例化 在使用时根据类型参数生成具体类 编译时直接定义具体类
代码复用 通过模板参数复用代码 需要手动为不同类型编写重复代码

1.2容器类模板和容器类

  • 容器类模板是一个泛型类模板,容器类是其实例化产物。两者概念相近又相区别。
  • 容器类(Container Class)​是一种用于存储和管理其他对象的数据结构模板,属于标准模板库(STL)的核心组成部分。它的本质是通过泛型编程技术实现对象的动态组织、访问和操作。
    必须先引入对应名称的头文件才能使用;
    必须通过类型参数显式实例化才能使用:
std::unordered_set<int> mySet;    // 键类型为 int 的无序集合
std::map<std::string, double> myMap; // 键为 string,值为 double 的有序映射

下文默认已使用using namespace std;

  • 容器类基本特性:泛型性、内存自动管理、接口统一、支持迭代器。
  • 容器类的核心作用:
    • 动态存储对象
    • 提供标准接口
      • 增删改查:insert()插入、erase()删除、find()查找
      • 遍历: iterator迭代器、 Range-based for loop范围循环
      • 统计信息:size()大小、capacity()容量
    • 支持泛型算法sort()、find_if(),通过迭代器传递范围
    • 提升代码复用

1.3常见容器分类

容器类型 特性 实现方式(类模板)
序列容器 存储顺序元素,支持随机访问 vector,list,array
关联容器 基于键存储元素,支持快速查找 map,set,unordered_map,unordered_set
容器适配器 对现有容器的接口进行封装 stack,queue,priority_queue
复杂容器 组合多个基础容器或算法 multiset

1.4 容器类的典型使用场景

  • 序列容器
    • vector:类线性表,在需要动态数组支持随机访问(如游戏角色属性列表、图像像素数据)、尾部插入/删除频繁(如日志记录)的场景。
    • list:类链表,频繁在中间插入/删除(如链表节点管理、实时协作编辑中的操作记录)、不随机访问,仅顺序遍历(如消息队列)的场景。
    • array:大小固定的数组(如数学矩阵运算)。
    • deque:双端队列,支持前端和后端的快速插入/删除(如BFS序列、滑动窗口)
  • 关联容器
    • map/set:在按键有序存储且唯一(如字典、学生成绩单)、插入,删除,查找频繁且要求延迟低(如数据库索引)的场景。基于红黑树实现。
    • unordered_map、unordered_set:需要快速查询唯一键是否存在(如用户登录状态验证)、插入删除操作远多于查找(如缓存系统)的场景。
  • 容器适配器
    • stack: LIFO、FIFO的数据结构(如浏览器历史记录、任务队列)。
    • priority_queue:需要快速获取最值(如游戏中的优先级队列)的场景。
  • 复杂容器
    • multiset:允许重复键的有序集合(如成绩统计中的分数列表)。
    • unordered_multiset、unordered_multimap:允许重复键且快速查询的哈希表(如计数器、词频统计)。

1.5容器类使用的性能优化技巧

  • 选择合适的底层容器:
    • stack通常用vector实现,若需要更快的弹出速度可选deque。
    • priority_queue默认基于vector,需要更快插入可改为priority_queue
  • 哈希表的负载因子:
    • unordered_map的负载因子load_factor()默认为1,超过0.75时需重新哈希(rehash),避免性能下降。
  • 避免不必要的复制:
    • 使用move移动语义(C++11 新增)优化大对象的插入操作。
  • 优先选择连续内存存储的容器(如vector)以利用CPU缓存;
  • 关联容器插入删除操作频繁时,哈希表(unordered_map)比红黑树(map)更快。

1.6容器适配器及使用方法

容器适配器是基于现有容器的接口封装,提供特定行为(LIFO、FIFO)。常见有stack、queue、priority_queue。
  • stack:
    #include
    ...
    //使用deque作为底层容器
    stack<string> s;
    ...
    
    • 关键操作:push(),pop(),top(),empty(),size()。
  • queue:
    #include
    ...
    queue<int> q;
    ...
    
    • 关键操作:push(),pop()队首元素出队,front()获取队首元素,back()获取队尾元素。
  • priority_queue优先队列:
    #include
    ...
    //大根堆(默认)	
    priority_queue<int> max_heap;
    //小根堆(自定义比较器)
    priority_queue<int,vector<int>,greater<int>> min_heap;
    ...
    
    • 关键操作:push(),pop(),top(),size()

1.7复杂容器

复杂容器通过组合基础容器和算法实现更高级功能,常见有multiset,unordered_multiset,multimap。
用到再回来展开。

1.8 其他使用过未提及的容器的成员函数

  • emplace_back():用于在容器末尾高效插入新元素,核心优势在于直接在容器内构造新元素,避免临时对象的拷贝或移动开销。尤其适用于复杂类型或大对象的场景。
    • 区别于push_back():接受已构造好的对象,将其复制或移动到容器中。

这里不在代码范围内的*号指容器。

  • insert():向容器中插入元素。
    • *.insert(value): 插入单个元素。
    • *.insert(begin,end,value):将多个相同值插入到迭代器指定的范围内。
    • *.insert(position,other_container.begin(),other_container.end()):在目标position位置插入其他容器内由迭代器指定的范围内元素。
    • auto it=*.insert(value):返回值为插入位置的迭代器。
      单个参数的返回值为插入位置的迭代器。
  • erase():删除元素。
    • *.erase(value):删除所有指定值的元素。
    • *.erase(iteraor):删除迭代器指向的元素。
    • *.erase(first,last):删除由迭代器指向的区间内的所有元素。
    • 若容器中有多个具有相同值的元素,erase(value)会删除所有等于该值的元素。

1.9容器内插入、删除的优化建议

  • 尾部插入时,优先使用emplace_back():
  • 中间插入时,使用insert();尽量不要在中间插入。
  • 避免在遍历时删除元素;一定要这么做要使用反向迭代器或指针:
    for(auto it=vec.rbegin();it!=vec.rend();++it){
    	if(*it ==42)erase(it);
    }
    
  • 处理erase()的返回值:删除迭代器后,立即获取下一个有效迭代器:
    
    auto it = vec.find(42);
    if(it!=vec.end()){
    	if=vec.erase(it);//现在指向被删除元素的下一个元素
    }
    
    
  • 批量插入、删除:
    • 使用insert()的范围插入(避免多次单次插入)。
    • 使用clear()的清空容器。

2.迭代器

基本概念

  • 定义:迭代器指向容器中的一个位置,可通过解引用(*it)获取元素的值,通过递增(++it)移动到下一个位置。
  • 五种迭代器类别(功能从低到高):
    • 输入迭代器Input Iterator
    • 输出迭代器Output Iterator
    • 正向迭代器Forward Iterator
    • 双向迭代器Bidirectional Iterator
    • 随机访问迭代器Random Access Iterator
      -目前常见的使用到关于迭代器内容的有.begin()和.end()。两方法获取的迭代器类型取决于容器自身特性。

之后涉及多了再详细展开。

3.滑动窗口

滑动窗口是算法设计中一种常用的计数,尤其在处理连续子数组或子字符串问题时非常高效。它通过维护一个动态窗口(通常使用左右指针定义范围)来遍历数据,满足时间复杂度O(n)的需求。

3.1 核心思想

  • 定义:用两个指针left和right表示当前窗口的左右边界(闭区间,[left,right])。
  • 窗口滑动:右指针right向右扩展窗口,左指针left向右收缩窗口以维持条件。
  • 目标条件:窗口内元素需满足特定要求,如最值、唯一性、特定统计等特性。

3.2 典型场景

  • 最值问题:在每个窗口中找到最值。
  • 无重复字符的最长字串:找到字符串s中不含重复字符的最长字串的长度。
    • 记得我看过的代码中别人使用了contains(x) 方法来寻找窗口中是否存在x元素,这还需要另外的库函数。直接使用find(x)应该就可以了。
  • 子数组和等于目标值:找到和为目标值的连续子数组。

3.3 关键技巧

  • 注意双指针的移动条件。
  • 避免重复计算:尽量复用窗口内的已有信息。
  • 边界处理:注意窗口大小不足时的初始化。

4.sort()函数

C++ 标准库中的sort()函数会根据不同情况动态选择不同排序算法实现高效排序。
不同编译器也会有不同的排序策略。

4.1原型与参数

	#include  // 必须包含此头文件

	template<typename Iterator>
	void sort(Iterator first, Iterator last);           // 升序排序

	template<typename Iterator, typename Compare>
	void sort(Iterator first, Iterator last, Compare comp); // 自定义比较规则
  • 参数:
    • first,last:排序范围的迭代器(左闭右开,[a,b))
    • comp:可选的自定义比较函数,返回bool。若comp(a,b)为true,则a应该排在b前,也就是原本的顺序不变。
  • 自定义排序函数:
    • 可传入排序函数,也可使用lambda表达式:
      • lambda表达式:
      	vector<pair<int, string>> vec = {{3, "apple"}, {1, "banana"}};
      	sort(vec.begin(), vec.end(), [](const auto& a, const auto& b) {//表达式中自动使用vec中元素进行按要求的比较
      		return a.first > b.first; // 按第一个元素降序排序
      	});
      
      • 使用过的有点复杂的排序函数:
      	bool comp(pair<char,int> a,pair<char,int> b){
      		//按值排序,值相同按同字母大小写排序,再按字母码表顺序排序 
      		if(a.second!=b.second)return a.second>b.second;
      		//a计数是否大于b计数,是则返回true表示不用交换,否则返回false表示要交换
      		if(islower(a.first)!=islower(b.first))return islower(a.first);
      		//两者是否一个大写一个小写。若是则只用根据a的大小写来确定是否与b换位
      		return a.first<b.first;//最后按照ASCII码大小进行比较
      	}
      

4.2其他相关排序函数

  • nth_element:找到第n大的元素并该元素处于正确位置,其余元素无序。
  • partial_sort:部分排序。
  • stable_sort:稳定排序。

总结与感想

通过持续不断的学习,逐渐对算法有了点经验。不过要学的东西还是太多,之后还须保持。
后续就得尝试脱离答案,自己来想思路写代码了。

你可能感兴趣的:(笔记,c++,算法)