序列式容器
一. vector :
vector采用一段连续的内存来存储其元素,向vector添加元素的时候,如果容量不足,vector便会重新malloc一段更大的
内存,然后把原内存中的数据memcpy到新的内存中,并free原内存块,然后将新元素加入。
vector的元素插入性能跟以下几个要素关系重大:
1. 插入的位置
头部插入:将所有元素后移,然后将新元素插入
中间插入:将插入点后面的元素后移,然后插入新元素
尾部插入:将新元素直接插入尾部
总结:
尾部插入无疑是最快的,头部插入最慢,中间插入次之,(尾部插入>中间插入>头部插入)
慢的原因:在于插入前要移动后面的所有元素。删除元素也是同样的道理。
2. 保留空间大小
如果插入元素是,空间不足将导致重新malloc以及一系列的内存拷贝。如果使用者能对容量有预期,
那么采用reserve()来预先分配内存,将大大的提高性能。
综述,vector适用于尾部插入,但是此时无法兼顾查找的性能,因为二分查找的vector要求重新排序,
或者要求vector在插入时就保持有序,这样就无法做到尾部插入。
但是vector作为动态数组的替代,已经足够优秀。
常用接口:
头文件: #include <vector>
vector<int> vec1;
vector<int> vec2(vec1);
vector<int> vec3(vec1.begin(),vec1.end());
vector<int> vec4(10);
vector<int> vec5(10,4);
vec1.push_back(100);
int size = vec1.size();
bool isEmpty = vec1.empty();
cout<<vec1[0]<<endl;
vec1.insert(vec1.end(),5,3);
vec1.pop_back();
vec1.erase(vec1.begin(),vec1.begin()+2);
cout<<(vec1==vec2)?true:false;
vector<int>::iterator iter = vec1.begin();
vector<int>::const_iterator c_iter = vec1.begin();
vec1.clear();
int length = vec1.size();
for(int i=0;i<length;i++)
{
cout<<vec1[i]<<" ";
}
cout<<endl;
vector<int>::iterator iter = vec1.begin();
for(;iter != vec1.end();iter++)
{
cout<<*iter<<" ";
}
cout<<endl;
用法一:
void test2()
{
vector<int> V1(10,1);
vector<int> V2(V1);
vector<int> V3(V1.begin(),V1.end());
cout<<(V2 == V3)?true:false;
cout<<endl;
vector<int> ::iterator iter = V3.begin();
for( ; iter < V3.end(); iter++ )
{
cout<<*iter<<" ";
}
cout<<endl;
}
用法二:
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> V1;
V1.push_back(1);
V1.push_back(2);
V1.insert(V1.end(),5,3);
int Size = V1.size();
for(int i = 0; i < Size; i++)
{
cout<<V1[i]<<" ";
}
cout<<endl;
vector<int> ::iterator iter = V1.begin();
for( ; iter < V1.end(); iter++ )
{
cout<<*iter<<" ";
}
cout<<endl;
vector<int> ::reverse_iterator riter = V1.rbegin();
for( ; riter < V1.rend(); riter++ )
{
cout<<*riter<<" ";
}
cout<<endl;
return 0;
}
用法三:
void test3()
{
vector<int> V1(10,1);
V1.erase(V1.begin(),V1.begin()+2);
V1.pop_back();
V1.resize(8);
vector<int> ::iterator iter = V1.begin();
for( ; iter < V1.end(); iter++ )
{
cout<<*iter<<" ";
}
cout<<endl;
V1.clear();
cout<<V1.empty()<<endl;
}
二. list:
list很简单,它就是个双向链表。每一个节点的内存都是独立的。
理论上,其优点是:任何位置的插入删除元素操作都是非常快的。
缺点是:不适合用于元素的查找,因为他只能是扫描的方式。
单链表和双向链表的优缺点:
1. 结构上而言:双向链表复杂,占用的空间大一点
2. 插入和删除:双向链表简单,更实用(因为在删除和插入时,需要找到前一个prev,而双向链表不用找)
3. 时间复杂度:双向链表O(1);单链表O(n)
既然双向链表的优点很多,为什么单链表也很重要?
因为:单链表会在哈希表的拉链法中会用到,还有图中会用到,应用还是很广的。
如果单用链表时,双向链表会比较好。
常用的接口:
头文件: #include <list>
list<int> lst1;
list<int> lst2(3);
list<int> lst3(3,2);
list<int> lst4(lst2);
list<int> lst5(lst2.begin(),lst2.end());
lst1.push_back(10);
lst1.pop_back();
lst1.begin();
lst1.end();
lst1.clear();
bool isEmpty1 = lst1.empty();
lst1.erase(lst1.begin(),lst1.end());
lst1.front();
lst1.back();
lst1.insert(lst1.begin(),3,2);
lst1.rbegin();
lst1.remove(2);
lst1.reverse();
lst1.size();
lst1.sort();
lst1.unique();
list<int>::iterator iter = lst1.begin();
for(;iter != lst1.end();iter++)
{
cout<<*iter<<" ";
}
cout<<endl;
用法一:初始化
#include <iostream>
#include <list>
using namespace std;
void test1()
{
list<int> lst1;
list<int> lst2(3);
list<int> lst3(3,2);
list<int> lst4(lst2);
list<int> lst5(lst2.begin(),lst2.end());
cout<<(lst4 == lst5)?true:false;
cout<<endl;
list<int>::iterator iter = lst3.begin();
for(;iter != lst3.end();iter++)
{
cout<<*iter<<" ";
}
cout<<endl;
}
int main()
{
test1();
return 0;
}
用法二:头插头删、尾插尾删
void test2()
{
list<int> l1;
l1.push_back(1);
l1.push_back(2);
l1.push_back(3);
list<int>::iterator iter1 = l1.begin();
for(;iter1 != l1.end();iter1++)
{
cout<<*iter1<<" ";
}
cout<<endl;
l1.pop_back();
l1.push_back(3);
l1.pop_front();
l1.push_front(1);
l1.insert(l1.end(),2,4);
list<int>::iterator iter2 = l1.begin();
for(;iter2 != l1.end();iter2++)
{
cout<<*iter2<<" ";
}
cout<<endl;
}
用法三:unique( ) & sort( )
1. unique( ):只是将相邻的相同元素删除
void test2()
{
list<int> l1;
l1.push_back(1);
l1.push_back(1);
l1.push_back(1);
l1.push_back(2);
l1.push_back(1);
l1.push_back(3);
l1.unique();
list<int>::iterator iter1 = l1.begin();
for(;iter1 != l1.end();iter1++)
{
cout<<*iter1<<" ";
}
cout<<endl;
}
去重:先排序sort( ),再unique( )
void test2()
{
list<int> l1;
l1.push_back(1);
l1.push_back(1);
l1.push_back(1);
l1.push_back(2);
l1.push_back(1);
l1.push_back(3);
l1.sort();
l1.unique();
list<int>::iterator iter1 = l1.begin();
for(;iter1 != l1.end();iter1++)
{
cout<<*iter1<<" ";
}
cout<<endl;
}
用法四: 逆置:reverse( ) || 反向迭代器
1. reverse( )
void test2()
{
list<int> l1;
l1.push_back(1);
l1.push_back(2);
l1.push_back(3);
l1.reverse();
list<int>::iterator iter1 = l1.begin();
for(;iter1 != l1.end();iter1++)
{
cout<<*iter1<<" ";
}
cout<<endl;
}
2. 反向迭代器
void test2()
{
list<int> l1;
l1.push_back(1);
l1.push_back(2);
l1.push_back(3);
list<int>::reverse_iterator iter1 = l1.rbegin();
for(;iter1 != l1.rend();iter1++)
{
cout<<*iter1<<" ";
}
cout<<endl;
}
三 . vector和list的区别:
相同点:
1. 都可以存储一组类型相同的元素
2. 尾部插入和删除,性能差不多
不同点:
1. 存储顺序:
vector :是顺序表,表示的是一块连续的内存,元素被顺序存储;
list : 是双向链表,在内存中不一定连续。
2. 扩容的开销:
vector : 当数值内存不够时,vector会重新申请一块足够大的连续内存,把原来的数据拷贝到新的内存里面
list : 因为不用考虑内存的连续,因此新增开销比vector小。
3. 头部或者中间的插入和删除:
vector :vector需要进行元素的移动
list : 性能比vector好
4. 随机访问:
vector : 直接通过下标就可以访问,时间复杂度为O(1)
list : 需要遍历,时间复杂度为O(n)
所以:vector的随机访问效率更高
总而言之:
1. 已知需要存储的元素时,vector要好
2. 如果需要任意位置插入元素,list要好