一、vector 和 list 的区别
一、底层实现
1.vector的底层实现是顺序表,在内存中是一串连续的空间
2.list的底层实现是带头节点的双向循环链表,在内存中不是一串连续的空间
二、随机访问
1.vector支持随机访问,通过下标精准定位到一个元素,访问元素的时间复杂度是O(1);
2.list不支持随机访问,想要访问某一个元素只能遍历整个链表,访问元素的时间复杂度为O(N);
三、插入和删除
1.vector的插入和删除的效率低,因为在每次的删除或插入(除了尾插和尾删)都需要移动元素,时间复杂度为O(N)。
由于vector的特性,在插入过程中有可能会牵扯到空间的扩容和数据的搬移,这会导致效率更为低下。
2.list的插入和删除的效率较高,因为他不需要数据的移动,只需要更改插入或删除的位置上的前后节点的指向就行。时间复杂度为O(1)。
4、迭代器失效问题
其底层实际就是一个指针,或者是对指针进行了封装因此迭代器失效,实际就是迭代器底层对应指针所指向的空间被销毁了,而使用一块已经被释放的空间,造成的后果是程序崩溃
关于vector插入操作的迭代器失效:
std::vector<int> v{1, 2, 3, 4, 5, 6};
auto it = v.begin();
v.resize(100, 8);
while(it != v.end())
{
cout<< *it << " " ;
++it;
}
此时迭代器就已经失效了,解决方法:
for (const auto& element : v)
{
// 使用element进行操作,而不是直接使用迭代器
std::cout << element << " ";
}
在使用for-each循环时,编译器会自动处理迭代器的访问,它会确保在每次迭代时都重新计算正确的元素位置。,
二、vector 重要函数assign(),resize(),reserve()
动态扩展:(这个概念很重要)
动态扩展并不是在原空间之后续接新空间,而是找到比原来更大的内存空间,将原数据拷贝到新空间,释放原空间
1.resize() 和 reserve()
resize(int num); //重新指定容器的长度为num,若容器变长,则以默认值0填充新位置
如果容器变短,则末尾超过容器长度的元素被删除
resize(int num,int elem); //重新指定容器的长度为num,若容器变长,则以elem填充新位置
如果容器变短,则末尾超过容器长度的元素被删除
resize和reserve函数本质都涉及了vector的内存存储空间,因为vector在内存中是连续存放的,所以当resize的空间大于现有的存储空间(capacity() 函数 返回当前vector在重新进行内存分配以前所能容纳的元素数量.)时,会重新选择更大的空间,并将所有元素复制过去。resize在初始化内存容量时有对值的初始化,所以此时push_back会产生size+1,内存容量不够,重新寻找更大的内存空间并复制所有元素,所以这个过程是很费事的。
vector<int> vector1;
vector1.resize(10);
vector1.push_back(1);
一般vector容器扩容是按原容量的1.5倍扩容,可以使用reserve(),提前就让vector容器开好空间避免时间的浪费,所以先reserve()再pushback()可以大大增加效率
2.clear()
当调用clear()时,清除的是vector的内部数据,而不是vector本身容量,可以这样
int main()
{
vector<int> v;
v.resize(100);
{
vector <int> v2;
swap(v2,v1);
}
}
我们在块作用域中定义一个空的vector对象再相互交换,当块作用域结束后自动释放
这样就可以释放掉原来大内存的vector对象
结论:
vector::resize() 使用数组,效率最高,但是需要提前知道size大小
vector::reserve()使用 push_back(),效率一般,较原生有一定提升。
3.assign()
- 以 count 份 value 的副本替换内容。
- 以范围 [first, last) 中元素的副本替换内容。其中有任何一个迭代器是指向 *this 中的迭代器时行为未定义。
如果 InputIt 是整数类型,那么此重载与 (1) 拥有相同效果。
(C++11 前)
此重载只有在InputIt 满足老式输入迭代器 (LegacyInputIterator) 时才会参与重载决议。
(C++11 起)
3) 以来自 initializer_list ilist 的元素替换内容。