目录
一、基本概念
1、功能
将数据进行链式存储
2、链表
- 链表是一种物理存储单元上非连续的存储结构,数据元素的逻辑顺序是通过链表中的指针链接实现的
- 链表的组成:由一系列的结点组成
- 结点的组成:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域
- 链表优点:在任意位置插入删除效率高 ,时间复杂度O(1);按需申请释放空间
- 链表缺点:不支持随机访问(用下标访问), 意味着: 一些排序 , 二分查找等在这种结构上不适用;链表存储一个值, 同时要存储链接指针, 也有一定的消耗
双向循环链表:
3、基本结构
- STL中的链表是一个双向循环链表
- 由于链表的存储方式不是连续的存储空间,所以链表list中的迭代器只支持前移和后移,属于双向迭代器
4、list优缺点
list优点:
- 采用动态存储分配,不会造成内存浪费和溢出
- 链表执行插入和删除操作十分方便,修改指针即可,不需要大量移动元素
list缺点:
- 链表灵活,但是空间(指针域)和时间(遍历)额外消耗大
list在进行插入和删除操作都不会造成原有list迭代器失效,这在vector是不成立的
二、构造函数
1、函数
list构造方式和其他几个STL常用容器基本相同
list<T> lst; //采用模板实现类实现,默认构造函数
list(beg,end); //将[beg,end]区间中的元素拷贝给本身
list(n,elem); //构造函数将n个elem拷贝给本身
list(const list &lst); //拷贝构造函数
2、测试
#include<list>
//list构造函数
void printList(list<int>& L) {
for (list<int>::iterator it = L.begin(); it != L.end(); it++) {
cout << *it << " ";
}
cout << endl;
}
void Test01() {
list<int> L1;
L1.push_back(10);
L1.push_back(20);
L1.push_back(30);
L1.push_back(40);
printList(L1);//10 20 30 40
//通过区间方式
list<int> L2(L1.begin(),L1.end());
printList(L2);//10 20 30 40
//拷贝构造
list<int> L3(L2);
printList(L3);//10 20 30 40
list<int> L4(4,1000);
printList(L4);//1000 1000 1000 1000
}
int main() {
Test01();
return 0;
}
三、赋值和交换
1、函数
assign(beg,end); //将[beg,end]区间中的数据拷贝赋值给本身
assign(n,elem); //将n个elem拷贝赋值给本身
list& operator=(const list &lst); //重载等号运算符
swap(lst); //将lst与本身元素互换
2、测试
#include<list>
//list赋值和交换
void printList(const list<int>& L) {
for (list<int>::const_iterator it = L.begin(); it != L.end(); it++) {
cout << *it << " ";
}
cout << endl;
}
//赋值
void Test02() {
list<int> L1;
L1.push_back(10);
L1.push_back(20);
L1.push_back(30);
L1.push_back(40);
printList(L1);//10 20 30 40
list<int> L2;
L2 = L1;
printList(L2);//10 20 30 40
list<int> L3;
L3.assign(L2.begin(),L2.end());
printList(L3);//10 20 30 40
list<int> L4(4, 100);
printList(L4);//100 100 100 100
}
//交换
void Test03() {
list<int> L1;
L1.push_back(10);
L1.push_back(20);
L1.push_back(30);
L1.push_back(40);
list<int> L2(4,100);
cout << "交换前:" << endl;
printList(L1);//10 20 30 40
printList(L2);100 100 100 100
L1.swap(L2);
cout << "交换后:" << endl;
printList(L1);//100 100 100 100
printList(L2);//10 20 30 40
}
int main() {
Test02();
Test03();
return 0;
}
四、大小操作
1、函数
empty(); //判断容器是否为空
size(); //返回容器中元素的个数
resize(int num);//重新指定容器的长度为num,若容器变长,则以默认值填充新位置;若容器变短,则末尾超出容器长度的元素被删除
resize(int num,elem);// 重新指定容器的长度为num,若容器变长,则以elem填充新位置;若容器变短,则末尾超出容器长度的元素被删除
2、测试
#include<list>
//list大小操作
void printList(const list<int>& L) {
for (list<int>::const_iterator it = L.begin(); it != L.end(); it++) {
cout << *it << " ";
}
cout << endl;
}
void Test04() {
list<int> L1;
L1.push_back(10);
L1.push_back(20);
L1.push_back(30);
L1.push_back(40);
printList(L1);//10 20 30 40
if (L1.empty()) {
cout << "L1为空" << endl;
}
else {
cout << "L1不为空" << endl;
cout <<"L1的元素个数:" << L1.size() << endl;
}
//重新指定大小
L1.resize(10);
printList(L1);//10 20 30 40 0 0 0 0 0 0
L1.resize(15,100);
printList(L1);//10 20 30 40 0 0 0 0 0 0 100 100 100 100 100
L1.resize(2);
printList(L1);//10 20
}
int main() {
Test04();
return 0;
}
五、插入和删除
1、函数:
pos,bdg,end均为迭代器指向位置
两端插入删除
- push_back(elem); //在容器尾部插入一个元素
- pop_back(elem); //删除容器中最后一个元素
- push_front(); //在容器头部插入一个元素
- pop_front(); //删除容器中第一个元素
任意位置插入删除
- insert(pos,elem); //在pos位置插入elem的拷贝,返回新数据的位置
- insert(pos,n,elem); //在pos位置插入n个elem数据,无返回值
- insert(pos,beg,end); //在pos位置插入[beg,end]区间的数据,无返回值
- erase(beg,end); //删除[beg,end]区间的数据,返回下一个数据的位置
- erase(pos); //删除pos位置的数据,返回下一个数据的位置
clear(); //移除容器的所有数据
remove(elem); //删除容器中所有与elem值匹配的数据
2、测试
#include<list>
//list插入和删除
void printList(const list<int>& L) {
for (list<int>::const_iterator it = L.begin(); it != L.end(); it++) {
cout << *it << " ";
}
cout << endl;
}
void Test05() {
list<int> L1;
//尾插
L1.push_back(10);
L1.push_back(20);
L1.push_back(30);
//头插
L1.push_front(100);
L1.push_front(200);
L1.push_front(300);
printList(L1);//300 200 100 10 20 30
//尾删
L1.pop_back();
printList(L1);//300 200 100 10 20
//头删
L1.pop_front();
printList(L1);//200 100 10 20
//insert插入
//L1.insert(L1.begin(), 1000);
//printList(L1);//1000 200 100 10 20
list<int>::iterator it = L1.begin();
it++;
L1.insert(it, 1000);//参数迭代器是可以移动的
printList(L1);//200 1000 100 10 20
//删除
it = L1.begin();
L1.erase(++it);
printList(L1);//200 100 10 20
//移除
L1.push_back(10000);
printList(L1);//200 100 10 20 10000
L1.remove(10000);
printList(L1);//200 100 10 20
/*如果list中有多个elem值,那么执行完remove(elem)之后,所有的elem都会被删除*/
L1.push_back(10000);
L1.push_back(10000);
L1.push_back(10000);
printList(L1);//200 100 10 20 10000 10000 10000
L1.remove(10000);
printList(L1);//200 100 10 20
//清空
L1.clear();
printList(L1);
}
int main() {
Test05();
return 0;
}
六、数据存取
1、函数
因为list本质上是一个链表,不是用连续的存储空间存储数据,因此不能用[]或at方式访问数据
front(); //返回第一个元素
back(); //返回最后一个元素
验证list迭代器是不支持随机访问的
list<int>::iterator it = L1.begin();
//支持双向,++和--都可
it++;//it=it+1;/it=it+2;都是错误写法,不支持随机访问
it--;//it=it-1;/it=it-2;都是错误写法,不支持随机访问
2、测试
#include<list>
//list数据存取
void printList(const list<int>& L) {
for (list<int>::const_iterator it = L.begin(); it != L.end(); it++) {
cout << *it << " ";
}
cout << endl;
}
void Test06() {
list<int> L1;
L1.push_back(10);
L1.push_back(20);
L1.push_back(30);
L1.push_back(40);
printList(L1);
cout << "第一个元素为:" << L1.front()<<endl;
cout << "最后一个元素为:" << L1.back() << endl;
//验证迭代器是不支持随机访问的
list<int>::iterator it = L1.begin();
//支持双向,++和--都可
it++;//it=it+1;/it=it+2;都是错误写法,不支持随机访问
it--;//it=it-1;/it=it-2;都是错误写法,不支持随机访问
}
int main() {
Test06();
return 0;
}
七、反转和排序
1、函数
reverse(); //反转链表
sort(); //链表排序,默认为从小到大升序,也可指定为降序
sort()是一个成员函数
2、测试
#include<list>
//list反转和排序
void printList(const list<int>& L) {
for (list<int>::const_iterator it = L.begin(); it != L.end(); it++) {
cout << *it << " ";
}
cout << endl;
}
bool myCompare(int v1,int v2) {
//降序 让第一个数>第二个数
return v1 > v2;
}
void Test07() {
list<int> L1;
L1.push_back(20);
L1.push_back(10);
L1.push_back(50);
L1.push_back(40);
L1.push_back(30);
cout << "反转前:";
printList(L1);//20 10 50 40 30
//反转
L1.reverse();
cout << "反转后:";
printList(L1);//30 40 50 10 20
//排序 默认为升序
L1.sort();
cout << "排序后:";
printList(L1);//排序后:10 20 30 40 50
//所有不支持随机访问的迭代器的容器,不可以使用标准算法,它的内部会提供对应一些算法
//sort(L1.begin(),L1.end());不可以这样写,会报错的,因为list是不支持随机访问迭代器的容器
//指定为降序
L1.sort(myCompare);
printList(L1);//50 40 30 20 10
}
int main() {
Test07();
return 0;
}
所有不支持随机访问的迭代器的容器,不可以使用标准算法,它的内部会提供对应一些算法
八、排序案例
测试
将Person自定义数据类型进行排序,Person中属性有姓名、年龄、身高;
排序规则:按照年龄升序,如果年龄相同则按照身高进行降序
#include<list>
#include<string>
//list排序案例--对自定义数据类型进行排序
//排序规则:按照年龄升序,如果年龄相同则按照身高进行降序
class Person {
public:
string m_Name;//姓名
int m_Age;//年龄
int m_Height;//身高
Person(string name, int age, int height) {
this -> m_Name = name;
this->m_Age = age;
this->m_Height = height;
}
};
//指定排序规则:按照年龄升序,如果年龄相同则按照身高进行降序
bool ComparePerson(Person &p1, Person& p2) {
if (p1.m_Age == p2.m_Age) {//年龄相同则按照身高进行降序
return p1.m_Height > p2.m_Height;
}
else {//年龄升序
return p1.m_Age < p2.m_Age;
}
}
void Test08(){
list<Person> L;
//准备数据
Person p1("刘晨",19,167);
Person p2("王敏",20,194);
Person p3("张立",21,185);
Person p4("李四",19,193);
Person p5("张三",19,175);
Person p6("王五",25,177);
//插入数据
L.push_back(p1);
L.push_back(p2);
L.push_back(p3);
L.push_back(p4);
L.push_back(p5);
L.push_back(p6);
for (list<Person>::iterator it = L.begin(); it != L.end(); it++) {
cout << "姓名:" << (*it).m_Name << "年龄:" << (*it).m_Age << "身高:" << (*it).m_Height << endl;
}
//排序
cout << "排序后:" << endl;
L.sort(ComparePerson);
for (list<Person>::iterator it = L.begin(); it != L.end(); it++) {
cout << "姓名:" << (*it).m_Name << "年龄:" << (*it).m_Age << "身高:" << (*it).m_Height << endl;
}
}
int main() {
Test08();
return 0;
}
- 对于自定义数据类型,必须要指明排序规则,否则编译器不知道如何排序
- 高级排序只是在排序规则上再进行一次逻辑规则的制定,其实并不复杂
排序规则:按照年龄升序,如果年龄相同则按照身高进行降序 ,身高相同再按体重降序
#include<list>
#include<string>
//list排序案例--对自定义数据类型进行排序
//排序规则:按照年龄升序,如果年龄相同则按照身高进行降序 ,身高相同再按体重降序
class Person {
public:
string m_Name;//姓名
int m_Age;//年龄
int m_Height;//身高
int m_weight;//体重
Person(string name, int age, int height,int weight) {
this->m_Name = name;
this->m_Age = age;
this->m_Height = height;
this->m_weight = weight;
}
};
//指定排序规则:按照年龄升序,如果年龄相同则按照身高进行降序 ,身高相同再按体重降序
bool ComparePerson(Person& p1, Person& p2) {
if (p1.m_Age == p2.m_Age) {//年龄相同则按照身高进行降序
if (p1.m_Height == p2.m_Height) {
return p1.m_weight > p2.m_weight;
}
else {
return p1.m_Height > p2.m_Height;
}
}
else {//年龄升序
return p1.m_Age < p2.m_Age;
}
}
void Test09() {
list<Person> L;
//准备数据
Person p1("刘晨", 19, 193,70);
Person p2("王敏", 20, 194,71);
Person p3("张立", 21, 185,88);
Person p4("李四", 19, 193,78);
Person p5("张三", 19, 175,66);
Person p6("王五", 25, 177,64);
//插入数据
L.push_back(p1);
L.push_back(p2);
L.push_back(p3);
L.push_back(p4);
L.push_back(p5);
L.push_back(p6);
for (list<Person>::iterator it = L.begin(); it != L.end(); it++) {
cout << "姓名:" << (*it).m_Name << "年龄:" << (*it).m_Age << "身高:" << (*it).m_Height <<"体重:"<<(*it).m_weight << endl;
}
//排序
cout << "排序后:" << endl;
L.sort(ComparePerson);
for (list<Person>::iterator it = L.begin(); it != L.end(); it++) {
cout << "姓名:" << (*it).m_Name << "年龄:" << (*it).m_Age << "身高:" << (*it).m_Height << "体重:" << (*it).m_weight << endl;
}
}
int main() {
Test09();
return 0;
}