C++的list容器

一、介绍

        C++的list容器又被称为链表,它是一种物理存储单元上非连续的存储结构,它的逻辑顺序是通过链表的指针来实现的。可以通过链表将数据转换为“链式存储”。

        list的迭代器iterator是一个双向迭代器不支持通过下标随机访问的方式。

二、数据结构

        链表的数据结构是有多个结点连接在一起的。每个节点中一定有一个节点指针类型的指针变量,它指向下一个结点。下图所示的就是一个简单的结点:

        我们可以将他们连接起来,组成一个链表,链表的最后一个节点的指针是NULL。一般链表分为带头节点和不带头结点的,头结点的Data部分是不存储数据的:

        这里带头结点的原因是:在遍历元素时,更加具有规律性。除了利用头结点进行分类,链表还分为单向链表双向链表,C++STL的list容器所采用的就是双向链表

        我们观察上图可以看到,其实最后一个节点的指针是NULL,如果让最后一个指针指向头部,那么链表就形成了循环链表

         从这里我们也可以观察出,由头结点的链表更利于元素遍历

        C++STL中的list链表容器是一个双向循环链表

三、list与vector比较

        C++的STL中,vector和list是最常用的两种容器。

  •  list优点
    • 采用动态分配,减少空间浪费
    • 链表执行插入和删除,只需要修改指针即可,不需要(像vector一样)移动大量元素
    • 链表的插入和删除不会造成迭代器失效,但vector的迭代器失效
  • list缺点
    • 链表的空间(多了指针域)和时间(元素遍历)消耗大

四、list的使用

        为了便于观察链表元素的值,我们编写printList()函数遍历打印元素信息:

//用于输出链表各个节点的Data值的函数
void printList(const list<int>& lst) {
	if (lst.empty()) {
		cout << "The list is EMPTY now." << endl;
		return;
	}

	cout << "[";
	for (list<int>::const_iterator it = lst.begin(); it != lst.end(); it++) {
		if (it != lst.begin()) {
			cout << ", ";
		} 
		cout << *it;
	}
	cout << "]" << endl;
}

4.1 list初始化

声明解释
list<T> lst初始化一个空链表 lst
list<T> lst(begin, end)将区间[begin, end)的所有元素初始化 lst
list<T> lst(int n, T elem)nelem初始化链表
list<T> lst(list<T> lst1)拷贝函数
list<int> lst0;//空链表
cout << "lst0 : "; printList(lst0);
list<int> lst1(6, 6);//用6个6初始化链表
cout << "lst1 : "; printList(lst1);
list<int> lst2(lst1.begin(), lst1.end());//区间初始化
cout << "lst2 : "; printList(lst2);
list<int> lst3(lst2);//拷贝函数
cout << "lst3 : "; printList(lst3);

        程序执行结果如下所示:

 4.2 list赋值

        list采用运算符=assign函数来实现赋值操作

声明解释
list& operator=(const list &list)

重载=运算符

(有关重载运算符,可以看之前写的《C++重载运算符》)

assign(begin, end)[begin(), end())区间赋值给自身
assign(int n, T elem)nelem赋值给自身
list<int> lst0;
cout << "lst0 : "; printList(lst0);

lst0.assign(6, 6);//将6个6赋值给链表
cout << "lst0 : "; printList(lst0);

list<int> lst1 = lst0;//等号运算符重载
cout << "lst1 : "; printList(lst1);

list<int> lst2;
lst0.push_front(1);
lst2.assign(lst0.begin(), lst0.end());//区间赋值
cout << "lst2 : "; printList(lst2);

        程序的执行结果如下:

4.3 list元素交换

声明解释
swap(const list &list)交换两个链表的全部元素
list<int> lst0(6, 6);
list<int> lst1(7, 7);
cout << "Before SWAP : " << endl;
cout << "lst0 : "; printList(lst0);
cout << "lst1 : "; printList(lst1);

lst0.swap(lst1);//SWAP

cout << "After SWAP : " << endl;
cout << "lst0 : "; printList(lst0);
cout << "lst1 : "; printList(lst1);

        程序的执行结果如下:

4.4 list容量与大小

声明解释
empty()判断deque是否为空(是否存在元素)
size()返回deque中结点的个数
resize(int num)

重新将deque的容量大小设置为num

1. 若容量长度变,则用默认值填充新位置

2. 若容量长度变,则超出容量长度的元素被删除

resize(int num, T elem)

重新将deque的容量大小设置为num

1. 若容量长度变,则用elem填充新位置

2. 若容量长度变,则超出容量长度的元素被删除

list<int> lst0(6, 6);

//判空
cout << "lst0.empty() : " << boolalpha << lst0.empty() << endl;
//获取结点个数
cout << "lst0.size() : " << lst0.size() << endl;

lst0.resize(8, 9);//容量变大,用9填充
cout << "lst0 : "; printList(lst0);

lst0.resize(4);//容量变小
cout << "lst0 : "; printList(lst0);

        程序的执行结果如下所示:

4.5 list插入元素

声明解释
push_back(Node node)在链表尾部插入一个结点node
push_front(Node node)在链表头部插入一个结点node
Node* insert(Node *pos, Node node)

pos位置插入一个结点node

(返回新插入元素的位置)

insert(Node *pos, int n, Node node)

pos位置插入n个结点node

(无返回值)

insert(Node *pos, begin, end)

pos位置插入[begin, end)区间的数据

(无返回值)

list<int> lst;
//为lst尾部插入0 1 2 3 4 5 6 7 8 9 
for (int i = 0; i < 10; i++) {
	lst.push_back(i);
}
cout << "lst : "; printList(lst);
//[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

//在lst头部插入996
lst.push_front(996);
cout << "lst : "; printList(lst);
//[996, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

//在第三个位置插入333
list<int>::iterator it = lst.begin();
it++; it++;
list<int>::iterator it_new = lst.insert(it, 333);
cout << "lst : "; printList(lst);
//[996, 0, 333, 1, 2, 3, 4, 5, 6, 7, 8, 9]
cout << "新插入元素为:" << *it_new << endl;//333

//在it_new位置插入3个888
lst.insert(it_new, 3, 888);
cout << "lst : "; printList(lst);
//[996, 0, 888, 888, 888, 333, 1, 2, 3, 4, 5, 6, 7, 8, 9]

//在it_new位置插入lst1的区间结点
list<int> lst1(2, 777);
lst.insert(it_new, lst1.begin(), lst1.end());
cout << "lst : "; printList(lst);
//[996, 0, 888, 888, 888,777, 777, 333, 1, 2, 3, 4, 5, 6, 7, 8, 9]

         程序执行结果如下所示:

4.6 list删除元素

声明解释
pop_back()删除链表中最后一个结点
pop_front()删除链表中第一个结点
clear()删除链表的全部结点
erase(begin, end)

删除[begin, end)区间的结点

(返回下一个结点的位置

erase(pos)

删除pos位置的结点

(返回下一个结点的位置

remove(Data data)删除链表中所有data值相匹配的结点
list<int> lst;
//为lst尾部插入0 1 2 3 4 5 6 7 8 9 
for (int i = 0; i < 10; i++) {
	lst.push_back(i);
}
cout << "lst : "; printList(lst);//[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

//删除最后一个结点
lst.pop_back();
cout << "lst : "; printList(lst);//[0, 1, 2, 3, 4, 5, 6, 7, 8]

//删除第一个结点
lst.pop_front();
cout << "lst : "; printList(lst);//[1, 2, 3, 4, 5, 6, 7, 8]

//删除[begin+2,end-2)区间的结点
list<int>::iterator itBegin = lst.begin();
itBegin++; itBegin++;
list<int>::iterator itEnd = lst.end();
itEnd--; itEnd--;
lst.erase(itBegin, itEnd);
cout << "lst : "; printList(lst);//[1, 2, 7, 8]

//删除begin+1位置的结点
itBegin = lst.begin();
itBegin++;
lst.erase(itBegin);
cout << "lst : "; printList(lst);//[1, 7, 8]

//在链表尾部添加值为7的结点
lst.push_back(7);
cout << "lst : "; printList(lst);//[1, 7, 8, 7]
//删除链表中值为7的结点
lst.remove(7);
cout << "lst : "; printList(lst);//[1, 8]

         程序执行结果如下所示:

4.7 list数据获取

声明解释

front()

返回链表中最后一个结点
back()返回链表中第一个结点
list<int> lst;
//为lst尾部插入0 1 2 3 4 5 6 7 8 9 
for (int i = 0; i < 10; i++) {
	lst.push_back(i);
}
cout << "lst : "; printList(lst);//[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

cout << lst.front() << endl;//0
cout << lst.back() << endl;//9

4.8 list反转

声明解释

reverse()

将链表中的全部元素反转
list<int> lst;
//为lst尾部插入0 1 2 3 4 5 6 7 8 9 
for (int i = 0; i < 10; i++) {
	lst.push_back(i);
}

cout << "Before reverse : " << endl;
cout << "lst : "; printList(lst);//[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

lst.reverse();

cout << "After reverse : " << endl;
cout << "lst : "; printList(lst);//[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]

         程序直接结果如下所示:

4.9 list排序

        在包含头文件#include<algorithm>可以使用sort函数对链表元素进行升序排序

4.9.1 标准类型的list

声明解释

sort()

将链表中的全部元素按升序排序
list<int> lst;
//为lst尾部插入0 1 2 3 4 5 6 7 8 9 
for (int i = 9; i >= 0; i--) {
	lst.push_back(i);
}

cout << "Before sort : " << endl;
cout << "lst : "; printList(lst);//[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]

lst.sort();

cout << "After sort : " << endl;
cout << "lst : "; printList(lst);//[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

        程序运行结果如下:

 4.9.2 自定义类型的list

        如果是自定义的类型,比如现在有一个自定义类Person,里边有五花八门的元素,现在

list<Person> p;
p.sort();

 执行以上代码,程序就会报错,因为编译器根本不知道两个Person对象比较大小的规则是什么。

        因此我们需要告知sort函数,我们的比较规则,此时需要我们自己写一个compare函数:

                        bool compare(Person &p1, Person &p2);

        然后将这个函数名称,也就是compare作为参数传给sort函数:

                        p.sort(compare);

        可以结合我之前写的小例子更直观的理解:《C++ 自定义类型list的排序案例》

  • 36
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值