list的底层实际是双向链表结构
构造函数 | 说明 |
---|---|
list() | 无参构造 |
list (size_type n, const value_type& val = value_type()) | 构造的list中包含n个值为val的元素 |
list (const list& x) | 拷贝构造函数 |
list (InputIterator first, InputIterator last) | 用[first, last)区间中的元素构造list |
构造函数和前面的容器用法相同
void test1()
{
list<int> lt1;//无参构造
list<int> lt2(10, 1);//1,1,1,1,1,1,1,1,1,1
list<int> lt3(lt2);//拷贝构造
list<int> lt4(lt2.begin(), lt2.end());
}
赋值重载
list& operator= (const list& x);
void test1()
{
list<int> lt1;//无参构造
list<int> lt2(10, 1);//1,1,1,1,1,1,1,1,1,1
list<int> lt3(lt2);//拷贝构造
list<int> lt4(lt2.begin(), lt2.end());
list<int> lt5;
lt5 = lt4;//赋值重载
}
迭代器类型分为三种:单向迭代器、双向迭代器、随机迭代器
单向迭代器:支持++ 例如forward_list、哈希的迭代器类型是单向迭代器
双向迭代器:支持++,- - 例如list、map、set的迭代器类型是双向迭代器
随机迭代器:支持++,- -,+,- 例如vector、string、deque的迭代器类型是随机迭代器
随机迭代器可以认为是特殊的双向迭代器,双向迭代器可以认为是特殊的单向迭代器
list的迭代器与vector
和string
不同
迭代器的类型是与容器底层结构决定的
vector
和string
的底层是连续的,所以它们的迭代器实际上就是指针,所以它们支持++,–,+,- ,类型是随机迭代器
而list
的底层是不连续的,前后是通过指针连接在一起的,所以它的迭代器不是指针(实际上是指针经过封装),经过封装后,迭代器会支持++、- -,类型是单向迭代器
对于不支持
+
,-
,这样封装是因为:像it.begin()+5
这样的效率太低,C++不支持
list的迭代器不支持像vector中的it.begin()+5
,如果这样写会报错
void test2()
{
list<int> lt{ 1,2,3,4,5,6 };
lt.begin()--;
lt.begin()++;
}
如果想将list
的迭代器像vector
中it.begin()+5
一样移动多个位置,只能这样:
void test2()
{
list<int> lt{ 1,2,3,4,5,6 };
list<int>::iterator it = lt.begin();
for (size_t i = 0; i < 5; i++)
{
++it;
}
}
empty
bool empty() const;
判断容器是否为空
size
size_type size() const;
返回容器中元素的个数
list
作为双向循环链表,头插,头删,尾插,尾删的效率都很高,所以list中都支持这些操作
函数 | 说明 |
---|---|
void push_front (const value_type& val); | 在list首元素前插入值为val的元素 |
void pop_front(); | 删除list中第一个元素 |
void push_back (const value_type& val); | 在list尾部插入值为val的元素 |
void pop_back(); | 删除list中最后一个元素 |
void test3()
{
list<int> lt{ 1,2,3,4,5,6 };
lt.push_back(10);
lt.push_front(0);
lt.pop_back();
lt.pop_front();
}
insert
iterator insert (iterator position, const value_type& val);
void insert (iterator position, size_type n, const value_type& val);
template <class InputIterator>
void insert (iterator position, InputIterator first, InputIterator last);
insert
的操作和vector
中的用相同,但是这个insert
不会导致迭代器失效
因为链表的插入必须要扩容,迭代器是指向某一个节点,插入后迭代器还是指向原先的节点,不会导致失效
erase
iterator erase (iterator position);
iterator erase (iterator first, iterator last);
erase
的操作和vector
中的用相同,这个erase
会导致迭代器失效
迭代器是指向某一个节点,删除这个节点后,迭代器失效
void reverse();
这个reverse
是list
类中自带的一个函数,作用是逆置链表
而
中也有一个reverse
函数
reverse
函数中迭代器类型是双向迭代器,而list的迭代器类型就是双向迭代器,所以list也可以使用
中的reverse
void test4()
{
list<int> lt{ 1,2,3,4,5,6 };
lt.reverse();
reverse(lt.begin(), lt.end());
}
void sort();
作用是排序,底层是归并
中也有sort
函数,但是对于list来说,想要排序,只能使用list
库中的sort
函数,不能使用
中也有sort
因为list的迭代器类型是双向迭代器,而
中的sort
的参数迭代器的类型是随机迭代器,所以list不能使用
中的sort
函数。
其实这里的sort
意义不大,因为相对于
中的sort
效率低(list
中的sort
底层使用的是归并,
中的sort
使用的是快排)
而唯一的意义是:方便,数据量小了可以拍,但是数据量再大,就不要使用list
中的sort
了
如果想要排序,完全可以把list
中的数据拷贝到vector
中,然后排vector
,排序完之后再把数据拷贝会list
中
void test5()
{
list<int> lt{ 5,7,3,9,1,0,4,7,8,9,4, };
vector<int> v;
//将数据从list拷贝到vector
for (auto e : lt)
{
v.push_back(e);
}
//在vector中排序
reverse(v.begin(), v.end());
//再把数据从vector拷贝到list中
for (auto e : v)
{
lt.push_back(e);
}
}
void unique();
作用是去重,但是需要先排序
void test6()
{
list<int> lt{2,6,5,2,2,2,2};
lt.sort();
lt.unique();// 5,6
}
void remove (const value_type& val);
remove的作用是先找到所有val
的位置,然后erase
掉所有的val
void test6()
{
list<int> lt{1,2,3,4,5,6,6,7,8};
//移除元素6
lt.remove(6);//1,2,3,4,5,7,8
}
void splice (iterator position, list& x);
void splice (iterator position, list& x, iterator i);
void splice (iterator position, list& x, iterator first, iterator last);
splice的作用是转移节点
void splice (iterator position, list& x)
,将x
链表中的所有元素转移到position
位置void splice (iterator position, list& x, iterator i)
,将x
链表中i
位置的元素转移到position
位置void splice (iterator position, list& x, iterator first, iterator last)
,将x
链表中[first,last)中的元素转移到position
位置void test7()
{
list<int> lt1{ 1,2,3,4,5,6,7 };
list<int> lt2{ 0,0 };
lt2.splice(++lt2.begin(), lt1);
for (auto e : lt1)
{
cout << e << " ";
}//lt1中的元素转移空了
cout << endl;
for (auto e : lt2)
{
cout << e << " ";
}//0 1 2 3 4 5 6 7 0
cout << endl;
list<int> lt3{ 1,2,3,4,5,6,7 };
list<int> lt4{ 0,0 };
lt4.splice(++lt4.begin(), lt3, ++lt3.begin());
for (auto e : lt3)
{
cout << e << " ";
}//1 3 4 5 6 7
cout << endl;
for (auto e : lt4)
{
cout << e << " ";
}//0 2 0
cout << endl;
list<int> lt5{ 1,2,3,4,5,6,7 };
list<int> lt6{ 0,0 };
lt6.splice(++lt6.begin(), lt5,++++lt5.begin(), --lt5.end());
for (auto e : lt5)
{
cout << e << " ";
}//1 2 7
cout << endl;
for (auto e : lt6)
{
cout << e << " ";
}//0 3 4 5 6 0
cout << endl;
}