创作活动
迭代器失效:
- 顺序容器(如
vector
、deque
、list
)vector
- 插入操作:
- 当在
vector
中间或头部插入元素时,所有位于插入点之后的迭代器都会失效。这是因为vector
的元素在内存中是连续存储的,插入元素可能会导致内存重新分配和元素的移动。例如,有一个vector<int> v = {1,2,3,4,5};
,如果执行v.insert(v.begin() + 2, 6);
(在索引为 2 的位置插入 6),那么从v.begin()+2
之后的迭代器(包括指向 3、4、5 的迭代器)都将失效。 - 如果插入操作没有导致内存重新分配(例如在
vector
的尾部插入元素,且当前容量足够),那么只有指向插入位置的迭代器会失效。
- 当在
- 删除操作:
- 当删除
vector
中的元素时,所有指向被删除元素及其之后元素的迭代器都会失效。例如,对于vector<int> v = {1,2,3,4,5};
,如果执行v.erase(v.begin() + 2);
(删除索引为 2 的元素),那么指向 3、4、5 的迭代器都将失效。
- 当删除
- 插入操作:
deque
- 插入操作:
- 在
deque
中间插入元素时,指向插入位置及其之后元素的迭代器会失效。deque
是双端队列,它的内部实现是分段存储的,插入操作可能会导致部分元素的重新排列。例如,在一个deque
中间插入元素后,该位置之后的元素可能会被移动到新的段或者在当前段内重新排列。
- 在
- 删除操作:
- 删除
deque
中的元素时,指向被删除元素及其之后元素的迭代器会失效。和插入操作类似,因为元素的删除可能会导致剩余元素的重新排列。
- 删除
- 插入操作:
list
- 插入和删除操作:
- 对于
list
,插入和删除操作只会使指向被插入或删除元素的迭代器失效。这是因为list
是链表结构,元素在内存中不是连续存储的。插入或删除一个节点只影响该节点及其相邻节点的链接关系。例如,有一个list<int> l = {1,2,3,4,5};
,如果执行l.insert(l.begin() + 2, 6);
(在第二个位置插入 6),只有指向原来第二个位置元素(2)的迭代器可能会失效(如果之前有保存该迭代器的话),其他迭代器仍然有效。同样,对于l.erase(l.begin() + 2);
(删除第二个位置的元素),只有指向被删除元素的迭代器失效。
- 对于
- 插入和删除操作:
- 关联容器(如
map
、set
、multimap
、multiset
)- 插入操作:
- 在关联容器中插入元素不会使已有的迭代器失效。这是因为关联容器(如红黑树实现的
map
和set
)在插入新元素时,通过调整树的节点链接来保持平衡,而不会改变已有节点的地址。例如,对于一个map<int, int> m;
,插入新的键值对后,之前获取的指向其他键值对的迭代器仍然有效。
- 在关联容器中插入元素不会使已有的迭代器失效。这是因为关联容器(如红黑树实现的
- 删除操作:
- 当删除关联容器中的元素时,只有指向被删除元素的迭代器会失效。因为删除一个节点只会影响该节点本身,而不会改变其他节点的地址。例如,在一个
set<int> s = {1,2,3,4,5};
中,执行s.erase(s.find(3));
(删除元素 3),只有指向 3 的迭代器失效,指向 1、2、4、5 的迭代器仍然有效。
- 当删除关联容器中的元素时,只有指向被删除元素的迭代器会失效。因为删除一个节点只会影响该节点本身,而不会改变其他节点的地址。例如,在一个
- 插入操作:
- 无序关联容器(如
unordered_map
、unordered_set
等)- 插入操作:
- 一般情况下,插入元素不会使已有的迭代器失效。但是如果插入操作导致容器的哈希表进行了重新哈希(例如,当容器的负载因子超过一定阈值时),所有的迭代器都会失效。这种情况相对较少,但在使用时需要注意。
- 删除操作:
- 删除元素时,只有指向被删除元素的迭代器会失效。和关联容器类似,因为删除一个桶中的元素通常不会影响其他桶中的元素和迭代器。例如,在一个
unordered_set<int> us = {1,2,3,4,5};
中,执行us.erase(3);
,只有指向 3 的迭代器失效。
- 删除元素时,只有指向被删除元素的迭代器会失效。和关联容器类似,因为删除一个桶中的元素通常不会影响其他桶中的元素和迭代器。例如,在一个
- 插入操作:
支持随机访问的容器:
- 支持随机访问的容器
vector
(动态数组)- 存储结构:
vector
在内存中是连续存储的元素序列,就像一个普通的数组。例如,vector<int> v = {1,2,3,4,5};
在内存中是依次排列的,每个元素紧挨着前一个元素。这种连续存储方式使得它能够通过计算偏移量来快速访问元素。
- 访问方式:
- 可以使用
[]
运算符或者at()
函数进行随机访问。例如,要访问v
中的第三个元素,可以使用v[2]
或者v.at(2)
(注意at()
函数会进行边界检查,[]
运算符不会)。这种访问方式的时间复杂度是,因为可以直接通过计算元素的内存地址来获取元素。比如,对于一个vector
对象v
,第n
个元素的地址可以通过&v[0]+n
来计算(在符合语法规则的情况下)。
- 可以使用
- 迭代器支持:
vector
的迭代器是随机访问迭代器,支持算术运算。例如,可以通过it + n
或者it - n
来移动迭代器n
个位置,其中it
是vector
的迭代器。这使得在遍历vector
或者在其中进行元素查找等操作时更加灵活。比如,可以用vector<int>::iterator it = v.begin(); it += 3;
来快速定位到v
中的第四个元素(索引为 3)。
- 存储结构:
array
(固定大小数组)- 存储结构:
- 和
vector
类似,array
也是连续存储元素的容器。不过,array
的大小在编译时就确定了,不能动态改变。例如,array<int, 5> a = {1,2,3,4,5};
的大小始终是 5 个元素,并且在内存中是连续排列的。
- 和
- 访问方式:
- 同样可以使用
[]
运算符或者at()
函数进行随机访问,访问时间复杂度也是。例如,a[2]
可以获取a
中的第三个元素。因为其存储结构的连续性,计算元素地址的方式和vector
类似。
- 同样可以使用
- 迭代器支持:
array
的迭代器也是随机访问迭代器,支持算术运算。例如,可以像操作vector
的迭代器一样,通过it + n
或者it - n
来移动array
的迭代器n
个位置,方便对array
中的元素进行遍历和操作。
- 存储结构:
- 不支持随机访问的容器
list
(双向链表)- 存储结构:
list
是由一系列节点组成的双向链表。每个节点包含数据元素以及指向前一个节点和后一个节点的指针。例如,对于list<int> l = {1,2,3,4,5};
,元素 1 的节点包含指向元素 2 节点的后向指针和nullptr
(如果是头节点)的前向指针,元素 2 的节点包含指向元素 1 节点的前向指针和指向元素 3 节点的后向指针,以此类推。
- 访问方式:
- 不能像
vector
和array
那样直接通过索引来访问元素。要访问list
中的元素,通常需要从链表的头部或者某个已知位置开始,逐个节点地遍历。例如,要找到l
中的第三个元素,可能需要从l.begin()
开始,使用迭代器++
操作符移动两次才能找到。这种访问方式的时间复杂度是,其中n
是要访问的元素位置与起始位置的距离。
- 不能像
- 迭代器支持:
list
的迭代器是双向迭代器,不支持像随机访问迭代器那样的算术运算。只能通过++
和--
操作符来向前或者向后移动迭代器。例如,给定一个list<int>
的迭代器it
,不能使用it + 3
这样的操作来移动迭代器,而只能通过it++; it++; it++;
这样的方式来移动 3 个位置。
- 存储结构:
forward_list
(单向链表)- 存储结构:
forward_list
是单向链表,每个节点只包含一个指向下一个节点的指针。例如,对于forward_list<int> fl = {1,2,3,4,5};
,元素 1 的节点包含指向元素 2 节点的指针,元素 2 的节点包含指向元素 3 节点的指针,以此类推。
- 访问方式:
- 和
list
类似,不能通过索引直接访问元素,需要从链表头部开始逐个节点地遍历。由于是单向链表,只能向前遍历。访问特定元素的时间复杂度也是,其中n
是要访问的元素位置与起始位置的距离。
- 和
- 迭代器支持:
forward_list
的迭代器是前向迭代器,只能使用++
操作符向前移动迭代器。不能进行--
操作,也不支持算术运算。例如,要遍历forward_list
,只能通过for (auto it = fl.begin(); it!= fl.end(); ++it)
这样的方式来进行。
- 存储结构:
map
、set
及其关联容器(基于树结构)- 存储结构:
- 以
map
为例,通常是基于红黑树实现的。map
中的元素是以键值对的形式存储的,键是唯一的,并且按照一定的顺序(默认是升序)排列在树中。例如,对于map<int, int> m = {{1,10},{2,20},{3,30}};
,这些键值对在红黑树中按照键的大小进行排序。
- 以
- 访问方式:
- 不能通过索引进行随机访问。一般通过键来查找元素,时间复杂度是,其中
n
是容器中的元素数量。例如,要查找m
中键为 2 的元素,可以使用m.find(2)
,它会在红黑树中进行对数时间复杂度的查找。
- 不能通过索引进行随机访问。一般通过键来查找元素,时间复杂度是,其中
- 迭代器支持:
- 迭代器是双向迭代器(对于
map
和set
),可以使用++
和--
操作符来遍历容器中的元素。但是不支持算术运算,因为元素在树中的存储位置不是像数组那样连续的,不能通过简单的偏移量来计算元素位置。例如,在遍历map
时,迭代器会按照键的顺序依次访问元素。
- 迭代器是双向迭代器(对于
- 存储结构: