*C++:list
一.list简介
1. list
是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代。
2. list
的底层是双向链表结构,双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向其前一个元素和后一个元素。
3. list
与
forward_list
非常相似:最主要的不同在于
forward_list
是单链表,只能朝前迭代,已让其更简单高效。
4.
与其他的序列式容器相比
(array
,
vector
,
deque)
,
list通常在任意位置进行插入、移除元素的执行效率更好。
5.
与其他序列式容器相比,
list
和
forward_list
最大的缺陷是不支持任意位置的随机访问,比如:要访问
list的第6
个元素,必须从已知的位置
(
比如头部或者尾部
)
迭代到该位置,在这段位置上迭代需要线性的时间开销;list
还需要一些额外的空间,以保存每个节点的相关联信息
(
对于存储类型较小元素的大
list
来说这可能是一个重要的因素)
二.标准库里的list类
标准list库网址:list - C++ Reference
1.list的构造
2.list迭代器
将迭代器理解成一个指针,该指针指向
list
中的某个节点
list是包含一个哨兵位的
注意:
1. begin
与
end
为正向迭代器,对迭代器执行
++操作,迭代器向后移动
2. rbegin(end)
与
rend(begin)
为反向迭代器,对迭代器执行
++操作,迭代器向前移动
list迭代器失效问题
迭代器可以暂时理解成类似于指针,迭代器失效即迭代器所指向的节点的无效,即该节点被删除了。因为list的底层结构为带头结点的双向循环链表,因此在list中进行插入时是不会导致list的迭代器失效的,只有在删除时才会失效,并且失效的只是指向被删除节点的迭代器,其他迭代器不会受到影响。
int main()
{
int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
list<int> l(array, array + sizeof(array) / sizeof(array[0]));
auto it = l.begin();
while (it != l.end())
{
// erase()函数执行后,it所指向的节点已被删除,因此it无效,在下一次使用it时,必须先给其赋值
l.erase(it++); //it = l.erase(it);
}
return 0;
}
这里l.erase(it++)要理解这个写法,相当于是先锁定了it的位置,这样erase后直接在这个位置上++就行;与l.erase(it);++it;不同,因为编译器里是一行一行执行的,在同一行内,it还不会失效,如果我们在进入下一行之前就给他重新找好位置的话就不会有问题。
3.list capacity
4.list元素访问
5.list元素修改
6.构造函数,插入,删除
int main()
{
list<int> lt;
lt.push_back(1);
lt.push_back(2);
lt.push_back(3);
lt.push_back(4);
lt.push_front(10);
lt.push_front(20);
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
// 第5个位置插入数据
//v.insert(v.begin()+5, 10);
//list是一个双向链表,不是连续的,begin()+5不是第5个位置
//不用挪动数据
auto it = lt.begin();
for (size_t i = 0; i < 5; i++)
{
++it; //这个++是个运算符重载,不是平时所谓的+1
}
lt.insert(it, 100);
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
//list没有find,这个find是std里的
it = find(lt.begin(), lt.end(), 3);
if (it != lt.end())
{
lt.insert(it, 30);
// insert以后,it不失效
*it *= 100;
}
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
it = find(lt.begin(), lt.end(), 2);
if (it != lt.end())
{
lt.erase(it);
//erase以后,it失效了
// *it *= 100;
}
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
it = lt.begin();
while (it != lt.end())
{
if (*it % 2 == 0)
{
it = lt.erase(it); //如果持续删除,需要先用迭代器it保存,erase是有一个返回值的
}
else
{
++it;
}
}
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
return 0;
}
7.reverse,sort
int main()
{
list<int> lt;
lt.push_back(1);
lt.push_back(2);
lt.push_back(3);
lt.push_back(4);
lt.push_front(10);
lt.push_front(20);
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
reverse(lt.begin(), lt.end());
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
lt.reverse();
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
//sort是RandomAccessIterator随机迭代器,但是list是双向迭代器,不适用
//sort(lt.begin(), lt.end());
lt.sort();
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
return 0;
}
可以看到,reverse可以使用std的函数,但是sort不可以,因为list是双向迭代器,std::sort不适用。
8.remove splice
int main()
{
int myints[] = { 17,89,7,14 };
std::list<int> mylist(myints, myints + 4);
mylist.remove(89);
for (auto e : mylist)
{
cout << e << " ";
}
cout << endl;
}
可以看到,remove可以删除指定val值的位置元素,与erase不同,erase删除的是指定位置的值。
int main()
{
list<int> mylist1, mylist2;
list<int>::iterator it;
// set some initial values:
for (int i = 1; i <= 4; ++i)
mylist1.push_back(i); // mylist1: 1 2 3 4
for (int i = 1; i <= 3; ++i)
mylist2.push_back(i * 10); // mylist2: 10 20 30
for (auto e : mylist1)
{
cout << e << " ";
}
cout << endl;
for (auto e : mylist2)
{
cout << e << " ";
}
cout << endl << endl;
it = mylist1.begin();
++it; // points to 2
// 全部转移
mylist1.splice(it, mylist2); //把mylist2里的东西,从it位置开始,转移到mylist1里
// 部分转移
//mylist1.splice(it, mylist2, ++mylist2.begin()); //把mylist2里++mylist2.begin()里的东西,从it位置开始,转移到mylist1里
//mylist1.splice(it, mylist2, ++mylist2.begin(), mylist2.end()); //把mylist2里++mylist2.begin()到mylist2.end()里的东西,从it位置开始,转移到mylist1里
//mylist1.splice(mylist1.begin(), mylist1, ++mylist1.begin(), mylist1.end()); //支持把自己的数据转移给自己
cout << "mylist1: ";
for (auto e : mylist1)
{
cout << e << " ";
}
cout << endl;
cout << "mylist2: ";
for (auto e : mylist2)
{
cout << e << " ";
}
cout << endl;
}
三.list和vector对比
void test_op()
{
srand(time(0));
const int N = 1000000;
vector<int> v;
v.reserve(N);
list<int> lt1;
list<int> lt2;
for (int i = 0; i < N; ++i)
{
auto e = rand();
lt2.push_back(e);
lt1.push_back(e);
}
// 拷贝到vector排序,排完以后再拷贝回来
int begin1 = clock();
// 先拷贝到vector
for (auto e : lt1)
{
v.push_back(e);
}
// 排序
sort(v.begin(), v.end());
// 拷贝回去
size_t i = 0;
for (auto& e : lt1)
{
e = v[i++];
}
int end1 = clock();
int begin2 = clock();
//list的sort效率太慢
//相对便捷,数据量小的时候可以用
lt2.sort();
int end2 = clock();
printf("vector sort:%d\n", end1 - begin1);
printf("list sort:%d\n", end2 - begin2);
}
int main()
{
test_op();
}
可以看到,即使是多进行了两次人为拷贝,vector效率依旧比list高,可以证明,在处理大量数据的时候,vector效率明显优于list,list相对便捷,在处理少量数据的时候可以使用。
四.list类模拟实现
课件代码/C++课件V6/List的模拟实现/List.h · will/C++上课 - Gitee.com