【C++】介绍STL中list容器的常用接口

目录

一、STL中的list简介

二、构造函数

2.1 默认构造函数

2.2 填充构造(用n个相同的值构造)

2.3 迭代器构造 

2.4 拷贝构造和赋值运算符重载 

三、迭代器

3.1 正向迭代器

3.2 反向迭代器

四、容量相关 

4.1 获取list中有效数据的个数

4.2 判断list是否为空 —— empty

五、元素访问

5.1 获取第一个有效节点的数据

5.2 获取最后一个有效节点的数据

六、修改相关

6.1 头插、尾插 —— push_front、push_back

6.2 尾插、尾删 —— pop_front、pop_back 

6.3 在任意位置插入

6.4 在任意位置删除 —— erase

6.5 交换两个list对象

6.6 清空list中的有效节点

七、链表操作

7.1 将一个list上的数据转移到另一个list —— splice

7.2 删除某个特定数据

7.3 给数据排序 —— sort

7.4 去除重复数据 —— unique

7.5 反转数据 —— reverse

八、list与vector的对比 


一、STL中的list简介

1. list是可以在常数时间(O(1))内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代

2. listforward_list非常相似,最主要的不同在于forward_list是单链表,只能向前迭代,这让forward_list更简单高效。

3. list的底层是双向链表结构,双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向其前一个元素和后一个元素。

4. 与其他的序列式容器(arrayvector等)相比,list通常在任意位置进行插入、移除元素的执行效率更好。

5. 与其他序列式容器相比,listforward_list最大的缺陷是不支持任意位置的随机访问,必须从已知的位置(比如头部或者尾部)迭代到目标位置,而迭代到目标位置需要线性的时间(O(n))开销;除此之外list还需要一些额外的空间,以保存每个节点的相关联系。

【文档描述】

【C++】介绍STL中list容器的常用接口_第1张图片


二、构造函数

2.1 默认构造函数

【代码演示】

#include 
#include 
#include 
using namespace std;

int main()
{
	//默认拷贝拷贝构造 list lt;(T为list存储的数据类型)
	list ls;

	return 0;
}

【输出结果】

【C++】介绍STL中list容器的常用接口_第2张图片

        注:list不像stringvectorlist没有容量capacity这个属性。 


2.2 填充构造(用n个相同的值构造)

【代码演示】

#include 
#include 
using namespace std;

int main()
{
	//填充构造    list (size_type n, const value_type& val = value_type())

	list li(5, 20);

	//如果不指定val的值就会初始化为相应类型的默认值
	//int为0 double为0.0 char为'\0'……
	list ld(3);

	for (const auto& k : li)
	{
		cout << k << " ";
	}
	cout << endl;

	for (const auto& k : ld)
	{
		cout << k << " ";
	}
	cout << endl;
	return 0;
}

【输出结果】

【C++】介绍STL中list容器的常用接口_第3张图片


2.3 迭代器构造 

【代码演示】

#include 
#include 
#include 
using namespace std;

int main()
{
	//迭代器构造 list (InputIterator first, InputIterator last) 
    //迭代器区间为左闭右开 -》 [first, last)

	//用数组构造
	int array[] = { 1,2,3,4,5,6 };
	list li(array, array + sizeof(array) / sizeof(array[0]));

	//这个其实是先用数组构造了一个list temp 
    //然后用拷贝构造将temp拷贝给了li_another -》 list li_another(temp)
	list li_another{ 1,2,3,4,5 };

	//可以用别的容器的迭代器来构造list
	string s("Hello list!");
	list lc(s.begin(), s.end());

	//可以用list的迭代器来构造list
	list lc_copy(++lc.begin(), --lc.end());

	for (const auto& k : li)
	{
		cout << k << " ";
	}
	cout << endl;

	for (const auto& k : li_another)
	{
		cout << k << " ";
	}
	cout << endl;

	for (const auto& k : lc)
	{
		cout << k;
	}
	cout << endl;

	for (const auto& k : lc_copy)
	{
		cout << k;
	}
	cout << endl;
	return 0;
}

【输出结果】

【C++】介绍STL中list容器的常用接口_第4张图片


2.4 拷贝构造和赋值运算符重载 

【代码演示】

#include 
#include 
using namespace std;

int main()
{
	//拷贝构造 list (const list& x)
    //赋值运算符重载 list& operator=(const list& x)

	list li{ 1,2,3,4,5 };

	//拷贝构造
	list li_copy(li);

	//赋值运算符重载
	list li_another = li;

	for (const auto& k : li)
	{
		cout << k << " ";
	}
	cout << endl;	

	for (const auto& k : li_copy)
	{
		cout << k << " ";
	}
	cout << endl;

	for (const auto& k : li_another)
	{
		cout << k << " ";
	}
	cout << endl;
	return 0;
}

【输出结果】 

【C++】介绍STL中list容器的常用接口_第5张图片


三、迭代器

【示意图】

【C++】介绍STL中list容器的常用接口_第6张图片

3.1 正向迭代器

【代码演示】

#include 
#include 
using namespace std;

int main()
{
	list li{ 1,2,3,4,5 };
	cout << *li.begin() << endl;
	cout << *(--li.end()) << endl;

	return 0;
}

        list的迭代器不能直接使用加法或减法(如+1或-1)来实现迭代器位置的更换,因为list的各个节点并不在一块连续的空间,如果直接通过加减法来实现迭代器位置的更换会造成非法访问。所以我们需要使用重载过后的前置或后置的加加、减减操作符来实现迭代器位置的变更。

 【输出结果】

【C++】介绍STL中list容器的常用接口_第7张图片


3.2 反向迭代器

【代码演示】

#include 
#include 
using namespace std;

int main()
{
	list li{ 1,2,3,4,5,6,7 };
	list::reverse_iterator rbit = li.rbegin();
	while (rbit != li.rend())
	{
		//从begin到end 从rbegin到rend都是++
		cout << *rbit << " ";
		++rbit;
	}
	cout << endl;
	return 0;
}

【输出结果】

【C++】介绍STL中list容器的常用接口_第8张图片


四、容量相关 

4.1 获取list中有效数据的个数

【代码演示】

#include 
#include 
#include 
using namespace std;

int main()
{
	list ls{ "Hello", "lisr", "and", "string", "!" };
	cout << ls.size() << endl;
	return 0;
}

【输出结果】 

【C++】介绍STL中list容器的常用接口_第9张图片


4.2 判断list是否为空 —— empty

【代码演示】

#include 
#include 
using namespace std;

int main()
{
	list li;
	cout << li.empty() << endl;

	//尾插一个1
	li.push_back(1);

	cout << li.empty() << endl;
	return 0;
}

【输出结果】

【C++】介绍STL中list容器的常用接口_第10张图片


五、元素访问

5.1 获取第一个有效节点的数据

【代码演示】

#include 
#include 
using namespace std;

int main()
{
	list li{ 1,2,3,4,5,6 };
	cout << li.front() << endl;

	return 0;
}

【输出结果】

【C++】介绍STL中list容器的常用接口_第11张图片


5.2 获取最后一个有效节点的数据

#include 
#include 
using namespace std;

int main()
{
	list ls{ "Hello", "list!" };
	cout << ls.back() << endl;

	return 0;
}

【输出结果】

【C++】介绍STL中list容器的常用接口_第12张图片


六、修改相关

6.1 头插、尾插 —— push_front、push_back

【代码演示】

#include 
#include 
using namespace std;

int main()
{
	list li{ 2,3,4,5 };

	//头插1
	li.push_front(1);

	//尾插6
	li.push_back(6);

	for (const auto& k : li)
	{
		cout << k << " ";
	}
	cout << endl;

	return 0;
}

【输出结果】

【C++】介绍STL中list容器的常用接口_第13张图片


6.2 尾插、尾删 —— pop_front、pop_back 

【代码演示】

#include 
#include 
using namespace std;

int main()
{
	list li{ 1,2,3,4,5,6 };
	//头删
	li.pop_front();

	//尾删
	li.pop_back();

	for (const auto& k : li)
	{
		cout << k << " ";
	}
	cout << endl;

	return 0;
}

【输出结果】

【C++】介绍STL中list容器的常用接口_第14张图片


6.3 在任意位置插入

【代码演示】

#include 
#include 
using namespace std;

int main()
{
	list li{ 1,2,3,5,6,7 };

	list::iterator pos = li.begin();

	//list的迭代器不支持直接加减一个值。
    //比如我们要在5的前面插入一个4,就必须让pos++3次,而不能pos + 3
	//循环的次数可以看成目标位置下标的数值,但不是真正的下标
    //因为list节点并不分布在一块连续的空间
	for (int i = 0; i < 3; ++i)
	{
		++pos;
	}

	li.insert(pos, 4);

	for (const auto& k : li)
	{
		cout << k << " ";
	}
	cout << endl;
	return 0;
}

【输出结果】

【C++】介绍STL中list容器的常用接口_第15张图片

        list的插入操作不像vector的插入操作,在插入数据中如果进行了扩容,vector会发生迭代器失效,而list不会。这是因为vector的扩容是开辟一块更大的空间,将原空间数据拷贝到新空间后再释放原空间,而迭代器pos任然指向旧空间,这样就会对非法空间进行访问。但是list的扩容只是开辟一块空间来存储新的数据,其他数据的位置和关系并不会发生变化,所以并不会发生迭代器失效。


6.4 在任意位置删除 —— erase

#include 
#include 
using namespace std;

int main()
{
	list li{ 1,2,3,4,5,6,7,8 };
	list::iterator pos = li.begin();

	//删除list中的所有偶数
	while (pos != li.end())
	{
        //避免发生迭代器失效
		if (*pos % 2 == 0)
		{
			pos = li.erase(pos);
		}
		else
		{
			++pos;
		}
	}

	for (const auto& k : li)
	{
		cout << k << " ";
	}
	cout << endl;

	return 0;
}

        虽然list在插入过程中不会发生迭代器失效,但是在删除过程中任然会发生迭代器失效。为了避免这种情况我们就需要利用erase的返回值

        erase的返回值是一个指向被删除数据的下一个数据的迭代器,我们在执行删除操作之后让迭代器pos等于erase的返回值,如果没有执行就正常++,这样就能避免迭代器失效问题了。


6.5 交换两个list对象

【代码演示】

#include 
#include 
using namespace std;

int main()
{
	list li1{ 1, 2, 3, 4 ,5 };
	list li2{ 6, 7, 8 ,9, 10 };

	li1.swap(li2);

	cout << "li1的数据为:";
	for (const auto& k : li1)
	{
		cout << k << " ";
	}
	cout << endl;

	cout << "li2的数据为:";
	for (const auto& k : li2)
	{
		cout << k << " ";
	}
	cout << endl;
	return 0;
}

【输出结果】

【C++】介绍STL中list容器的常用接口_第16张图片


6.6 清空list中的有效节点

【代码演示】

#include 
#include 
using namespace std;

int main()
{
	list li{ 1,2,3,4,5 };
	cout << "clear前的数据为:";
	for (const auto& k : li)
	{
		cout << k << " ";
	}
	cout << endl;

	li.clear();
	cout << "clear后的数据为:";
	for (const auto& k : li)
	{
		cout << k << " ";
	}
	cout << endl;
	return 0;
}

【输出结果】

【C++】介绍STL中list容器的常用接口_第17张图片


七、链表操作

7.1 将一个list上的数据转移到另一个list —— splice

【函数原型】

【C++】介绍STL中list容器的常用接口_第18张图片

【代码演示】

#include 
#include 
using namespace std;

int main()
{
	list des1{ 1,2,3,4,5 };
	list des2(des1);
	list des3 = des2;

	list sour1{ 6,7,8,9,10 };
	list sour2(sour1);
	list sour3 = sour2;

	//转移一整个链表
	des1.splice(des1.end(), sour1);

	//转移一个元素
	des2.splice(des2.end(), sour2, sour2.begin());

	//转移一个区间 -》左闭右开
	des3.splice(des3.end(), sour3, ++sour3.begin(), --sour3.end());

	cout << "des1的数据为:";
	for (const auto& k : des1)
	{
		cout << k << " ";
	}
	cout << endl;

	cout << "sour1的数据为:";
	for (const auto& k : sour1)
	{
		cout << k << " ";
	}
	cout << endl;

	cout << "des2的数据为:";
	for (const auto& k : des2)
	{
		cout << k << " ";
	}
	cout << endl;

	cout << "sour2的数据为:";
	for (const auto& k : sour2)
	{
		cout << k << " ";
	}
	cout << endl;

	cout << "des3的数据为:";
	for (const auto& k : des3)
	{
		cout << k << " ";
	}
	cout << endl;

	cout << "sour3的数据为:";
	for (const auto& k : sour3)
	{
		cout << k << " ";
	}
	cout << endl;
	return 0;
}

【输出结果】

【C++】介绍STL中list容器的常用接口_第19张图片


7.2 删除某个特定数据

【代码演示】

#include 
#include 
using namespace std;

int main()
{
	list li{ 1,2,3,1,1,5,8,5,4,3 };
	cout << "remove前的数据为:";
	for (const auto& k : li)
	{
		cout << k << " ";
	}
	cout << endl;

	li.remove(1);
	cout << "remove后的数据为:";
	for (const auto& k : li)
	{
		cout << k << " ";
	}
	cout << endl;

	return 0;
}

【输出结果】

【C++】介绍STL中list容器的常用接口_第20张图片


7.3 给数据排序 —— sort

【代码演示】

#include 
#include 
using namespace std;

int main()
{
	list li{ 32,5,86,12,6,2 };
    
    //list排序通过归并排序实现
	li.sort();
	for (auto& k : li)
	{
		cout << k << " ";
	}
	cout << endl;

	return 0;
}

【输出结果】

【C++】介绍STL中list容器的常用接口_第21张图片

         注:list用不了algorithm算法库中的sort,因为algorithmsort的参数要求是随机迭代器RandomAccessIterator,而list的迭代器是双向迭代器bidirectional iterator

【C++】介绍STL中list容器的常用接口_第22张图片

        除了随机迭代器RandomAccessIterator和双向迭代器bidirectional iterator外还有一种迭代器类型是InputIterator,这个迭代器类型就是只要是迭代器就行。

        不过话虽如此,但并不建议对list进行排序,因为list的数据在空间上是分散开的,并不适合排序,list排序的效率远远不及vector这种在连续空间上储存数据的容器,因此并不建议对list进行排序。


7.4 去除重复数据 —— unique

【代码演示】

#include 
#include 
using namespace std;

int main()
{
	list li{ 1,4,6,4,6,7,8,9,1,3,5 };

	//在去重之前建议先对list进行排序 不然效率实在是太低了 不过排序的效率也很低就是了
	li.sort();
	li.unique();

	for (const auto& k : li)
	{
		cout << k << " ";
	}
	cout << endl;
	return 0;
}

【输出结果】

【C++】介绍STL中list容器的常用接口_第23张图片


7.5 反转数据 —— reverse

【代码演示】

#include 
#include 
using namespace std;

int main()
{
	list li{ 1,2,3,4,5,6 };

	li.reverse();
	for (const auto& k : li)
	{
		cout << k << " ";
	}
	cout << endl;

	return 0;
}

【输出结果】

【C++】介绍STL中list容器的常用接口_第24张图片


八、list与vector的对比 

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

你可能感兴趣的:(C++,c++,笔记,学习,stl,list)