顺序容器 -list双向链表

发布于:2025-04-02 ⋅ 阅读:(25) ⋅ 点赞:(0)

list双向链表

STL中的list是一个双向循环链表,如下图所示:

使用list时必须先包含头文件

#include <list>

其中的list类型定义于namespace std中,是个class template:

namespace std {
    template< typename T,
    typename Allocator = allocator<T> >
    class list;
}

第一个参数是任何数据类型,第二个参数可有可无。

list对象自带两个指针,一个指向第一个元素,另一个指向最后一个元素。因此list在如下几个主要方面与array、 vector 或deque不同:

1.list不支持随机访问,如果你要访问第5个元素,就得顺着链表逐一遍历前 4个元素。所以,在list中随机访问任意元素是很缓慢的。然而访问第一个和最后一个元素的速度很快。

2.任何位置(不只两端)插入和删除都非常快,在常量时间内完成,因为无须移动其他元素。只需要进行一些指针操作。

list提供的比较好用的函数:

1.list提供front,push_front,pop_front和back,push_back,pop_back等函数

2.由于不支持随机访问,list即不支持下标操作也不支持at函数。

3.list提供不少特殊成员函数,专门用于移动和删除元素,和同名的STL通用算法相比,这些函数执行起来更快速,优先使用list自己提供的算法。

定义及初始化

下表列出定义list对象的常用初始化方法。

#include <iostream>
#include <list>
using namespace std;

//输出链表元素的值
void Show(const list<int>& l)
{
    for (auto x : l)
        cout << x << " ";
    cout << endl;
}

int main()
{
    list<int> l1;         //空链表
    list<int> l2(2);      //包含两个默认元素值的链表
    list<int> l3(3, 4);    //包含3个4的链表
    list<int> l4{ 1,2,3,4 };//包含1,2,3,4的链表
    list<int> l5(l4);     //把l4的值赋值给l5

    cout << "l1:";  Show(l1);//输出l1的元素
    cout << "l2:";  Show(l2);//输出l2的元素
    cout << "l3:";  Show(l3);//输出l3的元素
    cout << "l4:";  Show(l4);//输出l4的元素
    cout << "l5:";  Show(l5);//输出l5的元素

    return 0;
}

list常用迭代器

list支持双向迭代器(不支持随机迭代器),所以STL中需要随机迭代器的算法不能用于list,例如sort。不过list自带sort算法。

list常用的迭代器如下:

例如,利用迭代器访问list对象。

#include <iostream>
#include <list>
using namespace std;

int main()
{
    list<int>l{ 1, 2, 3, 4, 5 };

    //从头到尾输出链表元素
    cout << "从头到尾输出:";
    for (list<int>::const_iterator it = l.cbegin(); it != l.cend(); ++it)
        cout << *it << " ";
    cout << endl;

    //把所有元素值+10
    for (auto it = l.begin(); it != l.end(); ++it)
        *it += 10;

    //从尾到头输出链表元素
    cout << "从尾到头输出:";
    for (auto it = l.crbegin(); it != l.crend(); ++it)
        cout << *it << " ";
    cout << endl;

    //auto it = l.begin() + 1;//错误,不是随机迭代器不能+1或-1,但可以++,--
    cout << "从尾到头输出(少一个):";
    for (auto it = --l.end(); it != l.begin(); --it)//这段代码不好,仅仅为了演示--
        cout << *it << " ";
    cout << endl;

    return 0;
}

请注意:

1.list迭代器不是随机迭代器,不能 + - 数字。例如上面。可以++,--。

2.list迭代器不能比较大小。可以==和!=。

list常用运算符

list类既支持常用的=,==,!=,< , >等运算符,也可以通过调用其成员函数来执行相应的操作。

下面列举了其常用的操作。

#include <iostream>
#include <list>
using namespace std;

//输出链表的所有数据
void Show(const list<int>& l)
{
	for (auto x : l)
		cout << x << " ";
	cout << endl;
}

int main()
{
	list<int> l1{ 1,2,3,4,5,6 };//创建包含1,2,3,4,5,6的链表
	list<int>l2;//创建空链表

	l2 = l1;//把l1的所有元素赋值给l2
	cout << "l1:"; Show(l1);//输出l1所有元素值
	cout << "l2:"; Show(l2);//输出l2所有元素值

	if (l1 == l2)
		cout << "l1 == l2" << endl;

	l1.front() = 100;//把l1的第一元素改为100,等同下一行的代码
	//int& x = l1.front(); x = 100;//把l1的第一元素改为100
	cout << "l1:"; Show(l1);//输出l1所有元素值
	cout << "l2:"; Show(l2);//输出l1所有元素值

	if (l1 != l2)
		cout << "l1 != l2" << endl;

	if (l1 > l2)
		cout << "l1 > l2" << endl;
	else
		cout << "l1 <= l2" << endl;

	return 0;
}

list常用成员函数

下面列举list对象常用的成员函数.

push_front(头部插入),push_back(尾部插入)

#include <iostream>
#include <list>
using namespace std;

//输出链表的所有数据
void Show(const list<int>& l)
{
	for (auto x : l)
		cout << x << " ";
	cout << endl;
}

int main()
{
	list<int> l1;//定义一个空的链表
	//头插1,2,3
	l1.push_front(1);
	l1.push_front(2);
	l1.push_front(3);
	//尾插10,20,30
	l1.push_back(10);
	l1.push_back(20);
	l1.push_back(30);

	cout << "l1:"; Show(l1);//输出l1的数据

	return 0;
}

pop_front(头部删除),pop_back(尾部删除)

#include <iostream>
#include <list>
using namespace std;

//输出链表的所有数据
void Show(const list<int>& l)
{
	for (auto x : l)
		cout << x << " ";
	cout << endl;
}

int main()
{
	list<int> l1;//定义一个空的链表
	//头插1,2,3
	l1.push_front(1);
	l1.push_front(2);
	l1.push_front(3);
	//尾插10,20,30
	l1.push_back(10);
	l1.push_back(20);
	l1.push_back(30);

	cout << "l1:"; 
	Show(l1);//输出l1的数据

	cout << "删除第一个元素:";
	l1.pop_front();//删除第一个元素
	Show(l1);
	cout << "删除最后一个元素:";
	l1.pop_back();//删除最后一个元素
	Show(l1);


	return 0;
}

insert成员函数

插入一个或多个元素。list插入元素时,不需要移动后面的数据,只需要修改几个指针,所以时间复杂度为常数。

#include <iostream>
#include <list>
using namespace std;

//输出链表的所有数据
void Show(const list<int>& l)
{
    for (auto x : l)
        cout << x << " ";
    cout << endl;
}

int main()
{
    list <int> l1{ 1, 2, 3 };//创建一个包含1,2,3的链表
    list <int> l2{ 4, 5, 6 };//创建一个包含4,5,6的链表
    list <int>::iterator Iter; //迭代器
    cout << "l1:"; Show(l1);
    cout << "l2:"; Show(l2);

    Iter = l1.begin();
    l1.insert(Iter, 100);//在开始位置插入数字100
    cout << "l1:"; Show(l1);

    Iter = l1.begin();
    Iter++;
    l1.insert(Iter, 2, 200);//在Iter位置插入2个100
    cout << "l1:"; Show(l1);

    //在l1的begin后面插入l2的所有元素,不包含l2的最后一个
    l1.insert(++l1.begin(), l2.begin(), --l2.end());//最后一个迭代器不包含
    cout << "l1:"; Show(l1);

    l2.insert(l2.begin(), { 10,20,30 });//插入多个元素
    cout << "l2:"; Show(l2);

    return 0;
}

list特有成员函数

下面列举list特有的函数(相比vector)

merge特有成员函数

把两个链表的元素进行合并。注意合并的前提是原来两个链表必须按照合并的方法有序。默认升序合并。

#include <iostream>
#include <list>
using namespace std;

//输出链表的所有数据
void Show(const list<int>& l)
{
    for (auto x : l)
        cout << x << " ";
    cout << endl;
}

int main()
{
    list <int> l1{ 3, 4, 6 };
    list <int> l2{ 2, 5, 7 };
    cout << "l1:"; Show(l1);
    cout << "l2:"; Show(l2);

    l2.merge(l1);  //把l1合并到l2中,默认升序合并,前提l1和l2必须升序有序
    cout << "合并l1后" << endl;
    cout << "l1:"; Show(l1);
    cout << "l2:"; Show(l2);
    cout << endl;

    list <int> l3{ 20, 15, 12,11 };
    list <int> l4{ 22, 15, 10 };
    cout << "l3:"; Show(l3);
    cout << "l4:"; Show(l4);

    l4.merge(l3, greater<int>());//按降序合并,前提l3,l4必须降序有序
    cout << "合并l3后" << endl;
    cout << "l3:"; Show(l3);
    cout << "l4:"; Show(l4);

    return 0;
}

合并的前提是原有的两个链表是"有序的"(按照排序的规则),如果不符合前提条件结果是未定义的。如下代码:

int main()
{
    list <int> l5{3, 4, 6};//升序
    list <int> l6{2, 20, 7};//非升序

    l5.merge(l6);  //把l6合并到l5中,程序错误

    return 0;
}

reverse特有成员函数

将所有的元素反序。

#include <iostream>
#include <list>
using namespace std;

//输出链表的所有数据
void Show(const list<int>& l)
{
    for (auto x : l)
        cout << x << " ";
    cout << endl;
}

int main()
{
    list <int> l1{ 1,2,3,4,5,6,7,8,9,10 };
    cout << "l1:"; Show(l1);

    l1.reverse();
    cout << "反序后" << endl;
    cout << "l1:"; Show(l1);

    return 0;
}

splice特有成员函数

链表连结,把一个链表的元素连结到另一个链表中。它和merge的区别是:merge要求数据必须有序,而splice对数据没有要求。

#include <iostream>
#include <list>
using namespace std;

//输出链表的所有数据
void Show(const list<int>& l)
{
    for (auto x : l)
        cout << x << " ";
    cout << endl;
}

int main()
{
    list<int> l1{ 10, 11 };
    list<int> l2{ 20, 21, 22 };
    list<int> l3{ 30, 31 };
    list<int> l4{ 40, 41, 42, 43 };

    list<int>::iterator where_iter;
    list<int>::iterator first_iter;
    list<int>::iterator last_iter;

    cout << "初始值:" << endl;
    cout << "l1:";    Show(l1);
    cout << "l2:";    Show(l2);
    cout << "l3:";    Show(l3);
    cout << "l4:";    Show(l4);

    //连结整个链表
    where_iter = l2.begin();
    ++where_iter; //在数据11位置处处理,这个迭代器在后面可以一直使用
    l2.splice(where_iter, l1);
    cout << "把l1连结到l2后" << endl;
    cout << "l1 = ";    Show(l1);
    cout << "l2 = ";    Show(l2);

    //连结一个元素
    first_iter = l3.begin();
    l2.splice(where_iter, l3, first_iter);
    cout << "把l3的第一个元素连结到l2后" << endl;
    cout << "l3:";    Show(l3);
    cout << "l2:";    Show(l2);

    //连结一个区间
    first_iter = l4.begin();
    last_iter = l4.end();
    ++first_iter; //第二个
    --last_iter; //倒数第二个
    l2.splice(where_iter, l4, first_iter, last_iter);
    cout << "把l4的区间数据连结到l2后:" << endl;
    cout << "l4 = ";    Show(l4);
    cout << "l2 = ";    Show(l2);

    return 0;
}

在上面的代码中where_iter这个迭代器在代码中可以多次使用,是因为在插入或者删除(没有删除它本身)操作中list的迭代器不会失效(在vector中插入和删除操作都可能会让迭代器失效)。

unique特有成员函数

删除相邻且重复的值,只保留一个。

#include <iostream>
#include <list>
using namespace std;

//输出链表的所有数据
void Show(const list<int>& l)
{
    for (auto x : l)
        cout << x << " ";
    cout << endl;
}

int main()
{
    list <int> l1{ 1,1,2,2,4,4,3,3,3,3,0 };
    list <int> l2{ -1,1,1,3,3,3,5,5,-1 };

    cout << "l1:"; Show(l1);
    cout << "l2:"; Show(l2);

    cout << "删除过相邻且相同的数字后" << endl;
    l1.unique();
    l2.unique();
    cout << "l1:"; Show(l1);
    cout << "l2:"; Show(l2);

    return 0;
}

注意:相同的值如果不相邻是不删除的。