list,vector,deque有什么区别
vector
表示一段连续的内存区域每个元素被顺序存储在这段内存中对vector的随机访问比如先访问元素5 然后访问15然后再访问7等等效率很高,因为每次访问离vector起始处的位移都是固定的。
但是在任意位置而不是在vector末尾插人元素则效率很低 ,因为它需要把待插入元素右边的每个元素都拷贝一遍。
类似地删除任意一个而不是vector 的最后一个元素效率同样很低。
因为待删除元素右边的每个元素都必须被复制一遍这种代价对于大型的复杂的类对象来说尤其大。
deque
一个deque 也表示一段连续的内存区域但是与vector不同的是它支持高效地在其首部插入和删除元素它通过两级数组结构来实现一级表示实际的容器第二级指向容器的首和尾
list
List表示非连续的内存区域并通过一对指向首尾元素的指针双向链接起来从而允许向前和向后两个方向进行遍历在list的任意位置插入和删除元素的效率都很高
指针必须被重新赋值但是不需要用拷贝元素来实现移动
另一方面它对随机访问的支持并不好,访问一个元素需要遍历中间的元素另外每个元素还有两个指针的额外空间开销
下面是选择顺序容器类型的一些准则
如果我们需要随机访问一个容器则vector要比list好得多 。
如果我们已知要存储元素的个数则vector 又是一个比list好的选择。
如果我们需要的不只是在容器两端插入和删除元素则list显然要比vector好
除非我们需要在容器首部插入和删除元素否则vector要比deque好
//
1 vector向量 相当于一个数组
2 list双向链表
3 deque双端队列 double-end queue
使用区别:
//
vector和built-in数组类似,它拥有一段连续的内存空间,并且起始地址不变,因此
它能非常好的支持随即存取,即[]操作符,但由于它的内存空间是连续的,所以在中间
进行插入和删除会造成内存块的拷贝,另外,当该数组后的内存空间不够时,需要重新
申请一块足够大的内存并进行内存的拷贝。这些都大大影响了vector的效率。
list就是数据结构中的双向链表(根据sgi stl源代码),因此它的内存空间可以是不连续
的,通过指针来进行数据的访问,这个特点使得它的随即存取变的非常没有效率,因此它
没有提供[]操作符的重载。但由于链表的特点,它可以以很好的效率支持任意地方的删除
和插入。
deque是一个double-ended queue,它的具体实现不太清楚,但知道它具有以下两个特点:
它支持[]操作符,也就是支持随即存取,并且和vector的效率相差无几,它支持在两端的
操作:push_back,push_front,pop_back,pop_front等,并且在两端操作上与list的效率
也差不多。
因此在实际使用时,如何选择这三个容器中哪一个,应根据你的需要而定,一般应遵循下面
的原则:
1、如果你需要高效的随即存取,而不在乎插入和删除的效率,使用vector
2、如果你需要大量的插入和删除,而不关心随即存取,则应使用list
3、如果你需要随即存取,而且关心两端数据的插入和删除,则应使用deque。
Vector:C++容器模板中的大哥大,就像是一个加强版的队列,之所以这样说,是因为它不但有队列形式的索引,还能动态的添加扩充。
特点:把被包含的对象以数组的形式存储,支持索引形式的访问(这种访问速度奇快无比)。但由此也产生了一个问题,由于数据存储形式的固定化,你如果想在他中间部位insert对象的话,搞不好会让你吃尽头。因为他在分配空间的时候,可是成块分配的连续空间。
Deque:英文“double-ended-queue”。名如其人,这是C++有序容器中闻名遐迩的双向队列。他在设计之初,就为从两端添加和删除元素做了特殊的优化。同样也支持随即访问,也有类似vector的[ ]操作符,但不要因此就把他和vector混为一潭。
特点:从本质上讲,他在分配内存的时候,使用了MAP的结构和方法。化整为零,分配了许多小的连续空间,因此,从deque两端添加、删除元素是十分方便的。最重要的一点:如果在不知道内存具体需求的时候,使用deque绝对是比vector好的。
List:模板中的双向链表。设计他的目的可能就是为了在容器中间插入、删除吧,所以有得比有失,他的随机访问速度可不敢恭维。而且没有[ ]操作。
vector和deque的区别主要在于他们底层的实现不同,特别是在插入和删除操作的实现机制不同。
对于vector来说,不管其大小是多少,在头部插入的效率总是比在尾部插入的效率低。在尾部插入将耗费固定的时间。在头部进行插入时,耗费的时间与vector的大小成正比,vector越大,耗费的时间越多。例如,在一个大小为1000的vector头部插入一个元素,与在一个大小为10的vector头部插入一个元素相比,将耗费100倍的时间。删除操作的情形也与插入类似。因此,vector适合于插入和删除操作都在尾部进行的情况。
deque和vector不同,不管进行的插入还是删除操作,也不管这些操作时在头部还是尾部进行,算法的效率是固定的。例如:不管deque的大小是10,100,还是1000.deque在头部和尾部插入删除的时间是一样的。因此要在对于两端进行插入或者删除操作时。deque要优于vector。
STL中: string、vector、list、deque、set、map 的区别
set 和map都是无序的保存元素,只能通过它提供的接口对里面的元素进行访问
set:集合, 用来判断某一个元素是不是在一个组里面,使用的比较少
map:映射,相当于字典,把一个值映射成另一个值,如果想创建字典的话使用它好了
string、vector、list、deque、set 是有序容器
1.string
string 是basic_string<char> 的实现,在内存中是连续存放的.为了提高效率,都会有保留内存,如string s= "abcd",这时s使用的空间可能就是255, 当string再次往s里面添加内容时不会再次分配内存.直到内容>255时才会再次申请内存,因此提高了它的性能.
当内容>255时,string会先分配一个新内存,然后再把内容复制过去,再复制先前的内容.
对string的操作,如果是添加到最后时,一般不需要分配内存,所以性能最快;
如果是对中间或是开始部分操作,如往那里添加元素或是删除元素,或是代替元素,这时需要进行内存复制,性能会降低.
如果删除元素,string一般不会释放它已经分配的内存,为了是下次使用时可以更高效.
由于string会有预保留内存,所以如果大量使用的话,会有内存浪费,这点需要考虑.还有就是删除元素时不释放过多的内存,这也要考虑.
string中内存是在堆中分配的,所以串的长度可以很大,而char[]是在栈中分配的,长度受到可使用的最大栈长度限制.
如果对知道要使用的字符串的最大长度,那么可以使用普通的char[],实现而不必使用string.
string用在串长度不可知的情况或是变化很大的情况.
如果string已经经历了多次添加删除,现在的尺寸比最大的尺寸要小很多,想减少string使用的大小,可以使用:
string s = "abcdefg";
string y(s); // 因为再次分配内存时,y只会分配与s中内容大一点的内存,所以浪费不会很大
s.swap(y); // 减少s使用的内存
如果内存够多的话就不用考虑这个了
capacity是查看现在使用内存的函数
大家可以试试看string分配一个一串后的capacity返回值,还有其它操作后的返回值
2.vector
vector就是动态数组.它也是在堆中分配内存,元素连续存放,有保留内存,如果减少大小后内存也不会释放.如果新值>当前大小时才会再分配内存
对最后元素操作最快(在后面添加删除最快 ), 此时一般不需要移动内存,只有保留内存不够时才需要
对中间和开始处进行添加删除元素操作需要移动内存,如果你的元素是结构或是类,那么移动的同时还会进行构造和析构操作,所以性能不高 (最好将结构或类的指针放入vector中,而不是结构或类本身,这样可以避免移动时的构造与析构)。
访问方面,对任何元素的访问都是O(1),也就是是常数的,所以vector常用来保存需要经常进行随机访问的内容,并且不需要经常对中间元素进行添加删除操作.
相比较可以看到vector的属性与string差不多,同样可以使用capacity看当前保留的内存,使用swap来减少它使用的内存.
总结
需要经常随机访问请用vector
3.list
list就是链表,元素也是在堆中存放,每个元素都是放在一块内存中
list没有空间预留习惯,所以每分配一个元素都会从内存中分配,每删除一个元素都会释放它占用的内存,这与上面不同,可要看好了
list在哪里添加删除元素性能都很高,不需要移动内存,当然也不需要对每个元素都进行构造与析构了,所以常用来做随机操作容器.
但是访问list里面的元素时就开始和最后访问最快
访问其它元素都是O(n) ,所以如果需要经常随机访问的话,还是使用其它的好
总结
如果你喜欢经常添加删除大对象的话,那么请使用list
要保存的对象不大,构造与析构操作不复杂,那么可以使用vector代替
list<指针>完全是性能最低的做法,这种情况下还是使用vector<指针>好,因为指针没有构造与析构,也不占用很大内存
4.deque
双端队列,也是在堆中保存内容的.它的保存形式如下:
[堆1]
...
[堆2]
...
[堆3]
每个堆保存好几个元素,然后堆和堆之间有指针指向,看起来像是list和vector的结合品,不过确实也是如此
deque可以让你在前面快速地添加删除元素,或是在后面快速地添加删除元素,然后还可以有比较高的随机访问速度
vector是可以快速地在最后添加删除元素,并可以快速地访问任意元素
list是可以快速地在所有地方添加删除元素,但是只能快速地访问最开始与最后的元素
deque在开始和最后添加元素都一样快,并提供了随机访问方法,像vector一样使用[]访问任意元素,但是随机访问速度比不上vector快,因为它要内部处理堆跳转
deque也有保留空间.另外,由于deque不要求连续空间,所以可以保存的元素比vector更大,这点也要注意一下.还有就是在前面和后面添加元素时都不需要移动其它块的元素,所以性能也很高
STL 入门--vector list deque 区别
stl的内容很多,本文就实际中比较常用的和初学时应该注意的方面进行介绍
任何一门高级计算机语言,都需要有一定的类库或 者函数库的支持。C++的标准库(c++ standard library),在1998年成为国际标准。在这个标准库中,包含了一个很重要的组成部分,也就是这篇文章的主题所在---标准模板库 STL(standard template library)。为了在标准库中加入这个部分,c++的标准甚至推迟了出炉的日期,可见它在整个标准库中的重要地位。
在标准中只是规定了stl库的实现规范,任何人都可以根据这个规范实现出stl库,所以现在的stl库有很多版本,他们的实现方法也不完全相同。
一、 工作环境
由 于STL是98年标准的产物,所以早先的C++编译器(比如tc3.0)是无法编译运行使用了STL库的程序的。当然,现在流行的C++编译器或开发环境 都提供了对它的支持,比如visual c++和c++ builder, g++等等。所以,stl自然的可以应用到所有的用标准c++开发的程序中去。
二、 第一个使用了STL程序
//一个对数组进行快速排序的程序
#include<iostream>
#include<algorithm>
using namespace std;
int main()
{
int a[10]= {3, 4, 7, 10, 3, 2, 1, 5, 6, 7};
int i = 0;
sort(a, a+10);
for(; i < 10; i++)
{
cout << a[i] <<” “<<endl;
}
}
这么短的快速排序程序可能以前没有见过吧:),既没有比较大小,也没有元素交换,就完成了工作。
现在提出另一个问题,要对事先不知道个数的元素进行排序,看看使用STL的解决方案。
#include<vector>
#include<algorithm>
#include<iostream>
using namespace std;
int main()
{
vector<int> vec;
int n;
cin >> n;
while(n--)
{
int x;
cin >> x;
vec.push_back(x);
}
sort(vec.begin(), vec.end());
}
vector,暂且把他看成一个类似数组的东西,过会我们会详细地讲解他的用法。
从上面两个例子, 我们可以看到stl最常用的两大类组件:容器和算法。容器,顾名思义是用来存放数据的,就如同第二个例子里的vector。算法,当然是用来对数据进行操作的啦,第一个例子里的sort()就是stl里很多算法的一个。
三、 容器类模板介绍
在stl中,使用频率最高的容器类模板有三个vector, list, deque
1.vector模板的常用使用方法
vector容器的性质类似于数组,他的元素排列方式和c++的内建数组一样,在内存中顺序排列。
声明: vector<string> vec; // 一个空的vector容器
vector<int> vec(10) //一个有10个int元素的容器
vector<float> vec(10, 0.5)//一个有10个float元素的容器,并且他们得值都是0.5
使 用:(1) push_back()、pop_back()和size()方法,这是对vector操作的两个常用方法。Push_back()用于将一个元素插入 vector的尾部,而pop_back()则将最后一个元素删除。就要用到size()返回整型值,表示vector里有多少元素。例如:
vector<int> vec;
vec.push_back(4);
vec.push_back(5);
vec.pop_back(); //此时vec中有一个元素4
int s = vec.size(); // s的值是1
对于事先不知道大小的vector, 我们通常都用push_back(),将元素送入容器中,比如开头第二个例子。
(2)[]运算符,前面说了,vector的性质类似于数组,所以他支持[]运算符,使得对数组的运算方法完全适用于vector。也就是说,vector有和数组一样的“下标”概念。
vector<int> vec(100, 5); //有100个元素且元素值都为5的vector
for(int j = 0; j < 100; j++)
vec[j] = vec[j]+1; //通过下标遍历这个vector
(3)指向vector内元素的指针vector<T1>::iterator
这种指针实际上是一种智能指针,它实际上也是一个模板,但是重载了指针的常用运算符,比如++,--。它定义在vector头文件中。与vector<T1>::iterator相关的方法有:
begin():用来获得一个指向vector第一个元素的指针
end():用来获得一个指向vector最后一个元素之后的那个位置的指针,注意不是指向最后一个元素。
erase(vector<T1>::iterator):用来删除作为参数所传入的那个iterator所指向的那个元素。
举一个使用iterator来遍历vector的例子
vector<int> vec(100, -1);
vector<int>::iterator iter;
for(iter = vec.begin(); iter != vec.end(); iter++)
{
//. . . . . . .
}
for(iter = vec.end()-1;iter != vec.begin()-1; iter--)
{
//. . . . . . .
}
2.list模板简介
list,是一个链表类的容器,它的元素在内存中不是顺序排列的,而是通过指针链接的,这一点使它和vector有了本质的区别。
常用方法:
(1)push_back(), pop_back(), push_front(), pop_front(),前两个方法和vector中的两个同名方法作用相同,而push_front()用来从list的前端插入一个元 素,pop_front()用来删除list的第一个元素。
(2)list不支持[]――下标运算符,所以对其中的元素进行存取遍历只能通过指针了。指向list所存元素的指针是list<T1>::iterator,和vector<T1>::iterator一样,它也支持++,--运算符。
begin():用来获得一个指向list第一个元素的指针
end():用来获得一个指向list最后一个元素之后的那个位置的指针,注意不是指向最后一个元素。
erase(list<T1>::iterator):用来删除作为参数所传入的那个iterator所指向的那个元素。
举一个使用iterator来遍历list的例子
list<string> stringList;
list<string>::iterator iter;
for(iter = stringList.begin(); iter != stringList.end(); iter++)
{
//. . . . . . .
}
for(iter = stringList.end()-1;iter != stringList.begin()-1; iter--)
{
//. . . . . . .
}
一个使用iterator来删除元素的例子
list<string> stringList;
list<string>::iterator iter;
advance(iter, 5); //将iterator指向stringList的第五个元素
stringList.erase(iterator);//删除
那么删除操作进行以后,传入erase()方法的iterator指向哪里了呢?它指向了删除操作前所指向的那个元素的后一个元素。
3.deque模板
deque模板是一个队列类的模板,它的常用方法如下:
定义: deque<double> deq;
deque<double> deq(5);//deque是可以定义初始大小的
(1)[]下标运算符,使用方法和vector是完全一样的。
(2)push_back(), pop_back(), push_front(), pop_front()这四个方法的用法和list模板中的是完全一样的。
(3)指向deque类容器所存元素的指针deque<T1>::iterator
4.这三个模板的总结 比较和一般使用准则
这三个模板都属于序列类模板,可以看到他们有一些通用方法
size():得到容器大小
begin():得到指向容器内第一个元素的指针(这个指针的类型依容器的不同而不同)
end():得到指向容器内最后一个元素之后一个位置的指针
erase():删除传入指针指向的那个元素
clear():清除所有的元素
==运算符:判断两个容器是否相等
=运算符:用来给容器赋值
上面的三个模板有各自的特点
vector模板的数据在内存中连续的排列,所以随机存取元素(即通过[]运算符存取)的速度最快,这一点和数组是一致的。同样由于它的连续排列,所以它在除尾部以外的位置删除或添加元素的速度很慢,在使用vector时,要避免这种操作。
list模板的数据是链式存储,所以不能随机存取元素。它的优势在于任意位置添加 删除元素的速度。
deque模板是通过链接若干片连续的数据实现的,所以均衡了以上两个容器的特点