自学内容网 自学内容网

C++初阶:STL详解(六)——list的介绍和使用

✨✨小新课堂开课了,欢迎欢迎~✨✨

🎈🎈养成好习惯,先赞后看哦~🎈🎈

所属专栏:C++:由浅入深篇

小新的主页:编程版小新-CSDN博客

前言:

前面我们已经了解了string,vector的用法以及他们的底层实现,今天我们再来学习了一个新的容器list,这几个容器之间有很大的相通性,我们来一起看一下吧。

一.什么是list ?

std::list是C++模板库(STL)中的一个双向链表容器。它提供了一种灵活的方式来储存和操作一组元素。

和vector,string一样,list也是一个模板类,使用时只需指定元素类型,编译器会确保类型安全。它定义在头文件(include<list>)中,可以使用list来存储和访问任意类型的对象。 

二.list的特性

1.双向链表——每个节点包含指向前一个和后一个的指针,因此它可以在进行高效的插入删除操作

2.动态大小——根据需要动态增加和减少元素个数,没有空间浪费

3.非连续储存——元素在内存中是不连续的,不支持下标的随机访问

4.迭代器支持——支持STL迭代器,可以使用标准的算法进行遍历和操作。在进行插入和删除操作时,除了被指向被删除元素的迭代器失效,其他迭代器通常不失效,这点与vector也不同。vector在进行插入操作时迭代器不一定失效,在进行删除操作时被删除元素及其后面元素的迭代器都失效。

C++初阶:STL详解(四)——vector迭代器失效问题-CSDN博客(可供参考)

三.list的文档介绍

list - C++ Reference(英文版)

https://zh.cppreference.com/w/cpp/container/list(中文版)

 

四.list的使用

list中的接口比较多,此处类似,只需要掌握如何正确的使用,然后再去深入研究背后的原理。在实际中我们熟悉常见的接口就可以,下面列出了哪些接口是要重点掌握的 。我们在后面模拟实现的时候,也是实现这些常见的接口。

4.1list的构造

构造函数( (constructor))接口说明

list (size_type n, const value_type& val =value_type())

构造的list中包含n个值为val的元素

list()构造空的list

list (const list& x)拷贝构造函数

list (InputIterator first, InputIterator last)用[first, last)区间中的元素构造list

1.list() :构造一个空的list

list<int> st1;//构造一个空list

2.list (size_type n, const value_type& val =value_type()) :构造一个list,元素个数为n,元素为val 

list<int> st2(10, 1);//用n个值为val的元素构造一个list

3.list (const list& x) :拷贝构造

list<int> st3(st2);//拷贝构造

4.list (InputIterator first, InputIterator last) : 使用迭代器区间进行初始化构造

list<int> st4(st2.begin(), st2.end());//使用一个迭代器区间构造list

补充:构造数组某段区间的复制品

int myints[] = { 16,2,77,29 };
list<int> st5(myints, myints + sizeof(myints) / sizeof(int));//构造数组某段区间的复制品

4.2list的迭代器

此处,大家可暂时将迭代器理解成一个指针,该指针指向list中的某个节点。

函数声明接口说明

begin +end返回第一个元素的迭代器+返回最后一个元素下一个位置的迭代器

rbegin+ rend

返回指向列表最后一个元素的反向迭代器+返回指向列表第一个元素之前的反向迭代器

begin+end

//正向迭代器遍历
void list1()
{
list<int> st(10, 1);

list<int>::iterator it = st.begin();

while (it != st.end())
{
cout << *it << " ";
it++;
}

cout << endl;
}

rbegin+rend

//反向迭代器遍历
void list2()
{
list<int> st(10, 1);

list<int>::reverse_iterator rit = st.rbegin();

while (rit != st.rend())
{
cout << *rit << " ";
rit++;
}

cout << endl;
}

注意:

1. begin与end为正向迭代器,对迭代器执行++操作,迭代器向后移动

2. rbegin(end)与rend(begin)为反向迭代器,对迭代器执行++操作,迭代器向前移动

4.3list的容量

函数声明接口说明

empty

检测list是否为空,是返回true,否则返回false

size返回list中有效节点的个数

size和empty

void list3()
{
list<int>st(10, 1);

cout << st.size() << endl;//list的有效节点个数

cout << st.empty() << endl;//不为空返回0,为空返回非0
}

4.4list的访问

函数声明接口说明

front返回list的第一个节点中值的引用

back返回list的最后一个节点中值的引用

front和back

void list4()
{
list<int> st;

st.push_back(1);
st.push_back(2);
st.push_back(3);
st.push_back(4);
st.push_back(5);

cout << st.front() << endl;//返回第一个节点中值的引用 1
cout << st.back() << endl;//返回最后一个节点值的引用 5
}

 4.5list的增删查改

push_front和pop_front

push_front用于头插一个元素,pop_front用于头删一个元素

void list5()
{
list<int> st;
st.push_back(1);
st.push_back(2);
st.push_back(3);
st.push_back(4);

st.push_front(5);//在首元素前插入
cout << "插入前:";
for (auto ch : st)
{
cout << ch << " ";//5 1 2 3 4
}

cout << endl;
st.pop_front();//删除首元素

cout << "插入后:";
for (auto ch : st)
{
cout << ch << " ";// 1 2 3 4
}
}

push_back和pop_back

push_back用于尾插一个元素,pop_back用于尾删一个元素。

void list6()
{
list<int> st;
st.push_back(1);
st.push_back(2);
st.push_back(3);
st.push_back(4);

st.push_back(5);//在尾部插入
for (auto ch : st)
{
cout << ch << " ";// 1 2 3 4 5
}

cout << endl;
st.pop_back();//删除尾部元素

cout << "插入后:";
for (auto ch : st)
{
cout << ch << " ";// 1 2 3 4
}
}

insert

list当中的insert函数支持三种插入方式:

  1. 在指定迭代器位置插入一个数。
  2. 在指定迭代器位置插入n个值为val的数。
  3. 在指定迭代器位置插入一段迭代器区间(左闭右开)。
void list7()
{
list<int> st;
st.push_back(1);
st.push_back(2);
st.push_back(3);
st.push_back(4);

//在指定迭代器位置插入一个数
st.insert(st.begin(), 0); //在容器开头插入0

//在指定迭代器位置插入n个val
st.insert(st.begin(), 5, -1); //在容器开头插入5个-1

//在指定迭代器位置插入一段迭代器区间
int myarray[] = { 501,502,503 };
st.insert(st.begin(), myarray, myarray + 3);//在容器开头插入 501 502 503

for (auto ch : st)
{
cout << ch << " ";// 501 502 503 -1 -1 -1 -1 -1 0 1 2 3 4
}

}

 erase

list当中的erase函数支持两种删除方式:

  1. 删除指定迭代器位置的元素。
  2. 删除指定迭代器区间(左闭右开)的所有元素。
void list8()
{
list<int> st;
st.push_back(1);
st.push_back(2);
st.push_back(3);
st.push_back(4);
st.push_back(5);
st.push_back(6);

//删除指定位置迭代器的元素
st.erase(st.begin()); //删除容器中的第一个元素

//删除指定迭代器区间(左闭右开)的所有元素。
st.erase(st.begin(), st.end()); //删除在该迭代器区间内的元素(左闭右开)

for (auto ch : st)
{
cout << ch << " ";
}

}

resize

resize的两种情况:

  1. 当所给值大于当前的size时,将size扩大到该值,扩大的数据为第二个所给值,若未给出,则默认为容器所存储类型的默认构造函数所构造出来的值。
  2. 当所给值小于当前的size时,将size缩小到该值。
void list9()
{
list<int> st;
st.push_back(1);
st.push_back(2);
st.push_back(3);
st.push_back(4);
st.push_back(5);
st.push_back(6);

st.resize(10, 9);//将size扩大到10,以9来填充

for (auto ch : st)
{
cout << ch << " ";//1 2 3 4 5 6 9 9 9 9 
}

cout << endl;

st.resize(4);//将size缩小到4

for (auto ch : st)
{
cout << ch << " "; //1 2 3 4
}

cout << endl;

}

assign

list中的assign函数用于将新内容分配给容器,替换其当前内容,新内容的赋予方式有两种:

  1. 将n个值为val的数据分配给容器。
  2. 将所给迭代器区间当中的内容分配给容器。
void list10()
{
list<int> st;
st.push_back(1);
st.push_back(2);
st.push_back(3);
st.push_back(4);
st.push_back(5);
st.push_back(6);

for (auto ch : st)
{
cout << ch << " ";//1 2 3 4 5 6
}
cout << endl;

//将新内容分配给容器,替换当前的内容
st.assign(10, 1);//用n个val替换
for (auto ch : st)
{
cout << ch << " "; //1 1 1 1 1 1 1 1 1 1
}

cout << endl;

//将新内容分配给容器,替换当前的内容
int myints[] = { 1776,7,4 };
st.assign(myints, myints + 3);//迭代器区间所指的内容进行替换

for (auto ch : st)
{
cout << ch << " ";//1776 7 4
}
cout << endl;


}

swap

交换两个list的元素。

void list11()
{
list<int> st1;
st1.push_back(1);
st1.push_back(2);
st1.push_back(3);
st1.push_back(4);
st1.push_back(5);
st1.push_back(6);

cout << "st1交换前:";
for (auto ch : st1)
{
cout << ch << " ";//1 2 3 4 5 6
}
cout << endl;

list<int> st2;
st2.push_back(1);
st2.push_back(2);
st2.push_back(3);
st2.push_back(4);

cout << "st2交换前:";
for (auto ch : st2)
{
cout << ch << " ";//1 2 3 4
}
cout << endl;

st1.swap(st2);
cout << "st1交换后:";
for (auto ch : st1)
{
cout << ch << " ";//1 2 3 4 
}
cout << endl;

cout << "st2交换前:";
for (auto ch : st2)
{
cout << ch << " ";//1 2 3 4 5 6
}
cout << endl;
}

clear

清空容器的内容。

void list12()
{
list<int> st;
st.push_back(1);
st.push_back(2);
st.push_back(3);
st.push_back(4);

for (auto ch : st)
{
cout << ch << " ";//1 2 3 4 
}
cout << endl;
cout << st.size() << endl;//4
st.clear();//清空容器的内容

cout << st.size() << endl;//0

}

4.6list的操作函数

sort

给容器中的内容排序,默认是排升序。

void list01()
{
list<int> st;
st.push_back(1);
st.push_back(2);
st.push_back(9);
st.push_back(3);
st.push_back(5);
st.push_back(4);
st.push_back(10);
st.push_back(6);

st.sort();//默认排升序

for (auto ch : st)
{
cout << ch << " ";//1 2 3 4 5 6 9 10
}
cout << endl;

}

splice

用于两个list的拼接,其有三种拼接方式:

  1. 将整个容器拼接到另一个容器的指定迭代器位置。
  2. 将容器当中的某一个数据拼接到另一个容器的指定迭代器位置。
  3. 将容器指定迭代器区间的数据拼接到另一个容器的指定迭代器位置。
void list02()
{
list<int> st1(4, 1);
list<int> st2(5, 2);
st1.splice(st1.begin(), st2); //将容器st2拼接到容器st1的开头
for (auto ch : st1)
{
cout << ch << " "; //2 2 2 2 2 1 1 1 1
}
cout << endl;

list<int> st3(4, 1);
list<int> st4(5, 2);
st3.splice(st3.begin(), st4, st4.begin()); //将容器st4的第一个数据拼接到容器st4的开头
for (auto ch : st3)
{
cout << ch << " ";//2 1 1 1 1
}
cout << endl;


list<int> st5(4, 1);
list<int> st6(5,2);
st5.splice(st5.begin(), st6, st6.begin(), st6.end()); //将容器st6的指定迭代器区间内的数据拼接到容器st6的开头
for (auto ch : st5)
{
cout << ch << " ";//2 2 2 2 2 1 1 1 1
}
cout << endl; 

}

remove

删除容器中特定元素。

void list03()
{
list<int> st;
st.push_back(1);
st.push_back(2);
st.push_back(3);
st.push_back(4);

st.remove(3);//删除指定元素
for (auto ch : st)
{
cout << ch << " ";//1 2 4
}
cout << endl;

}

remove_if

删除容器中满足条件元素。

bool single_digit(const int& val)
{
return val < 10;
}

void list04()
{
list<int> st;
st.push_back(1);
st.push_back(2);
st.push_back(3);
st.push_back(4);
st.push_back(10);
st.push_back(20);
st.push_back(30);
st.push_back(40);

st.remove_if(single_digit);//删除满足条件的元素

for (auto ch : st)
{
cout << ch << " ";//10 20 30 40
}
cout << endl;

}

unique

删除容器中连续重复元素。

void list05()
{
list<int> st;
st.push_back(1);
st.push_back(2);
st.push_back(2);
st.push_back(4);
st.push_back(10);
st.push_back(10);
st.push_back(10);
st.push_back(40);//有序

//如果list无序,可以使用sort使其有序
st.unique();//删除连续重复元素

for (auto ch : st)
{
cout << ch << " ";//1 2 4 10 40
}
cout << endl;

}

merge

合并两个有序容器,合并后依然有序。

void list06()
{
list<int> st1;
st1.push_back(8);
st1.push_back(3);
st1.push_back(1);
st1.push_back(6);

list<int> st2;
st2.push_back(0);
st2.push_back(10);
st2.push_back(2);
st2.push_back(5);

st1.sort();
st2.sort();//先满足是两个有序链表

//合并两个有序链表
st1.merge(st2);

//合并后依然有序
for (auto ch : st1)
{
cout << ch << " ";//0 1 2 3 5 6 8 10
}
cout << endl;
}

reverse 

将容器中的元素位置进行逆置。

void list07()
{
list<int> st1;
st1.push_back(8);
st1.push_back(3);
st1.push_back(1);
st1.push_back(6);

cout << "逆置前:";
for (auto ch : st1)
{
cout << ch << " ";//8 3 1 6
}
cout << endl;

st1.reverse();
cout << "逆置后:";
for (auto ch : st1)
{
cout << ch << " ";//6 1 3 8
}
cout << endl;
}

五.list的迭代器失效

前面说过,此处大家可将迭代器暂时理解成类似于指针,迭代器失效即迭代器所指向的节点的无效,即该节点被删除了。因为list的底层结构为带头结点的双向循环链表,因此在list中进行插入时是不会导致list的迭代器失效的,只有在删除时才会失效,并且失效的只是指向被删除节点的迭代器,其他迭代器不会受到影响。vector的迭代器失效理解了,这里就没什么可以说的了。

C++初阶:STL详解(四)——vector迭代器失效问题-CSDN博客

void TestListIterator1()
{
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;
}
} 

 运行结果:

改正:万能钥匙:在使用前,对迭代器重新赋值即可。

void TestListIterator()
{
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())
{
l.erase(it++); // it = l.erase(it);
}
}

 六.list 和vector的对比

vectorlist
底 层 结 构动态顺序表,一段连续空间带头结点的双向循环链表
随 机 访 问支持随机访问,访问某个元素效率O(1)不支持随机访问,访问某个元素O(N)
插 入 和 删 除任意位置插入和删除效率低,需要搬移元素,时间复杂度为O(N),插入时有可能需要增容,增容:开辟新空间,拷贝元素,释放旧空间导致效率更低任意位置插入和删除效率高,
不需要搬移元素,时间复杂度
为O(1)
空间利用率底层为连续空间,不容易造成内存碎片,空间利用率高,缓存利用率高底层节点动态开辟,小节点容
易造成内存碎片,空间利用率
低,缓存利用率低
迭 代 器 失 效在插入元素时,要给所有的迭代器重新赋值,因为插入元素有可能会导致重新扩容,致使原来迭代器失效,删除时,当前迭代器需要重新赋值否则会失效插入元素不会导致迭代器失效,删除元素时,只会导致当前迭代器失效,其他迭代器不受影响
使 用 场 景需要高效存储,支持随机访问,不关心插入删除效率大量插入和删除操作,不关心
随机访问
迭代器原生态指针对原生态指针(节点指针)进行
封装

总结:

list的常见接口及其用法,我们已经介绍完了,准备好一起来模拟实现list了嘛,我们下篇见。 

感谢各位大佬的观看,创作不易,还请各位大佬支持~ 


原文地址:https://blog.csdn.net/Zhihuiguoz/article/details/142438233

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!