1.关于list容器
list是一种序列式容器。
list容器完成的功能实际上和数据结构中的双向链表是极其相似的,list中的数据元素是通过链表指针串连成逻辑意义上的线性表,也就是list也具有链表的主要优点,即:在链表的任一位置进行元素的插入、删除操作都是快速的。
list的实现大概是这样的:list的每个节点有三个域:前驱元素指针域、数据域和后继元素指针域。
前驱元素指针域保存了前驱元素的首地址;数据域则是本节点的数据;后继元素指针域则保存了后继元素的首地址。
其实,list和循环链表也有相似的地方,即:头节点的前驱元素指针域保存的是链表中尾元素的首地址,list的尾节点的后继元素指针域则保存了头节点的首地址,这样,list实际上就构成了一个双向循环链。
由于list元素节点并不要求在一段连续的内存中,显然在list中是不支持快速随机存取的,因此对于迭代器,只能通过“++”或“--”操作将迭代器移动到后继/前驱节点元素处。而不能对迭代器进行+n或-n的操作,这点,是与vector等不同的地方。
一起对比一下三个常用的序列式:
vector
vector和built-in数组类似,拥有一段连续的内存空间,能非常好的支持随机存取,即[]操作符,但由于它的内存空间是连续的,所以在中间进行插入和删除会造成内存块的拷贝;
另外,当插入较多的元素后,预留内存空间可能不够,需要重新申请一块足够大的内存并把原来的数据拷贝到新的内存空间。这些影响了vector的效率;
但是实际上用的最多的还是vector容器,建议大多数时候使用vector效率一般是不错的。
list
list就是数据结构中的双向链表(根据sgi stl源代码),因此它的内存空间是不连续的,通过指针来进行数据的访问,这个特点使得它的随机存取变的非常没有效率,因此它没有提供[]操作符的重载。
但由于链表的特点,它可以以很好的效率支持任意地方的删除和插入。
deque
deque是一个double-ended queue,它的具体实现不太清楚,但知道它具有以下两个特点:它支持[]操作符,也就是支持随机存取,并且和vector的效率相差无几;
它支持在两端的操作:push_back,push_front,pop_back,pop_front等,并且在两端操作上与list的效率也差不多。
因此在实际使用时,如何选择这三个容器中哪一个,应根据需要而定,具体可以遵循下面的原则:
1. 如果需要高效的随机存取,而不在乎插入和删除的效率,使用vector
2. 如果需要大量的插入和删除,而不关心随机存取,则应使用list
3. 如果需要随机存取,而且关心两端数据的插入和删除,则应使用deque。
2.list中常用的函数
2.1list中的构造函数:
list() 声明一个空列表;
list(n) 声明一个有n个元素的列表,每个元素都是由其默认构造函数T()构造出来的
list(n,val) 声明一个由n个元素的列表,每个元素都是由其复制构造函数T(val)得来的
list(n,val) 声明一个和上面一样的列表
list(first,last) 声明一个列表,其元素的初始值来源于由区间所指定的序列中的元素
2.2 begin()和end():通过调用list容器的成员函数begin()得到一个指向容器起始位置的iterator,可以调用list容器的 end() 函数来得到list末端下一位置,相当于:int a[n]中的第n+1个位置a[n],实际上是不存在的,不能访问,经常作为循环结束判断结束条件使用。
2.3 push_back() 和push_front():使用list的成员函数push_back和push_front插入一个元素到list中。其中push_back()从list的末端插入,而 push_front()实现的从list的头部插入。
2.4 empty():利用empty() 判断list是否为空。
2.5 resize(): 如果调用resize(n)将list的长度改为只容纳n个元素,超出的元素将被删除,如果需要扩展那么调用默认构造函数T()将元素加到list末端。如果调用resize(n,val),则扩展元素要调用构造函数T(val)函数进行元素构造,其余部分相同。
2.6 clear(): 清空list中的所有元素。
2.7 front()和back(): 通过front()可以获得list容器中的头部元素,通过back()可以获得list容器的最后一个元素。但是有一点要注意,就是list中元素是空的时候,这时候调用front()和back()会发生什么呢?实际上会发生不能正常读取数据的情况,但是这并不报错,那我们编程序时就要注意了,个人觉得在使用之前最好先调用empty()函数判断list是否为空。
2.8 pop_back和pop_front():通过删除最后一个元素,通过pop_front()删除第一个元素;序列必须不为空,如果当list为空的时候调用pop_back()和pop_front()会使程序崩掉。
2.9 assign():具体和vector中的操作类似,也是有两种情况,第一种是:l1.assign(n,val)将 l1中元素变为n个T(val)。第二种情况是:l1.assign(l2.begin(),l2.end())将l2中的从l2.begin()到l2.end()之间的数值赋值给l1。
2.10 swap():交换两个链表(两个重载),一个是l1.swap(l2); 另外一个是swap(l1,l2),都可能完成连个链表的交换。
2.11 reverse():通过reverse()完成list的逆置。
2.12 merge():合并两个链表并使之默认升序(也可改),l1.merge(l2,greater<int>()); 调用结束后l2变为空,l1中元素包含原来l1 和 l2中的元素,并且排好序,升序。其实默认是升序,greater<int>()可以省略,另外greater<int>()是可以变的,也可以不按升序排列。
看一下下面的程序:
#include <iostream>
#include <list>
using namespace std;
int main()
{
list<int> l1;
list<int> l2(2,0);
list<int>::iterator iter;
l1.push_back(1);
l1.push_back(2);
l2.push_back(3);
l1.merge(l2,greater<int>());//合并后升序排列,实际上默认就是升序
for(iter = l1.begin() ; iter != l1.end() ; iter++)
{
cout<<*iter<<" ";
}
cout<<endl<<endl;
if(l2.empty())
{
cout<<"l2 变为空 !!";
}
cout<<endl<<endl;
return 0;
}
2.13 insert():在指定位置插入一个或多个元素(三个重载):
l1.insert(l1.begin(),100); 在l1的开始位置插入100。
l1.insert(l1.begin(),2,200); 在l1的开始位置插入2个100。
l1.insert(l1.begin(),l2.begin(),l2.end());在l1的开始位置插入l2的从开始到结束的所有位置的元素。
2.14 erase():删除一个元素或一个区域的元素(两个重载)
l1.erase(l1.begin()); 将l1的第一个元素删除。
l1.erase(l1.begin(),l1.end()); 将l1的从begin()到end()之间的元素删除。
list增删改查接口封装示例
#include <iostream>
#include <string>
#include <list>
using namespace std;
struct Student
{
int id;
string name;
int score;
};
#define func_log(s, s2) do {cout << "[" << s << "] " << s2 << endl;} while (0);
list<Student> Students;
void add(Student &obj)
{
func_log(__func__, "");
Students.push_back(obj);
}
void remove(int id)
{
func_log(__func__, "");
list<Student>::iterator it = Students.begin();
for (it; it != Students.end(); it++)
{
if (id == it->id)
{
cout << "remove: " << id << endl;
Students.erase(it);
break;
}
}
func_log(__func__, "end");
}
void modify(Student &obj)
{
func_log(__func__, "");
list<Student>::iterator it = Students.begin();
for (it; it != Students.end(); it++)
{
if (obj.id == it->id)
{
cout << "modify: [id]: " << obj.id << endl;
cout << "modify: [name]: " << obj.name << endl;
cout << "modify: [score]: " << obj.score << endl;
*it = obj;
break;
}
}
func_log(__func__, "end");
}
int search(int id, Student &obj)
{
func_log(__func__, "begin");
int result = false;
list<Student>::iterator it = Students.begin();
for (it; it != Students.end(); it++)
{
if (id == it->id)
{
obj = *it;
cout << "Found: [id]: " << obj.id << endl;
cout << "Found: [name]: " << obj.name << endl;
cout << "Found: [score]: " << obj.score << endl;
result = true;
break;
}
}
func_log(__func__, "end");
return result;
}
void show()
{
cout << "#####################" << endl;
list<Student>::iterator it = Students.begin();
for (it; it != Students.end(); it++)
{
cout << it->id << " ";
cout << it->name << " " ;
cout << it->score << endl;
}
cout << "#####################" << endl;
}
int main() {
Student st1 = {1, "laoyang", 99};
Student st2 = {2, "laoyanj", 98};
Student st3 = {3, "laoyanh", 97};
Student st4 = {4, "laoyani", 96};
add(st1);
add(st2);
add(st3);
add(st4);
show();
Student stu;
if (search(3, stu))
{
cout << "search OK" << endl;
cout << "\tid: " << stu.id << endl;
cout << "\tname: " << stu.name << endl;
cout << "\tscore: " << stu.score << endl;
}
else
{
cout << "search Failed." << endl;
}
stu.id = 1;
modify(stu);
show();
remove(2);
show();
return 0;
}
list方法使用总结示例
/*****************************************
* Copyright (C) 2018 * Ltd. All rights reserved.
*
* File name : list.cpp
* Author : longbin
* Created date: 2018-08-02 19:18:13
* Description :
*
*******************************************/
#include <iostream>
#include <string>
#include <list>
using namespace std;
#define print(s) do {cout << "[" << __func__ << "] " << #s << ": " << s << endl;} while (0);
template <class T>
void show(T& val)
{
typename T::iterator it;
cout << "[" << __func__ << "]: ";
for (it = val.begin(); it != val.end();)
{
cout << *it;
it++;
if (it != val.end()) {
cout << ", ";
}
}
cout << endl;
}
void lst_construct()
{
// default (1) explicit list (const allocator_type& alloc = allocator_type());
// fill (2) explicit list (size_type n, const value_type& val = value_type(),
// const allocator_type& alloc = allocator_type());
// range (3) template <class InputIterator>
// list (InputIterator first, InputIterator last, const allocator_type& alloc = allocator_type());
// copy (4) list (const list& x);
// (1) empty container constructor (default constructor)
// (2) fill constructor Constructs a container with n elements. Each element is a copy of val.
// (3) range constructor Constructs a container with as many elements as the range [first,last)
// (4) copy constructor Constructs a container with a copy of each of the elements in x, in the same order.
list<int> li; // (1) empty list of ints;
list<string> ls; // (1) empty list of strings;
list<int> li2(6, 258); // (2) 6 ints with value 258;
list<int> li20(6); // (2) 6 ints with value 0;
list<string> ls2(3, "c++"); // (2) 3 strings with value of "c++";
list<int> li3(li2.begin(), li2.end()); // (3) iteratoring from li2;
list<string> ls3(ls2.begin(), ls2.end()); // (3) iteratoring from li2;
list<int> li4(li2); // (4) a copy of li2;
list<string> ls4(ls2); // (4) a copy of ls2;
int arr[] = {1,2,3,4,5,6};
list<int> li5 (arr, arr+sizeof(arr)/sizeof(int)); // initializer list (6) //c++11
show(li); // show:
show(ls); // show:
show(li2); // show: 258, 258, 258, 258, 258, 258
show(li20); // show: 0, 0, 0, 0, 0, 0
show(ls2); // show: c++, c++, c++
show(li3); // show: 258, 258, 258, 258, 258, 258
show(ls3); // show: c++, c++, c++
show(li4); // show: 258, 258, 258, 258, 258, 258
show(ls4); // show: c++, c++, c++
show(li5); // show: 1, 2, 3, 4, 5, 6
}
void lst_OperatorEQ()
{
// Assign content
// Assigns new contents to the container, replacing its current contents, and modifying its size accordingly.
// copy (1) list& operator= (const list& x);
// move (2) list& operator= (list&& x); //c++11
// initializer list (3) list& operator= (initializer_list<value_type> il); //c++11
list<int> li1(5);
list<int> li2(6, 1);
show(li1); //0, 0, 0, 0, 0
show(li2); //1, 1, 1, 1, 1, 1
li2 = li1;
li1 = list<int>();
print(li1.size()); //0
print(li2.size()); //5
list<int> li3 = {1,2,3,4,5,6}; // c++11 要求类型相容
list<int> li31 {1,2,3,4,5,6}; // c++11 =可省略
show(li3); // show: 1, 2, 3, 4, 5, 6
show(li31); // show: 1, 2, 3, 4, 5, 6
}
void lst_begin_end()
{
// iterator begin();
// const_iterator begin() const;
// Return iterator to beginning // Returns an iterator pointing to the first element in the list.
// iterator end();
// const_iterator end() const;
// Return iterator to end // Returns an iterator referring to the past-the-end element in the list container.
// vector,string,deque的迭代器有加减法;但list,map,set,multimap,multiset的迭代器是没有加减法的(+n/-n),只有++和--;
list<int> li;
list<int>::iterator it;
int i = 0;
for (i = 1; i <= 9; i++)
{
li.push_back(i);
}
cout << "[" << __func__ << "]: ";
for (it = li.begin(); it != li.end();)
{
cout << *it;
it++;
if (it != li.end())
{
cout << ", ";
}
}
cout << endl;
}
void lst_size()
{
// Return size // Returns the number of elements in the list.
// size_type size() const;
list<int> li;
print(li.size()); //0
int i = 0;
for (i = 1; i <= 9; i++)
{
li.push_back(i);
}
print(li.size()); //9
li.insert(li.end(), 10, 100);
print(li.size()); //19
li.pop_back();
print(li.size()); //18
}
void lst_capacity()
{
cout << "capacity no such function for list" << endl;
}
void lst_empty()
{
// bool empty() const;
// Test whether list is empty
// Returns whether the list is empty (i.e. whether its size is 0).
// This function does not modify the container in any way. To clear the content of a list, see list::clear.
list<int> li;
int i = 0;
int sum = 0;
print(li.empty()); //1
for (i = 1; i <= 9; i++)
{
li.push_back(i);
}
print(li.empty()); //0
while (!li.empty())
{
sum += li.back();
li.pop_back();
}
print(li.empty()); //1
print(sum); //45
}
void lst_OperatorAT()
{
cout << "[] was not supported for list" << endl;
}
void lst_at()
{
cout << "at() was not supported for list" << endl;
}
void lst_front()
{
// reference front();
// const_reference front() const;
// Access first element // Returns a reference to the first element in the list.
list<int> li;
li.push_back(10);
li.push_back(20);
// now front equals 10, and back 20
li.front() += li.back();
print(li.front()); //30
}
void lst_back()
{
// reference back();
// const_reference back() const;
// Access last element // Returns a reference to the last element in the list.
list<int> li;
li.push_back(10);
print(li.back());
while (0 != li.back())
{
li.push_back(li.back() - 1);
}
show(li); //10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0
}
void lst_assign()
{
// range (1) template <class InputIterator>
// void assign (InputIterator first, InputIterator last);
// fill (2) void assign (size_type n, const value_type& val);
// initializer list (3) void assign (initializer_list<value_type> il); //c++11
// Assign list content
// Assigns new contents to the list, replacing its current contents, and modifying its size accordingly.
list<int> li1;
list<int> li2;
list<int> li3;
li1.assign(6, 8); // (2)
show(li1); //8, 8, 8, 8, 8, 8
li2.assign(li1.begin(), li1.end()); // (1)
// li2.assign(li1.begin()+1, li1.begin()+3); // error, not support it+n/-n;
show(li2); //8, 8, 8, 8, 8, 8
int arr[] = {3, 6, 9, 2, 5, 8};
li3.assign(arr, arr+3); // (3) assign from array
show(li3); //3, 6, 9
}
void lst_push_back()
{
// void push_back (const value_type& val);
// Add element at the end
// Adds a new element at the end of the list, after its current last element.
// The content of val is copied (or moved) to the new element.
list<int> li;
unsigned int i = 0;
for (i = 0; i <= 9; i++)
{
cout << "[" << __func__ << "]: push_back " << i << endl;
li.push_back(i);
}
show(li); //0, 1, 2, 3, 4, 5, 6, 7, 8, 9
}
void lst_pop_back()
{
// void pop_back();
// Delete last element
// Removes the last element in the list, effectively reducing the container size by one.
list<int> li;
unsigned int i = 0;
for (i = 0; i <= 9; i++)
{
li.push_back(i);
}
show(li);
while (!li.empty())
{
cout << "[" << __func__ << "]: pop_back " << li.back() << endl;
li.pop_back();
}
}
void lst_insert()
{
// single element (1) iterator insert (iterator position, const value_type& val);
// fill (2) void insert (iterator position, size_type n, const value_type& val);
// range (3) template <class InputIterator>
// void insert (iterator position, InputIterator first, InputIterator last);
// move (4) iterator insert (const_iterator position, value_type&& val); //c++11
// initializer list (5) iterator insert (const_iterator position, initializer_list<value_type> il); //c++11
// Insert elements
// The list is extended by inserting new elements before the element at the specified position,
// effectively increasing the container size by the number of elements inserted.
list<int> li(3, 9);
list<int>::iterator it;
show(li); //9, 9, 9
it = li.begin();
++it;
// it = it + 1; //no match for ‘operator+’ in ‘it + 1’ in list;
it = li.insert(it, 1); // (1)
show(li); //9, 1, 9, 9
li.insert(it, 2, 6); // (2)
show(li); //9, 6, 6, 1, 9, 9
// "it" maybe valid, get a new one
it = li.begin();
it++;
// If a reallocation happens, all iterators, pointers and references related to the container are invalidated.
// Otherwise, only those pointing to position and beyond are invalidated, with all iterators, pointers and
// references to elements before position guaranteed to keep referring to the same elements they were referring
// to before the call.
list<int> li2(2, 7);
it++;
it++;
li.insert(it, li2.begin(), li2.end()); // (3)
show(li); //9, 6, 6, 7, 7, 1, 9, 9
int arr[] = {0, 1, 2, 3};
li.insert(li.begin(), arr, arr+3); // (5)
show(li); //0, 1, 2, 9, 6, 6, 7, 7, 1, 9, 9
}
void lst_erase()
{
// iterator erase (iterator position);
// iterator erase (iterator first, iterator last);
// Erase elements
// Removes from the list either a single element (position) or a range of elements ([first,last)).
list<int> li;
list<int>::iterator it1, it2;
unsigned int i = 0;
for (i = 1; i <= 9; i++)
{
li.push_back(i * 10);
}
show(li); //10, 20, 30, 40, 50, 60, 70, 80, 90
// 10 20 30 40 50 60 70 80 90
it1 = it2 = li.begin(); // ^^
advance(it2, 6); // ^ ^
++it1; // ^ ^
it1 = li.erase(it1); // (1) // 10 30 40 50 60 70 80 90
// ^ ^
it2 = li.erase(it2); // (1) // 10 30 40 50 60 80 90
// ^ ^
++it1; // ^ ^
--it2; // ^ ^
li.erase(it1, it2); // (2) // 10 30 60 80 90
// ^
// print(*it1);//40 //invalid
print(*it2);//60
show(li); //10, 30, 60, 80, 90
}
void lst_clear()
{
// void clear();
// Clear content
// Removes all elements from the list (which are destroyed), lealing the container with a size of 0.
list<int> li;
unsigned int i = 0;
for (i = 0; i <= 9; i++)
{
li.push_back(i);
}
show(li); //0, 1, 2, 3, 4, 5, 6, 7, 8, 9
print(li.size()); //10
print(li.empty());//0
li.clear();
show(li); //
print(li.size()); //0
print(li.empty());//1
}
int main() {
lst_construct();
lst_OperatorEQ(); //=
lst_begin_end();
lst_size();
lst_capacity(); //list invalid
lst_empty();
lst_OperatorAT(); //[] list invalid
lst_at(); // list invalid
lst_front();
lst_back();
lst_assign();
lst_push_back();
lst_pop_back();
lst_insert();
lst_erase();
lst_clear();
return 0;
}
参考资料
https://blog.csdn.net/yas12345678/article/details/52601578/
http://www.cplusplus.com/reference/list/list/