C++容器用法简介——list
翻译自cplusplus
一、简介
List是一种的顺序容器,它允许你在任何地方以常量的时间完成插入或者删除操作(因为链表在删除或增加的的时候只是简单的修改一下指针的指向,是在O(1)的时间内完成的),List的迭代器是双向的。
List容器在实现上采用双向链表,这种链表能存储一些位置不同且不相关的元素(这里原文没看懂随便翻译的啦~),元素通过自己的指针域来指向下一个结点,或者上一个结点,通过这种方式可以使链表保持一定的顺序。
List容器和forward_list(C++11以上版本新增的)很像,唯一的区别是forward_list是单向链表,所以forward_list的迭代器是单向的,不过forward_list拥有更小的存储空间和更高的效率(因此我们在使用链表的时候,如果明确知道这是一个单向的链表应该优先使用forward_list)
和其他的标准顺序容器(比如array(数组 C++11),vector(向量),deque(双端队列))相比,List获得迭代器的时候(实际上就是当指针指向这个地方的时候,如果迭代器没有到位,实际上这些操作时间复杂度也是O(n),因为你首先需要找到它们,原因在一下段中给出了),在任何位置的插入,移动,获取元素操作会更加的高效。所以在算法中经常出现,比如排序算法(不知道这里的algorithms,是指常规意义的算法还是说algorithm这个头文件)
但是,不管是List还是forward_list和其他顺序容器相比,它们失去了随机读取的特性,例如:为了访问列表里面的第6个元素,你不得不花费线性(时间复杂度O(n))的时间,使用迭代器从List的某个已知位置(头或者尾这种地方)一直循环到这里。另外由于需要存储下一个结点或者上一个结点的位置信息,需要单独使用一个或者多个指针,会消耗过多的内存空间。尤其是使用大的链表存储小容量的数据时。(比如在链表中每个结点放置一个char,指针所占的空间已经超过数据了,指针的大小一般是4个字节,64位的电脑的话应该是8个字节,但是一个char才1个字节)
二、容器特点
1.序列
每个List里面都是严格的线性顺序,每个元素可以通过它们的位置来访问。
2.双向链表
每个元素都存储了关于下一个和上一个元素位置的信息,允许在常数时间内对特定的元素完成插入和删除操作(甚至是整个链表),但是不能随机访问(比如数组a[1])
3.动态内存分配
List通过动态内存分配需要的存储空间(C++的new,或者是C语言的malloc)
三、函数用法示例
1、构造与析构(C++11版本)
const allocator_type& alloc = allocator_type()这句话是STL里面为了合理分配空间定义的类,作为默认参数,如果需要使用自己的分配方式请参阅std::allocator
//default (1)
//构造一个空的链表。
explicit list (const allocator_type& alloc = allocator_type());
list<int>list1;
//fill (2)
//构造一个拥有n个元素的链表,每个元素的值为val(如果提供的话,没有一般就是按照全局变量的方式初始化)
explicit list (size_type n);
list (size_type n, const value_type& val,
const allocator_type& alloc = allocator_type());
list<int>list2(10); //0->0->0->......->0
list<int>list2(10, 1); //1->1->1->.......->1
//range (3)
//从区域<tt>[first,last)</tt>中构造一个链表,顺序保持不变
template <class InputIterator>
list (InputIterator first, InputIterator last,
const allocator_type& alloc = allocator_type());
int num[] = {1, 2, 3, 4};
list<int>list3(num, num + 2); //1->2
//copy (4)
//从另一个链表中复制相同的元素,且顺序不变,拷贝构造函数
list (const list& x);
list (const list& x, const allocator_type& alloc);
list<int>list4_1(10, 1);
list<int>list4_2(list4_1);
//move (5)
//C++11提供的move语义,可以是效率更高,避免复制
list (list&& x);
list (list&& x, const allocator_type& alloc);
//initializer list (6)
//C++11允许构造函数和其他函数把初始化列表当做参数。
list (initializer_list<value_type> il,
const allocator_type& alloc = allocator_type());
//list<int>list6 = {1, 2, 3, 4, 5, 6};
2.迭代器函数
2.1 begin()
返回容器中第一个元素的迭代器,和front()不同,begin()返回的只是一个指向第一个元素的双向迭代器而已,如果容器为空返回的迭代器是无效的。
例如:
list<int>l1 = {1, 2, 3, 4, 5};
cout << *l1.begin() << endl; //输出1
2.2 cbegin()
返回一个const_iterator,与前者的区别是const_iterator只能用于指向容器中的元素但是不能修改它,实际上begin是一个重载函数,当容器中数据是const时,自动返回const_iterator,但是cbegin()只能返回const_iterator。
list<int>l1 = {1, 2, 3, 4, 5};
cout << *l1.cbegin() << endl; //输出1
*l1.begin() = 2;
cout << *l1.begin() << endl; //输出2
*l1.cbegin() = 1; //error error: assignment of read-only location
2.3 end()与cend()
返回一个超过容器中最后一个元素的位置迭代器,所以事实上不指向任何元素,返回的引用不可用,( 之所以返回一个指向最后一个元素后一个的迭代器,事实上可以简化计算,理由参考链接,其实可以发现在很多编程语言里面都是使用前闭后开区间), end()一般和 begin()