作用:链表
,链式存储数据。STL中的链表是双向循环链表。
链表:链表list
是物理存储单元上非连续的存储结构,数据元素的逻辑顺序通过链表中的指针链接实现。链表由一系列节点组成。
节点的组成:
①数据域:存储数据元素;
②指针域:存储下一个节点的地址(指针指向下一个节点)。
list容器的迭代器:
链表采用不连续的存储结构,故链表list
的迭代器只支持前移和后移(不支持跳跃式访问),属于双向迭代器。
注:
list
容器插入或删除元素时,不会导致原有迭代器失效;
vector
容器插入或删除元素时,会导致原有迭代器失效。
list容器的数据结构:
STL中的list
容器,采用双向循环链表的数据结构:
第1个节点的前继节点为最后1个节点(第1个节点的prev指针
指向最后1个节点);
最后1个节点的后继节点为第1个节点(最后1个节点的next指针
指向第1个节点)。
push_front()
:头插法插入节点。
pop_front()
:头删法删除节点。
push_back()
:尾插法插入节点。
pop_back()
:尾删法删除节点。
front()
:获取list容器的第1个节点。
back()
:获取list容器的最后1个节点。
begin()
:获取起始迭代器,指向容器中的第1个节点。
end()
:获取结束迭代器,指向容器中的最后1个节点的下一个位置。
list的优缺点:
优点:
①采用动态存储分配,不会造成内存浪费或溢出;
②链表,可在任意位置插入或删除元素,只需修改指针指向,无需移动大量元素。
缺点:
①链表包括数据域和指针域,占用内存空间较大;
②元素访问/遍历速度较慢。
注1:
链表
增删快、查询慢;数组
增删慢、查询快。
注2:list
和vector
是C++ STL中最常用的容器,各有优缺点。
作用:创建list容器。
注:使用list容器时,需包含头文件
#include
。
函数原型:
(1)list
:默认无参构造函数,采用类模板实现。
(2)list(lst.begin(), lst.end());
:拷贝容器对象lst
的[begin(), end())
区间(左闭右开,包左不包右)的元素进行初始化。
(3)list(n, elem);
:有参构造函数,使用n
个elem元素
进行初始化。
(4)list(const list&lst);
:拷贝构造函数,使用已有list对象初始化新的对象。
示例:list构造函数
#include
using namespace std;
#include
//函数模板:遍历list容器的通用函数
template<typename T>
void printList(const list<T> &lst) { //形参使用const,避免被修改
//形参使用const后,遍历时需使用只读迭代器const_iterator
//使用typename关键字,防止编译器报错:C2760(语法错误,意外标记“标识符”)
for (typename list<T>::const_iterator it = lst.begin(); it != lst.end(); it++) {
cout << *it << " ";
}
cout << endl;
}
int main() {
//1.无参构造函数
list<int> lst;
lst.push_back(5);
lst.push_back(7);
lst.push_back(9);
lst.push_front(3);
lst.push_front(1);
printList<int>(lst); //1 3 5 7 9
//2.区间构造
list<int> l1(lst.begin(), lst.end());
printList<int>(l1); //1 3 5 7 9
//3.拷贝构造
list<int> l2(l1);
printList<int>(l2); //1 3 5 7 9
//4.n个elem元素构造
list<int> l3(5, 6);
printList<int>(l3); //6 6 6 6 6
return 0;
}
赋值操作:通过重载赋值运算符operator=
和成员函数assign()
,对list容器进行赋值。
函数原型:
(1)list& operator=(const list &lst);
:重载赋值运算符,使用目标list容器,对当前list容器赋值。
(2)assign(begin, end);
:拷贝目标list容器中[begin(), end())
区间(左闭右开,包左不包右)的元素,对当前list容器赋值。
(3)assign(n, elem);
:使用n
个elem元素
,对当前list容器赋值。
交换操作:实现list容器的交换。
swap(lst);
:将目标list容器lst
与自身的元素互换。
示例:list赋值与交换
#include
using namespace std;
#include
//函数模板:遍历list容器的通用函数
template<typename T>
void printList(const list<T>& lst) { //形参使用const,避免被修改
//形参使用const后,遍历时需使用只读迭代器const_iterator
//使用typename关键字,防止编译器报错:C2760(语法错误,意外标记“标识符”)
for (typename list<T>::const_iterator it = lst.begin(); it != lst.end(); it++) {
cout << *it << " ";
}
cout << endl;
}
int main() {
//无参构造函数
list<int> lst;
lst.push_back(5);
lst.push_back(7);
lst.push_back(9);
lst.push_front(3);
lst.push_front(1);
printList<int>(lst); //1 3 5 7 9
//1.重载赋值运算符
list<int> l1;
l1 = lst;
printList<int>(l1); //1 3 5 7 9
//2.assign()赋值
list<int> l2;
l2.assign(lst.begin(), lst.end());
printList<int>(l2); //1 3 5 7 9
//3.n个elem元素:assign(n, elem)
list<int> l3;
l3.assign(3, 6);
printList<int>(l3); //6 6 6
/* 容器交换 */
cout << "交换前l2:" << endl;
printList<int>(l2); //1 3 5 7 9
cout << "交换前l3:" << endl;
printList<int>(l3); //6 6 6
l3.swap(l2);
cout << "交换后l2:" << endl;
printList<int>(l2); //6 6 6
cout << "交换后l3:" << endl;
printList<int>(l3); //1 3 5 7 9
return 0;
}
作用:操作list容器的大小(即元素个数)。
函数原型:
(1)empty();
:判断容器是否为空。
(2)size();
:获取容器的大小,即元素个数。
(3)resize(int num);
:重新指定容器的长度为num
。
若容器变长,则以默认值0
填充新位置;若容器变短,则容器末尾超出新长度的元素被删除。
(4)resize(int num, elem);
:重新指定容器的长度为num
。
若容器变长,则以指定值elem
填充新位置;若容器变短,则容器末尾超出新长度的元素被删除。
注:list容器不存在容量的概念,即不存在
capacity()
成员函数。
list支持动态存储分配,通过维护指针域允许随时插入或删除节点。
示例:
#include
using namespace std;
#include
//函数模板:遍历list容器的通用函数
template<typename T>
void printList(const list<T>& lst) { //形参使用const,避免被修改
//形参使用const后,遍历时需使用只读迭代器const_iterator
//使用typename关键字,防止编译器报错:C2760(语法错误,意外标记“标识符”)
for (typename list<T>::const_iterator it = lst.begin(); it != lst.end(); it++) {
cout << *it << " ";
}
cout << endl;
}
int main() {
//无参构造函数
list<int> lst;
lst.push_back(5);
lst.push_back(7);
lst.push_back(9);
lst.push_front(3);
lst.push_front(1);
printList<int>(lst); //1 3 5 7 9
//size():容器大小
cout << "容器大小:" << lst.size() << endl; //5
//empty():判断容器是否为空
cout << (lst.empty() ? "空" : "非空") << endl; //非空
//resize(int num);:重新指定容器的长度为num。
//若容器变长,则以默认值0填充新位置;若容器变短,则容器末尾超出新长度的元素被删除。
lst.resize(10);
printList<int>(lst); //1 3 5 7 9 0 0 0 0 0
lst.resize(3);
printList<int>(lst); //1 3 5
//resize(int num, elem); :重新指定容器的长度为num。
//若容器变长,则以指定值elem填充新位置;若容器变短,则容器末尾超出新长度的元素被删除。
lst.resize(8, 6);
printList<int>(lst); //1 3 5 6 6 6 6 6
return 0;
}
(1)list容器插入元素:使用成员函数push_front(..)
、push_back(..)
、insert(..)
向list容器插入元素。
函数原型:
①push_front(elem);
:头插法,向list容器的头部插入元素elem
。
②push_back(elem);
:尾插法,向list容器的尾部插入元素elem
。
③insert(const_iterator pos, elem);
:迭代器指向位置pos
插入元素elem
④insert(const_iterator pos, int n, elem);
:迭代器指向位置pos
插入n
个元素elem
。
⑤insert(const_iterator pos, const_iterator begin, const_iterator end);
:迭代器指向位置pos
插入[begin(), end())
区间(左闭右开,包左不包右)的元素,无返回值。
(2)list容器删除元素:使用成员函数pop_front()
、pop_back(..)
、erase(..)
、clear(..)
删除list容器中的元素。
函数原型:
①pop_front()
:头删法,删除list容器头部的第1个元素。
②pop_back();
:尾删法,删除list容器尾部的最后1个元素。
③erase(const_iterator pos);
:删除迭代器指向位置的元素。
④erase(const_iterator start, const_iterator end);
:删除迭代器指向位置[start, end)
区间的所有元素。
⑤clear();
:清空容器中的所有元素。
⑥remove(elem);
:删除容器中与指定值elem
匹配的所有元素。
注1:清空容器的所有元素,
lst.clear();
等价于lst.erase(lst.begin(), lst.end());
。
注2:插入元素insert(..)
、删除元素erase(..)
均需要迭代器对象作为参数。
示例:list容器插入与删除元素
#include
using namespace std;
#include
//函数模板:遍历list容器的通用函数
template<typename T>
void printList(const list<T>& lst) { //形参使用const,避免被修改
//形参使用const后,遍历时需使用只读迭代器const_iterator
//使用typename关键字,防止编译器报错:C2760(语法错误,意外标记“标识符”)
for (typename list<T>::const_iterator it = lst.begin(); it != lst.end(); it++) {
cout << *it << " ";
}
cout << endl;
}
int main() {
list<int> lst;
//尾插
lst.push_back(5);
lst.push_back(7);
lst.push_back(9);
//头插
lst.push_front(3);
lst.push_front(1);
printList<int>(lst); //1 3 5 7 9
//尾删
lst.pop_back();
printList<int>(lst); //1 3 5 7
//头删
lst.pop_front();
printList<int>(lst); //3 5 7
//insert()-插入单个元素
//在第0个与第1个索引位置插入元素4
lst.insert(++lst.begin(), 4);
printList<int>(lst); //3 4 5 7
//erase()-删除单个元素
//删除第1个索引位置的元素
lst.erase(++lst.begin());
printList<int>(lst); //3 5 7
//insert()-插入多个元素
//在第0个索引位置前插入3个1
lst.insert(lst.begin(), 3 , 0);
printList<int>(lst); //0 0 0 3 5 7
list<int> l1;
//insert()-插入区间元素
l1.insert(l1.begin(), lst.begin(), lst.end());
printList<int>(l1); //0 0 0 3 5 7
//remove()-删除指定值的元素
l1.remove(0);
printList<int>(l1); //3 5 7
//erase()-删除区间元素
l1.erase(l1.begin(), l1.end());
printList<int>(l1); //(空)
//remove()-清空元素
lst.clear();
printList<int>(lst); //(空)
return 0;
}
作用:对list容器中数据进行存取。
注1:链表底层采用非连续线性的内存空间,且仅支持双向迭代器(只能前移和后移),无法使用
索引
跳跃式地访问元素。故不支持重载赋值运算符operator[]
和成员函数at(int index)
跳跃式地访问指定索引位置元素。
注2:list容器支持双向迭代器,允许前移it--;
和后移it++;
;
list容器不支持随机访问迭代器,不允许跳跃式移动it += 1;
或it -= 2;
,否则编译器报错:没有与操作数匹配的 += 或 -= 运算符
。
函数原型:
front();
:返回容器的第1个元素。
back();
:返回容器的最后1个元素。
示例:
#include
using namespace std;
#include
//函数模板:遍历list容器的通用函数
template<typename T>
void printList(const list<T>& lst) { //形参使用const,避免被修改
//形参使用const后,遍历时需使用只读迭代器const_iterator
//使用typename关键字,防止编译器报错:C2760(语法错误,意外标记“标识符”)
for (typename list<T>::const_iterator it = lst.begin(); it != lst.end(); it++) {
cout << *it << " ";
}
cout << endl;
}
int main() {
list<int> lst;
//尾插
lst.push_back(1);
lst.push_back(3);
lst.push_back(5);
lst.push_back(7);
lst.push_back(9);
printList<int>(lst); //1 3 5 7 9
cout << "第1个元素:" << lst.front() << endl; //1
cout << "最后1个元素:" << lst.back() << endl; //9
//修改元素
lst.front() = 0;
lst.back() = 100;
printList<int>(lst); //0 3 5 7 100
//list容器支持双向迭代器,允许前移和后移
list<int>::iterator it = lst.begin();
it++; //允许后移
it--; //允许前移
//list容器不支持随机访问迭代器,不允许跳跃式移动
//it += 1; //报错:没有与操作数匹配的 += 运算符
//it += 3; //报错:没有与操作数匹配的 += 运算符
//it -= 1; //报错:没有与操作数匹配的 -= 运算符
//it -= 3; //报错:没有与操作数匹配的 -= 运算符
return 0;
}
(1)list反转
reverse();
:将容器中的元素反转。
(2)list排序
sort();
:将容器中的数据进行排序,默认升序排序。
注1:链表排序的
sort()
函数是list容器的成员函数,而不是标准算法头文件的全局函数。
注2:可通过回调函数或仿函数实现sort()
函数按自定义规则排序。
回调函数-实现自定义排序:
//回调函数
bool myCompare(int val1, int val2){
//return val1 < val2; //升序排序
return val1 > val2; //降序排序
}
//使用回调函数,按自定义规则排序
lst.sort(myCompare);
仿函数/函数对象-实现自定义排序:
//函数对象/仿函数
class MyCompare {
public:
//重载函数调用运算符()
bool operator()(int val1, int val2) {
//return val1 < val2; //升序排序
return val1 > val2; //降序排序
}
};
//使用仿函数/函数对象,按自定义规则排序
//MyCompare mc;
//lst.sort(mc); //函数对象mc
lst.sort(MyCompare()); //匿名函数对象MyCompare()
示例:使用回调函数和仿函数,实现内置数据类型排序
#include
using namespace std;
#include
//函数模板:遍历list容器的通用函数
template<typename T>
void printList(const list<T>& lst) { //形参使用const,避免被修改
//形参使用const后,遍历时需使用只读迭代器const_iterator
//使用typename关键字,防止编译器报错:C2760(语法错误,意外标记“标识符”)
for (typename list<T>::const_iterator it = lst.begin(); it != lst.end(); it++) {
cout << *it << " ";
}
cout << endl;
}
//反转
void func1() {
list<int> lst;
//尾插
lst.push_back(5);
lst.push_back(8);
lst.push_back(3);
lst.push_back(6);
lst.push_back(1);
cout << "反转前:" << endl;
printList<int>(lst); //5 8 3 6 1
//反转
lst.reverse();
cout << "反转后:" << endl;
printList<int>(lst); //1 6 3 8 5
}
//回调函数-自定义排序规则
bool myCompare(int val1, int val2) {
//return val1 < val2; //升序排序
return val1 > val2; //降序排序
}
//函数对象/仿函数-自定义排序规则
class MyCompare {
public:
//重载函数调用运算符()
bool operator()(int val1, int val2) {
//return val1 < val2; //升序排序
return val1 > val2; //降序排序
}
};
//排序
void func2() {
list<int> lst;
//尾插
lst.push_back(5);
lst.push_back(8);
lst.push_back(3);
lst.push_back(6);
lst.push_back(1);
cout << "排序前:" << endl;
printList<int>(lst); //5 8 3 6 1
//默认升序排序
lst.sort();
cout << "升序排序后:" << endl;
printList<int>(lst); //1 3 5 6 8
//降序排序:使用回调函数
//lst.sort(myCompare);
//降序排序:使用仿函数/函数对象
//MyCompare mc;
//lst.sort(mc); //函数对象
lst.sort(MyCompare()); //匿名函数对象
cout << "降序排序后:" << endl;
printList<int>(lst); //8 6 5 3 1
}
int main() {
//func1();
func2();
return 0;
}
注1:对于自定义数据类型,必须指定排序规则,否则编译器不清楚如何排序。
注2:高级排序是在排序规则的基础上额外制定一次或多次逻辑规则。
案例描述:
对自定义数据类型Person进行排序,Person类包括姓名、成绩、年龄等成员属性。
排序规则:先按成绩降序排序,再按年龄升序排序。
示例:自定义数据类型Person的排序
#include
using namespace std;
#include
class Person {
public:
string name;
int score;
int age;
Person(string name, int score, int age) {
this->name = name;
this->score = score;
this->age = age;
}
};
//排序规则的回调函数
bool myCompare1(Person p1, Person p2) {
//先按照成绩socre降序排序
if (p1.score == p2.score) {
//成绩socre相同时,再按年龄age升序排序
return p1.age < p2.age;
}
else {
return p1.score > p2.score;
}
}
//排序规则的回调函数:使用三目运算符简化
bool myCompare2(Person p1, Person p2) {
//成绩不等时,按成绩降序排序
//成绩相等时,按年龄升序排序
return (p1.score == p2.score) ? (p1.age < p2.age) : (p1.score > p2.score);
}
int main() {
list<Person> personList;
//创建Person对象
Person p1("普通青年", 60, 20);
Person p2("聪明青年", 100, 20);
Person p3("油腻中年", 60, 40);
Person p4("勤奋青年", 80, 20);
Person p5("摸鱼青年", 60, 30);
Person p6("天才少年", 100, 15);
//向list容器添加Person对象
personList.push_back(p1);
personList.push_back(p2);
personList.push_back(p3);
personList.push_back(p4);
personList.push_back(p5);
personList.push_back(p6);
for (list<Person>::iterator it = personList.begin(); it != personList.end(); it++) {
cout << "姓名:" << (*it).name << ", "
<< "成绩:" << it->score << ", "
<< "年龄:" << it->age << endl;
}
cout << "----------排序后-----------" << endl;
//自定义排序:调用回调函数
//personList.sort(myCompare1);
personList.sort(myCompare2);
for (list<Person>::iterator it = personList.begin(); it != personList.end(); it++) {
cout << "姓名:" << (*it).name << ","
<< "成绩:" << it->score << ","
<< "年龄:" << it->age << endl;
}
}
输出结果:
姓名:普通青年, 成绩:60, 年龄:20
姓名:聪明青年, 成绩:100, 年龄:20
姓名:油腻中年, 成绩:60, 年龄:40
姓名:勤奋青年, 成绩:80, 年龄:20
姓名:摸鱼青年, 成绩:60, 年龄:30
姓名:天才少年, 成绩:100, 年龄:15
----------排序后-----------
姓名:天才少年,成绩:100,年龄:15
姓名:聪明青年,成绩:100,年龄:20
姓名:勤奋青年,成绩:80,年龄:20
姓名:普通青年,成绩:60,年龄:20
姓名:摸鱼青年,成绩:60,年龄:30
姓名:油腻中年,成绩:60,年龄:40