简介
容器是编程使用频率非常高的一种数据结构,必要的了解STL容器能帮助我们更好的使用,在对应的场景中使用最为恰当的容器能够提升程序的运行效率。
序列式容器
vector
vector<T>(向量容器)
- 数据结构
- 内存分配情况
vector<T>是一种可变长的容器,那么它是如何做到可变长的呢?步骤如下:
1. 首先vector中没有插入元素时,那么他的空间大小为0,当插入一个元素这时控件不够,产生一个类型长度的空间,这时空间大小为1;
2. 插入第二个发现空间又不够,那么就分配一个2倍大小的空间,将原来的数据拷贝过来,删除原来的空间,这时空间大小为2;
3. 插入第三个发现空间又不够,那么就分配一个原来2倍大小的空间,将原来的数据拷贝,删除原来的空间,这时空间大小为4;
4. 插入第四个发送空间是够的无需重新分配,插入第五个发现又不够,以此类推进行2倍空间增长分配,达到动态分配控件的作用;
vector<int> vt ;
vt.push_back(1); // 第一次插入数据
cout <<"capacity size :" << vt.capacity() << endl ;
vt.push_back(2); // 第二次插入数据
cout <<"capacity size :" << vt.capacity() << endl ;
vt.push_back(3); // 第三次插入数据
cout <<"capacity size :" << vt.capacity() << endl;
vt.push_back(4); // 第四次插入数据
cout <<"capacity size :" << vt.capacity() << endl;
vt.push_back(5); // 第五次插入数据
cout <<"capacity size :" << vt.capacity() << endl;
- 优缺点
优点 | 缺点 |
由于是动态分配空间,所以节省空间 | 当空间不足是要进行重新开辟空间,拷贝,删除等操作 |
在最后面插入或者删除数据比较快、查询速度比较快 | 从中间插入或者删除数据比较慢,因为插入或者删除数据后面的数据要整体向前或者向后移动 |
- 测试
vector<int> vt ;
clock_t timeStart = clock();
for(int i = 0 ; i < 10000000 ; ++ i) // 插入一千万条数据
vt.push_back(i);
cout << "push_back milli seconds : " << clock() - timeStart << endl;
timeStart = clock();
find(vt.begin(),vt.end(),23675);
cout << "find milli seconds : " << clock() - timeStart << endl ;
可以看到插入1000万条数据时间为700多毫秒,但是查询一个数据的时间几乎小于1ms;
array
array<T,size> 定长数组容器
- 数据结构
- 内存分配空间
这里有个有趣的东西,假如数组是在函数内定义,那么其开辟的空间不能超过栈的空间大小(一般栈的空间大小为1M或者2M,假如为2M那么申请的空间不能大于1024*1024个字节),否则就会出现段错误,空间不够;但是数组如果放在全局变量,那么其分配的空间在全局区(静态区,大小一般2G),所有可以分配很大的空间。
3. 优缺点
优点 | 缺点 |
插入数据快 | 空间浪费 |
查询数据很快 | 地址空间必须连续 |
- 测试
array<int,10000000> at; // 在全局区分配空间,空间足够不会报错
int compare(const void *p, const void *q)
{
return (*(int *)p - *(int *)q);
}
int main(int argc, char *argv[])
{
// array<int,10000000> at; // 栈内空间不够报错
clock_t timeStart = clock();
for(int i = 0 ; i < 10000000 ; ++ i) // 插入一千万条数据
at[i] = i ;
cout << "insert milli seconds : " << clock() - timeStart << endl;
timeStart = clock();
int key = 23675;
bsearch(&key,at.data(),10000000,sizeof(int),compare);
cout << "find milli seconds : " << clock() - timeStart << endl;
return 0;
}
list
list<T> 双向环状链表
- 数据结构
list::end()是指向最后一个数据的下一个,本身并不存储数据。 - 内存分配空间
list是一个双向链表,每添加一个元素就申请一块节点大小的内存地址空间,最后一个元素指向一个多出来的node,该node节点保存最后一个节点的地址和最开始节点的地址。 - 优缺点
优点 | 缺点 |
地址可以不是连续的 | 每次插入数据都要申请内存空间 |
插入每一个元素时间复杂度为常数 | 不支持随机访问 |
- 测试
int main(int argc, char *argv[])
{
list<int> lt;
clock_t timeStart = clock();
for(int i = 0 ; i < 10000000 ; ++ i) // 插入一千万条数据
lt.push_back(i);
cout << "insert milli seconds : " << clock() - timeStart << endl;
timeStart = clock();
int key = 23675;
::find(lt.begin(),lt.end(),key);
cout << "find milli seconds : " << clock() - timeStart << endl;
return 0;
}
- forward_list
单向链表
deque
deque<T> 双端队列 ,分段连续
- 数据结构
- 内存分配空间
deque是一种可变长的数组容器,原理就是他是由一段一段的连续内存空间串联起来的,每一段内存空间都是连续的,但是段与段之间的地址空间是不连续的。其中有一个中控器map管理器管理段地址。
- 优缺点
优点 | 缺点 |
支持头尾插入删除操作 | 随机访问能力不太好 |
- 测试
int main(int argc, char *argv[])
{
deque<int> dt;
clock_t timeStart = clock();
for(int i = 0 ; i < 10000000 ; ++ i) // 插入一千万条数据
dt.push_back(i);
cout << "insert milli seconds : " << clock() - timeStart << endl;
timeStart = clock();
int key = 23675;
::find(dt.begin(),dt.end(),key);
cout << "find milli seconds : " << clock() - timeStart << endl;
return 0;
}
- stack
先进后出栈,stack 默认情况就是用 deque 实现的。当然,也可以指定用 vector 或 list 实现。 - queue
先进先出队列,queue 可以用 list 和 deque 实现,默认情况下用 deque 实现。
关联式容器
map
map<T,T>
- 数据结构
map的数据结构是一种红黑树(RB-Tree) - 内存分配空间
map 是关联容器的一种,map 的每个元素都分为关键字和值两部分,容器中的元素是按关键字排序的,并且不允许有多个元素的关键字相同。 - 优缺点
优点 | 缺点 |
检索速度快,key自动排序 | 不能直接修改 map 容器中的关键字,插入速度会比较慢 |
Tips:不能直接修改 map 容器中的关键字。因为 map 中的元素是按照关键字排序的,当关键字被修改后,容器并不会自动重新调整顺序,于是容器的有序性就会被破坏,再在其上进行查找等操作就会得到错误的结果。
- 测试
int main(int argc, char *argv[])
{
map<int,int> mt;
clock_t timeStart = clock();
for(int i = 0 ; i < 10000000 ; ++ i) // 插入一千万条数据
mt[i] = i ;
cout << "insert milli seconds : " << clock() - timeStart << endl;
timeStart = clock();
int key = 23675;
mt.find(key);
cout << "find milli seconds : " << clock() - timeStart << endl;
return 0;
}
- multimap
允许key可以存在相同的值的一种map
set
- 数据结构
也是一种红黑树(RB-Tree),只是和map的结构不太一样,set的key和value都是同一个值
2. 内存分配空间
- 优缺点
优点 | 缺点 |
检索速度快,key自动排序 | 不能直接修改 map 容器中的关键字,插入速度会比较慢 |
- 测试
int main(int argc, char *argv[])
{
set<int> st;
clock_t timeStart = clock();
for(int i = 0 ; i < 10000000 ; ++ i) // 插入一千万条数据
st.insert(i);
cout << "insert milli seconds : " << clock() - timeStart << endl;
timeStart = clock();
int key = 23675;
st.find(key);
cout << "find milli seconds : " << clock() - timeStart << endl;
return 0;
}
5. multiset
可以插入相同的值
如有出现错误还望指正,多谢!!!