列表(list)
一、前言
本次博客主要讲解 list 容器的基本操作、常用接口做一个系统的整理,结合具体案例熟悉自定义内部排序方法的使用。如有任何错误,欢迎在评论区指出,我会积极改正。
二、什么是list
list是C++的一个序列容器,插入和删除元素的效率较高,时间复杂度为常数级别,list容器的底层数据结构为带头双向循环链表,这使得 list的元素可以存储在非相邻的内存中,在list内部,不同元素之间通过指向前一个元素的指针以及指向后一个元素的指针相关联。它的数据由若干个节点构成,每一个节点都包括一个信息块(即实际存储的数据)、一个前驱指针和一个后驱指针,可以向前也可以向后进行访问,但不能随机访问。列表的定义在头文件<list>中。list 容器与其他序列容器如vector deque array相比,由于其底层数据结构为带头双向循环链表,因此 list 在插入删除元素方面很有优势,在列表的任意位置进行插入和删除操作的时间复杂度为O(1)。但不能直接通过位置(下标)来直接访问元素。想要访问list的某个元素,必须从list的一端(或已知位置)迭代到该元素。另外,list还需要额外的存储空间来储存前一个元素和后一个元素的链接信息。
1.list的存储结构
list 容器底层为带头双向循环链表,带头双向循环链表每个节点包含指向前驱节点的pre指针,指向后继节点的next指针以及节点的数据。list存储结构如下图所示: 哨兵节点表示链表的第一个元素节点,且不保存任何数据。
2.list 容器的优点
(1)高效的插入和删除:由于std::list是基于带头双向循环链表实现的,插入和删除操作在任意位置都具有常数时间复杂度O(1),不需要移动其他元素。这使得std::list在需要频繁插入和删除元素的场景下非常高效。
(2)稳定的迭代器:在std::list中进行插入和删除操作不会使得迭代器失效。这意味着在插入或删除元素后,仍然可以继续使用之前获取的迭代器进行遍历和操作。
(3)动态内存管理:std::list可以动态调整大小,根据需要分配和释放内存。这使得std::list能够有效地处理大量元素的情况,而不会浪费过多的内存空间。
3.list容器的缺点
低效的随机访问:由于std::list的存储结构是带头双向循环链表,访问元素时需要从头或尾开始遍历链表,因此在列表中进行随机访问的效率较低。获取特定位置的元素需要遍历链表,时间复杂度为O(n),其中n是元素的总数量。
占用额外内存:相较于其他容器,std::list在存储上需要额外的指针来维护链表结构,因此在存储大量元素时,它可能占用更多的内存空间。
迭代器不支持指针算术:std::list的迭代器不支持指针算术运算,无法像指针那样直接进行加减操作,这限制了一些操作的灵活性。
头文件:
list是C++ 标准模板库的一部分,因此,想要使用list,需要在程序中包含头文件 list
#include<list>
三、list 容器的定义
单独定义一个 list:
list <typename> name;
这里的typename可以是任何基本类型,例如int、double、char、结构体等,也可以是STL标准容器,例如string、set、queue、vector等。
注意:使用前必须加上头文件:#include <list>
四、list 容器常用接口的使用
4.1 list 的常见构造(初始化)
在 C++ 中,std::list
是一个双向链表容器,提供了一些灵活的初始化方式。以下是 std::list
的常见构造(初始化)方法:
(1)默认构造函数
创建一个空list,不包含任何元素。
#include <iostream>
#include <list>
using namespace std;int main(){list<int> str_a; //list定义的int类型的列表return 0;
}
(2)使用指定数量的元素构造
使用 n
个元素初始化 list
,每个元素的值为默认值(例如,对于 int
,默认值为 0
)。
#include <iostream>
#include <list>
using namespace std;int main(){list<int> myList(5); // 列表包含 5 个元素,默认值为 0return 0;
}
也可以指定一个初始值 val
,为所有元素赋相同的值。
#include <iostream>
#include <list>
using namespace std;int main(){list<int> myList(5, 10); // 列表包含 5 个元素,每个元素的值为 10return 0;
}
(3)使用初始化列表构造
使用花括号 {}
来初始化 list
,指定初始元素的值。
#include <iostream>
#include <list>
using namespace std;int main(){list<int> myList = {1,2,3,4,5}; //列表包含5个元素,依次为1,2,3,4,5return 0;
}
(4)拷贝构造函数
使用另一个 list
来初始化新的 list
,会创建一个原列表的副本。
#include <iostream>
#include <list>
using namespace std;int main(){list<int> originalList = {1, 2, 3};list<int> copiedList(originalList); // 使用 originalList 初始化 copiedList,列表包含 3 个元素,依次为 1, 2, 3return 0;
}
(5)移动构造函数
使用 move
将一个临时 list
资源转移到新 list
中,从而避免拷贝开销。
#include <iostream>
#include <list>
using namespace std;int main(){list<int> originalList = {1, 2, 3};list<int> movedList(move(originalList)); // originalList 变为空,movedList 拥有原始数据return 0;
}
(6)范围构造函数
使用迭代器范围 [first, last)
初始化 list
,可以将其他容器的部分或全部内容拷贝到 list
中。
#include <iostream>
#include <list>
#include <vector>using namespace std;int main(){vector<int> vec = {1, 2, 3, 4, 5};list<int> myList(vec.begin(), vec.end()); // 使用 vector 的元素初始化 listreturn 0;
}
(7)使用 assign
赋值
虽然这不是构造函数,但 assign
可以在声明后直接赋值初始化。
#include <iostream>
#include <list>
#include <vector>using namespace std;int main(){list<int> myList;myList.assign(5, 10); // 列表包含 5 个元素,每个元素的值为 10return 0;
}
4.2 list 的遍历及迭代器的操作
接口名称 | 接口说明 |
迭代器(⭐) | begin() + end() 或者 rbegin() + rend() |
范围for | C++11支持更简单的for的新遍历方式(底层还是借用迭代器实现) |
注意:(遍历链表-------只能用**迭代器**和**范围for**)
- begin与end为正向迭代器,对迭代器执行++操作,迭代器向后移动
- rbegin(end)与rend(begin)为反向迭代器,对迭代器执行++操作,迭代器向前移动
迭代器
接口名称 | 接口说明 |
begin()(⭐) | 返回第一个元素的迭代器 |
end()(⭐) | 返回最后一个元素下一个位置的迭代器 |
rbegin() | 返回最后一个元素的迭代器 |
rend() | 返回第一个元素的前一个位置额迭代器 |
(1). 使用迭代器遍历
通过 begin()
和 end()
获取 list
的起始和终止迭代器,使用迭代器遍历 list
。
#include <iostream>
#include <list>
using namespace std;int main(){list<int> myList = {1,2,3,4,5};// 使用普通迭代器遍历for(list<int>::iterator it = myList.begin(); it != myList.end(); it++){cout << *it << " ";}cout << endl; //输出:1 2 3 4 5 return 0;
}
- 注意:begin与end为正向迭代器,对迭代器执行++操作,迭代器向后移动
rbegin和rend
- 通过rbegin函数可以得到容器中最后一个元素的反向迭代器,通过rend函数可以得到容器中第一个元素的前一个位置的反向迭代器。
反向迭代器遍历容器:
#include <iostream>
#include <list>
using namespace std;
int main()
{int array[] = { 1,2,3,4,5,6,7,8 };// 用已有的数组 初始化 list 链表list<int> lt(array, array + sizeof(array) / sizeof(array[0]));// 正向迭代器遍历容器list<int>::reverse_iterator it = lt.rbegin();// 开始循环遍历while (it != lt.rend()){cout << *it << " "; // 8 7 6 5 4 3 2 1it++;}cout << endl;return 0;
}
- 注意:rbegin(end)与rend(begin)为反向迭代器,对迭代器执行++操作,迭代器向前移动
(2)使用基于范围的 for
循环(C++11)
在 C++11 及以上版本中,可以使用基于范围的 for
循环简化遍历过程。
#include <iostream>
#include <list>using namespace std;int main(){list<int> myList = {1,2,3,4,5};for(int value : myList){cout << value << " ";}cout << endl; // 输出:1 2 3 4 5return 0;
}
4.3 list 容器的常见容量操作
接口名称 | 接口说明 |
empty() | 判断 list 是否为空 |
size | 返回容器中有效元素个数 |
resize(⭐) | 调整容器的有效元素大小(size) |
clear | 用于清空容器,清空后容器的size为0, 但是头结点(哨兵位)不会被清除 |
(1)empty()
判断 list
是否为空。如果 list
不包含任何元素,则返回 true
,否则返回 false
。
#include <iostream>
#include <list>
using namespace std;int main(){list<int> myList;if(myList.empty()) {cout << "The list is empty." << endl;} else{cout << "The list is not empty." << endl;}return 0; // 输出:The list is empty.
}
(2)size()
返回 list
中元素的数量。
#include <iostream>
#include <list>
using namespace std;int main()
{list<int> myList = {1,2,3,4,5};cout << "Size of the list: " << myList.size()<<endl; // 输出5return 0;
}
(3)resize()
调整 list
的大小。如果新大小比当前大小大,则会在 list
的末尾添加默认值的元素;如果新大小比当前大小小,则会移除多余的元素。
#include <iostream>
#include <list>
using namespace std;void PrintList(list<int> l){list<int>::iterator it = l.begin();while(it != l.end()){cout << *it << " ";it ++;}cout << endl;
}int main(){list<int> myList = {1,2,3,4,5};// 缩小列表的大小myList.resize(3); // 现在myList只包含1,2,3PrintList(myList);//扩大列表的大小,添加默认值0myList.resize(6); // 现在myList包含1,2,3,0,0,0PrintList(myList);return 0;
}
(4)clear
clear 函数用于清空容器,清空后容器的size为0, 但是头结点(哨兵位)不会被清除。
#include <iostream>
#include <list>
using namespace std;int main()
{list<int> lt(5, 2);for (auto e : lt){cout << e << " ";}cout << endl; //2 2 2 2 2cout << lt.size() << endl; //5lt.clear(); //清空容器for (auto e : lt){cout << e << " ";}cout << endl; //(无数据)cout << lt.size() << endl; //0return 0;
}
list 容量的常见访问操作
① front()——访问list头元素
❤front()返回list第一个元素❤
#include<iostream>
#include<list>
using namespace std;int main(){list<int> mylist {1,2,3,4,5,6,7,8};cout << "初始化后的mylist为: ";for(auto num: mylist){cout << num<< " ";}int front = mylist.front(); // 1cout << "\nmylist的头元素为: " << front;return 0;
}
② back()——访问list尾元素
❤back()返回list第一个元素❤
示例如下:
💻示例代码💻
#include<iostream>
#include<list>using std::cout;
using std::endl;
using std::list;int main() {list<int> mylist {1, 2, 3, 4, 5, 6, 7, 8};cout<< "初始化后的mylist为:";for (auto num : mylist) {cout<< num<< " ";}int front = mylist.back(); // 8cout<< "\nmylist的头元素为:"<< front;
}
list 容器的常见修改操作
接口名称 | 接口说明 |
push_front | 在list首元素前插入值为val的元素 |
pop_front | 删除list中第一个元素 |
push_back | 在list尾部插入值为val的元素 |
pop_back | 删除list中最后一个元素 |
insert(⭐) | 在list position 位置中插入值为val的元素 |
erase(⭐) | 删除list position位置的元素 |
swap | 交换两个list中的元素 |
① push_back()——添加元素(list尾部)
向list中添加元素,使用
push_back()
方法,作用是向list尾部添加一个元素。示例如下:
💻示例代码💻
#include <list>
#include <iostream>
using namespace std;int main(){list<int> mylist{1,2,3,4};cout << "初始化后的mylist为: ";for(auto num:mylist){cout <<num<<" ";}//在list尾部插入一个元素8mylist.push_back(8);cout << "\n尾部插入一个元素后的mylist为: ";for(auto num: mylist){cout << num << " "; //1 2 3 4 8}return 0;
}
② pop_back()——移除list元素(尾部)
删除list中的元素,使用pop_back()
方法,作用是删除list尾部的一个元素。示例如下:
💻示例代码💻
#include <list>
#include <iostream>using std::cout;
using std::list;
using std::endl;int main() {list<int> mylist{1, 2, 3, 4};cout << "初始化后的mylist为:";for (auto num : mylist) {cout << num << " ";}// 删除mydeue尾部一个元素mylist.pop_back();cout << "\n尾部删除一个元素后的mylist为:";for (auto num : mylist) {cout << num << " "; // 1 2 3}
}
③ pop_front()——删除list元素(头部)
删除list中的元素,使用pop_front()
方法,作用是删除list头部的一个元素。示例如下:
💻示例代码💻
#include <list>
#include <iostream>using std::cout;
using std::list;
using std::endl;int main() {list<int> mylist{1, 2, 3, 4};cout << "初始化后的mylist为:";for (auto num : mylist) {cout << num << " ";}// 删除mydeue头部一个元素mylist.pop_front();cout << "\n头部删除一个元素后的mylist为:";for (auto num : mylist) {cout << num << " "; // 2 3 4}
}
④ push_front()——添加元素(list头部)
向list中添加元素,使用push_front()
方法,作用是向list头部添加一个元素。示例如下:
💻示例代码💻
#include <list>
#include <iostream>using std::cout;
using std::list;
using std::endl;int main() {list<int> mylist{1, 2, 3, 4};cout << "初始化后的mylist为:";for (auto num : mylist) {cout << num << " ";}// 在list头部插入一个元素8mylist.push_front(8);cout << "\n头部插入一个元素后的mylist为:";for (auto num : mylist) {cout << num << " "; // 8 1 2 3 4}
}
⑤ insert()——添加元素(任意位置)
insert共有三种形式:
- insert(iterator, value);
- insert(iterator, num, value);
- insert(iterator, iterator1, iterator2);
用法一:list.insert(iterator,value)
使用insert(iterator,value)
方法,作用是向iterator迭代器指向元素的前边添加一个元素value,并返回一个迭代器指向新插入的元素。
#include <iostream>
#include <list>
using namespace std;void PrintList(list<int> l){for(auto num : l){cout << num << " ";}cout << endl;
}int main(){list<int> mylist{1,2,3,4};cout <<"初始化后的mylist为:";PrintList(mylist);// it 指向mylist的第二个元素list<int>::iterator it = mylist.begin();it ++;// 使用insert添加一个元素list<int>::iterator itnew = mylist.insert(it,10);cout << "\n返回的迭代器指向的元素为: " << *itnew << endl;PrintList(mylist);return 0;
}
用法二:list.insert(iterator,num,value)
使用insert(iterator,num,value)
方法,作用是向iterator迭代器指向元素的前边添加num个元素value,并返回一个迭代器指向新插入的第一个元素.
#include <iostream>
#include <list>
using namespace std;void PrintList(list<int> l){for(auto num: l){cout << num << " ";}cout << endl;
}int main(){list<int> mylist{1,2,3,4};cout << "初始化后的mylist为: ";for(auto num : mylist){cout << num << " ";}// it 指向mylist的第二个元素list<int>::iterator it = mylist.begin();it ++;//使用insert添加2个元素,value为20mylist.insert(it, 2, 20);PrintList(mylist);return 0;
}
用法三:insert(iterator, iterator1, iterator2)
使用insert(iterator, iterator1, iterator2);
方法,作用是向iterator迭代器指向元素的前边添加[iterator1,iterator2)之间的元素。
💻示例代码💻
#include <list>
#include <iostream>
using namespace std;void PrintList(list<int> l)
{for(auto num: l){cout << num << " ";}cout << endl;
}int main(){list<int> mylist{1,2,3,4};cout << "初始化后的mylist为: ";// it指向mylist的第二个元素list<int>::iterator it = mylist.begin();it ++;//定义一个辅助listlist<int> list{20,30,40};//it1指向list的第一个元素::list<int>::iterator it1 = list.begin();// it2指向list2的最后一个元素后一个位置::list<int>::iterator it2 = list.end();// 使用insert在2之前添加[it1,it2]之间的元素mylist.insert(it,it1,it2);cout << "\n使用insert插入元素后:";PrintList(mylist);return 0;
}
⑥ erase()——删除元素(任意位置)
❤erase的作用就是根据传入的迭代器删除list中的元素,参数为一个迭代器,只删除迭代器指向的元素;参数为两个迭代器,删除两个迭代器之间的元素❤
erase 共有 两 种形式:
- list.erase(iterator)
- list.erase(iterator1,iterator2)
用法一:list.erase(iterator)
这种用法会删除迭代器iterator指向的元素。
💻示例代码💻
#include <list>
#include <iostream>
using namespace std;void PrintList(list<int> l){for( auto num: l){cout << num<< " ";}}int main(){list<int> mylist{1,2,3,4};cout <<"初步化后的mylist为:";// it指向mylist的第二个元素list<int>::iterator it = mylist.begin();it ++ ;//删除it指向的元素,即2,并返回一个迭代器指向2之后的元素list<int>::iterator itnew = mylist.erase(it);cout << "\n删除元素后返回的迭代器itnew指向的元素为:"<< *itnew;cout << "\n使用erase删除元素后:";PrintList(mylist);}
用法二:list.erase(iterator1,iterator2)
这种用法会删除迭代器iterator1指向的元素到iterator2指向元素之间的元素,包括iterator1指向的元素但不包括iterator2指向的元素,即擦除[iterator1,iterator2)。
💻示例代码💻
#include <iostream>
#include <list>
using namespace std;void PrintList(list<int> l){for( auto num: l){cout << num<< " ";}}int main(){list<int> mylist{1,2,3,4};cout << "初始化后的mylist为:";PrintList(mylist);//it1指向mylist的第2个元素list<int>::iterator it1 = mylist.begin();it1 ++;// it2指向最后一个元素的最后一个位置list<int>::iterator it2 = mylist.end();// 删除[it1,it2]之间的元素,即删除2,3,4mylist.erase(it1,it2);cout << "\n使用erase删除元素后:";PrintList(mylist);return 0;
}
⑦ swap()——交换元素
❤swap的作用就是交换两个list的元素❤
交换两个list的元素,使用swap()
方法,list1.swap(list2)
,两个list存储的元素类型必须相同,元素个数可以不同。示例如下:
💻示例代码💻
/*交换两个list的元素,使用swap()方法,list1.swap(list2),两个list存储的元素类型必须相同,元素个数可以不同。
*/
#include <list>
#include <iostream>
using namespace std;void PrintList(list<int> l){for( auto num: l){cout << num<< " ";}cout << endl;}int main(){list<int> mylist1{1,11,111,1111};list<int> mylist2{2,22,222};cout << "初始化后的mylist1为:";PrintList(mylist1);cout << "初始化后的mylist2为:";PrintList(mylist2);//交换两个列表的元素·mylist1.swap(mylist2);//交换后mylist1为:cout << "使用swap()交换后mylist1为:";PrintList(mylist1);cout << "使用swap()交换后mylist1为:";PrintList(mylist2);return 0;
}
输出:
初始化后的mylist1为:1 11 111 1111
初始化后的mylist2为:2 22 222
使用swap()交换后mylist1为:2 22 222
使用swap()交换后mylist1为:1 11 111 1111
list 容器的常见操作函数
函数声明 | 接口说明 |
splice | 将元素从列表转移到其它列表 |
remove | 删除具有特定值的元素 |
remove_if | 删除满足条件的元素 |
unique | 删除重复值 |
sort | 容器中的元素排序 |
merge | 合并排序列表 |
reverse | 反转元素的顺序 |
① splice()——从另一个list中移动元素
splice 方法用于将另一个 std::list 的元素插入到当前列表的指定位置。示例如下:
splice共有三种形式,分别为:
- splice(iterator_pos, otherList) : 将otherList中的所有元素移动到iterator_pos指向元素之前
- splice(iterator_pos, otherList, iter1): 从 otherList转移 iter1 指向的元素到当前list。元素被插入到 iterator_pos指向的元素之前。
- splice(iterator_pos, otherList, iter_start, iter_end) : 从 otherList转移范围 [iter_start, iter_end) 中的元素到 当前列表。元素被插入到 iterator_pos指向的元素之前。
注意:
1. splice 操作不会进行元素的复制或移动,只是修改指针连接,因此效率较高。
2. 在 splice 操作后,被移动的元素将不再属于原始列表。
3. splice 操作后,原始列表和插入列表的大小会相应改变。
4. 插入操作的时间复杂度为 O(1)
💻示例代码💻
#include <iostream>
#include <list>
using namespace std;void PrintList(list<int> l){for( auto num: l){cout << num<< " ";}cout << endl;}int main(){list<int> list1{1,2,3,4,5};list<int> list2{10,20,30};list<int> list3{1,2,3,4,5};list<int> list4{10,20,30};list<int> list5{1,2,3,4,5};list<int> list6{10,20,30};cout << "初始化之后的list1: ";PrintList(list1);/*用法一 : splice(iterator_pos, otherList)*/// 将list2中所有元素插入到list1的第一个位置list1.splice(list1.begin(),list2);// 输出结果cout << "转移list2元素到list1之后的list1: ";PrintList(list1);cout << "转移list2元素到list1之后的list2: ";PrintList(list2);/*用法二:splice(iterator_pos, otherList, iter1)*/auto it = list3.begin();//将迭代器向后移动两个位置,指向第三个元素advance(it,2);//将list4的第一个元素(10)插入到list3的第三个位置list3.splice(it,list4,list4.begin());//输出结果cout << "转移list4元素到list3之后的list3: ";PrintList(list3);cout << "转移list4元素到list3之后的list4: ";PrintList(list4);/*用法三:splice(iterator_pos, otherList, iter_start, iter_end)*/auto it2 = list5.begin();advance(it2,2);list5.splice(it2,list6,list6.begin(),list6.end());cout << "转移list6元素到list5之后的list5: ";PrintList(list5);cout << "转移list6元素到list5之后的list6: ";PrintList(list6);return 0;
}
运行结果:
初始化之后的list1: 1 2 3 4 5
转移list2元素到list1之后的list1: 10 20 30 1 2 3 4 5
转移list2元素到list1之后的list2:
转移list4元素到list3之后的list3: 1 2 10 3 4 5
转移list4元素到list3之后的list4: 20 30
转移list6元素到list5之后的list5: 1 2 10 20 30 3 4 5
转移list6元素到list5之后的list6:
② remove()——移除特定值的元素
std::list 的 remove 方法用于从列表中移除与指定值相等的元素。示例如下:
#include <iostream>
#include <list>int main() {std::list<int> mylist {1, 2, 3, 4, 5};// 移除列表中所有值为2的元素mylist.remove(2);std::cout << "移除之后的mylist: ";for (const auto& element : mylist) {std::cout << element << " ";}std::cout << std::endl;return 0;
}
③ remove_if()——移除满足特定标准的元素
使用方式:remove_if(func)
使用 remove_if 方法可以从 mylist 中移除所有满足 函数func的元素,下边的例子中定义了一个函数isEven(),判断一个数字是否为偶数。示例如下:
💻示例代码💻
#include <iostream>
#include <list>
using namespace std;bool isEven(int n){return n % 2 == 0;
}
int main(){list<int> mylist{1,2,3,4,5};// 一处列表中所有满足 isEven 的元素(偶数)mylist.remove_if(isEven);cout << "移除之后的mylist: ";for(const auto& element : mylist){cout << element << " ";}cout << endl;return 0;
}
📄输出📄
移除之后的mylist: 1 3 5
④ unique()——删除连续的重复元素
unique()方法用于移除链表中相邻且重复的元素,仅保留一个副本。示例如下:
💻示例代码💻
#include <iostream>
#include <list>
using namespace std;int main(){list<int> mylist{1,2,2,3,3,4,5,5,5};cout << "初始化后的list为: ";for(auto i: mylist){cout << i << " ";}cout << endl;mylist.unique();cout << "执行unique后的list为: ";for (auto i: mylist){cout << i << " ";}cout << endl;return 0;
}
运行结果:
初始化后的list为: 1 2 2 3 3 4 5 5 5
执行unique后的list为: 1 2 3 4 5
⑤ sort()——对元素进行排序
sort()方法用于对链表中的元素进行排序。默认情况下,它按升序对元素进行排序,使用元素类型的 < 运算符进行比较。示例如下:
💻示例代码💻
#include <iostream>
#include <list>
using namespace std;void PrintList(list<int> l){for( auto num: l){cout << num << " ";}cout << endl;
}int main(){list<int> mylist{5,0,3,2,4,1};cout << "初始化后的list为 ";PrintList(mylist);mylist.sort();cout << "经过sort排序后的list为:";PrintList(mylist);return 0;
}
运行结果:
初始化后的list为 5 0 3 2 4 1
经过sort排序后的list为:0 1 2 3 4 5
⑥ merge()——合并list
将两个已排序的列表合并成一个有序的列表。merge()方法将另一个列表的元素插入到调用方法的列表中,并保持排序顺序。示例如下:
注意:merge()方法只能用于已排序的list。如果list未排序,则合并的结果将是不正确的。
💻示例代码💻
#include <iostream>
#include <list>
using namespace std;void PrintList(list<int> l){for( auto num: l){cout << num << " ";}cout << endl;
}int main(){list<int> list1{1,3,5};list<int> list2{2,4,6};cout << "初始化后的list1为: ";PrintList(list1);cout << "初始化后的list2为: ";PrintList(list2);//将list2合并到list1中list1.merge(list2);// 输出合并后的列表cout << "合并后的list1为: ";PrintList(list1);return 0;
}
运行结果:
初始化后的list1为: 1 3 5
初始化后的list2为: 2 4 6
合并后的list1为: 1 2 3 4 5 6
⑦ reverse()——将该链表的所有元素的顺序反转 【C++11】
reverse()方法用于反转链表中元素的顺序,即将链表中的第一个元素变为最后一个元素,第二个元素变为倒数第二个元素,以此类推。示例如下:
💻示例代码💻
#include <iostream>
#include <list>using namespace std;void PrintList(list<int> l){for( auto num: l){cout << num << " ";}cout << endl;
}int main(){list<int> mylist{1,2,0,3,4,5};cout <<"初始化list为:";PrintList(mylist);mylist.reverse();cout <<"Reverse后的list为:";PrintList(mylist);return 0;
}
代码运行结果:
初始化list为:1 2 0 3 4 5
Reverse后的list为:5 4 3 0 2 1
⑧ assign()——将值赋给容器
assign()方法用于将链表中的元素替换为新的元素序列。它可以接受不同形式的参数,提供了两种重载形式。
- 第一种形式接受迭代器范围作为参数,用于将另一个容器或数组中的元素复制到链表中。它会将链表清空,并将指定范围内的元素复制到链表中。void assign(InputIterator first,InputIteratorlast);
- 第二种形式接受一个元素数量和一个值作为参数,用于将指定数量的相同值的元素分配给链表。它会将链表清空,并将指定数量的元素赋值为指定的值。void assign(size_type count, const T& value);
💻示例代码💻
#include <iostream>
#include <vector>
#include <list>int main() {std::list<int> myList;// 使用迭代器范围进行分配std::vector<int> numbers = {1, 2, 3, 4, 5};myList.assign(numbers.begin(), numbers.end());std::cout << "第一次执行assign之后的list为: ";for (auto i : myList) {std::cout << i << " ";}std::cout << std::endl;// 使用元素数量和值进行分配myList.assign(3, 100);std::cout << "第二次执行assign之后的list为: ";for (auto i : myList) {std::cout << i << " ";}std::cout << std::endl;return 0;
}
运行结果:
第一次执行assign之后的list为: 1 2 3 4 5
第二次执行assign之后的list为: 100 100 100
五、vector 与 list 的对比
对比 | vector | list |
底层结构 | 动态顺序表,连续空间 | 带头结点的双向循环链表 |
访问 | 支持随机访问,首地址+下标 | 不能随机访问,可通过find查找,访问随即元素时间复杂度O(N) |
插入删除 | 任意位置插入和删除效率低,需要搬移元素,时间复杂度为O(N),插入时有可能需要增容,增容:开辟新空间,拷贝元素,释放旧空间,导致效率更低 | 任意位置插入和删除效率高,不需要搬移元素,时间复杂度为O(1) |
空间利用率 | 底层为连续空间,不容易造成内存碎片,空间利用率较高,缓存利用率高。可以一次将一个数据附近的空间都加载到缓存,不用频繁地从内存读取数据 | 底层节点动态开辟,容易造成内存碎片,空间利用率低,缓存利用率低 |
迭代器 | 原生态指针 | 对指针进行了封装 |
迭代器失效 | 容量相关的操作都有可能导致迭代器失效,如插入引起的扩容,删除元素等 | 插入元素不会导致迭代器失效,删除节点会导致,且只影响当前迭代器,其他迭代器不受影响 |
使用场景 | 不关心插入和删除效率,支持随机访问 | 大量插入和删除操作,不关心随机访问的场景 |
相关文章:

列表(list)
一、前言 本次博客主要讲解 list 容器的基本操作、常用接口做一个系统的整理,结合具体案例熟悉自定义内部排序方法的使用。如有任何错误,欢迎在评论区指出,我会积极改正。 二、什么是list list是C的一个序列容器,插入和删除元素…...

使用Python抓取数据的实战指南
引言 在当今信息爆炸的时代,数据已成为一种宝贵的资源。无论是学术研究、市场分析,还是个人兴趣,数据的获取都是至关重要的一步。Python,凭借其强大的库和简洁的语法,成为了数据抓取(也称为网络爬虫或网页…...

GIC寄存器介绍
往期内容 本专栏往期内容,interrtupr子系统: 深入解析Linux内核中断管理:从IRQ描述符到irq domain的设计与实现Linux内核中IRQ Domain的结构、操作及映射机制详解中断描述符irq_desc成员详解Linux 内核中断描述符 (irq_desc) 的初始化与动态分…...

c++实现B树(下)
书接上回小吉讲的是B树的搭建和新增方法的实现(blog传送门🚪:B树实现上)(如果有小可爱对B树还不是很了解的话,可以先看完上一篇blog,再来看小吉的这篇blog)。那这一篇主要讲的是B树中…...

外星人入侵
学习于Python编程从入门到实践(Eric Matthes 著) 整体目录:外星人入侵文件夹是打包后的不必在意 图片和音效都是网上下载的 音效下载网站:Free 游戏爆击中 Sound Effects Download - Pixabay 运行效果:可以上下左右移…...

【数据仓库】hbase的安装与简单操作
HBase 是一个分布式的、面向列的开源数据库,它支持大规模数据存储,并且是 Hadoop 生态系统的一部分。HBase 能够在廉价的硬件上运行,并提供对大量数据的随机、实时读写访问。下面是关于如何在 Linux 系统上安装 HBase 以及进行一些基本操作的…...

为什么RNN(循环神经网络)存在梯度消失和梯度爆炸?
1️⃣ 原理分析 RNN前向传播的公式为: x t x_t xt是t时刻的输入 s t s_t st是t时刻的记忆, s t f ( U ⋅ x t W ⋅ s t − 1 ) s_tf(U\cdot x_tW\cdot s_{t-1}) stf(U⋅xtW⋅st−1),f表示激活函数, s t − 1 s_{t-1} …...

【数据库】数据库迁移的注意事项有哪些?
数据库迁移是一个复杂且关键的过程,需要谨慎处理以确保数据的完整性和应用程序的正常运行。以下是一些数据库迁移时需要注意的事项: 1. 充分的前期准备 1.1 评估迁移需求 明确目标:确定迁移的具体目标,例如添加新字段、修改现…...

MQTT协议解析 : 物联网领域的最佳选择
1. MQTT协议概述 1.1 MQTT协议是什么 MQTT : Message Queuing Telemetry Transport 模式 : 发布 / 订阅主题优点 : 代码量小、低带宽、实时可靠应用 : 物联网、小型设备、移动应用MQTT 常用端口 : 1883 MQTT是一个网络协议,和HTTP类似,因为轻量简单&…...

pycharm中from[本地包]import文件/模块出现问题(最最最全方法!)
1.通过PYTHONPATH的方法在此处将路径添加上,能够让IDE访问得到。 2.通过选中目标文件所在的文件的文件夹单击右键,如下图所示可以看到下方的mark directory as选项中存在 存在excluded,选择此项可解决问题,如果仍有问题可以尝试其…...

MongoDB在现代Web开发中的应用
💓 博客主页:瑕疵的CSDN主页 📝 Gitee主页:瑕疵的gitee主页 ⏩ 文章专栏:《热点资讯》 MongoDB在现代Web开发中的应用 MongoDB在现代Web开发中的应用 MongoDB在现代Web开发中的应用 引言 MongoDB 概述 定义与原理 发展…...

Python Bokeh 数据可视化教程
Python Bokeh 数据可视化教程 引言 在数据科学和分析的过程中,数据可视化是一个至关重要的环节。它不仅能帮助我们更好地理解数据,还能在报告和展示中提升数据的可读性和吸引力。Python 作为数据科学的主要工具之一,提供了多种数据可视化库…...

(一)<江科大STM32>——软件环境搭建+新建工程步骤
一、软件环境搭建 (1)安装 Keil5 MDK 文件路径:江科大stm32入门教程资料/Keil5 MDK/MDK524a.EXE,安装即可,路径不能有中文。 (2)安装器件支持包 文件路径:江科大stm32入门教程资料…...

内存大小的单位转换
计算机中内存大小的单位转换通常是按照以下规则进行的: 基本单位 1 字节 (Byte) 8 位 (bit) 常见的内存单位及转换关系 1 字节 (Byte) 8 位 (bit)1 千字节 (KB) 1,024 字节 (B)1 兆字节 (MB) 1,024 千字节 (KB) 1,024 * 1,024 字节 (B)1 吉字节 (GB) 1,02…...

如何在 Spring MVC 中使用 `@PostMapping`? 如何在 Spring MVC 中使用 `@PutMapping`?
PostMapping 和 PutMapping 是 Spring MVC 中用于处理 HTTP POST 和 PUT 请求的注解。它们分别对应 HTTP 协议中的 POST 和 PUT 方法,通常用于创建和更新资源。下面详细解释如何在 Spring MVC 中使用这两个注解。 1. 使用 PostMapping PostMapping 注解用于处理 H…...

AIGC Agent(智能体)应用开发高级工程师实战培训 —— 线上8周系统教学课程学习路线图
🎯 课程目标 系统掌握AIGC核心技术:学员将通过项目驱动学习,从文本生成、图像创意到智能体开发,全面进阶AIGC技术,探索其在营销、教育、数据处理、知识管理等领域的实际应用。构建AIGC智能体服务体系:学成…...

GDSC、CTRP数据库学习
GDSC 写在前面下载数据疑问1.GDSC、CTRP数据里有TCGA配套的数据?数据类型?CTRP原始数据如何处理 写在前面 开此贴做GDSC的数据分析记录 下载数据 GDSC官网:http://www.cancerrxgene.org/ 由于在官网下载数据过于麻烦,于是我使用…...

【嵌入式】ESP32开发(一)ESP-IDF概述
文章目录 1 前言2 IDF环境配置3 在VS Code中使用IDF3.1 使用ESP-IDF例程3.2 底部按钮的作用【重要!】3.3 高级用法4 ESP-IDF框架分析5 从零开始创建一个项目5.1 组件(component)6 主要参考资料7 遇到的一些问题与解决办法8 对于ESP-IDF开发的一些感受1 前言 对于ESP32的开发…...

最新6.7分非肿瘤纯生信,使用机器学习筛选慢阻肺中的关键基因。机器学习在非肿瘤生信文章中正火,可重复!
关于非肿瘤生信,我们也解读过很多,主要有以下类型 1 单个疾病WGCNAPPI分析筛选hub基因。 2 单个疾病结合免疫浸润,铁死亡,自噬等基因集,机器学习算法等。 3 两种相关疾病联合分析,包括非肿瘤结合非肿瘤&…...

vue 提交表单抹除字段为空的数据
使用背景 在配合后端post请求接口的时候 仅需要将有值的字段传入接口中 关键代码 cleanDataObj(obj) {Object.keys(obj).forEach((key) > {if (obj[key] ) {delete obj[key]}})},demo如下 export default {data() {return {demoObject:{name:小花,sex:,hobb…...

web实验3:虚拟主机基于不同端口、目录、IP、域名访问不同页面
创建配置文件: 创建那几个目录及文件,并且写内容: 为网卡ens160添加一个 IPv4 地址192.168.234.199/24: 再重新激活一下网卡ens160: 重启服务: 关闭防火墙、改宽松模式: 查看nginx端口监听情况:…...

英伟达Isaac Manipulator产品体验
相关配置 Isaac Manipulator3.1.0Isaac Sim4.2.0Ubuntu20.04GPURTX 4090 LaptopCPUI9 13900HXMem64GB 过程记录与反馈 GPU加速效果 请描述您在使用Isaac Manipulator时,调用cuMotion加速库来进行机器人运动规划和轨迹优化等任务的步骤和过程,并记录任…...

网安加·百家讲坛 | 仝辉:金融机构鸿蒙应用安全合规建设方案
作者简介:仝辉,北京娜迦信息科技发展有限公司攻防安全负责人,深耕移动应用安全领域十余年,获得过CISP、CISSP、OSCP、PMP、CCRC-CIASW等相关证书,参与多项移动应用安全标准起草,参与华为、平安集团、中国移…...

PHP Session
PHP Session PHP Session 是一种在 PHP 中用于跟踪用户会话的技术。会话允许在用户浏览网站时存储和访问用户信息。本文将详细介绍 PHP Session 的工作原理、如何创建和销毁会话、会话的安全性和最佳实践。 什么是 PHP Session? 在 Web 开发中,HTTP 是一种无状态的协议,这…...

泷羽sec学习打卡-Linux基础2
声明 学习视频来自B站UP主 泷羽sec,如涉及侵权马上删除文章 笔记的只是方便各位师傅学习知识,以下网站只涉及学习内容,其他的都与本人无关,切莫逾越法律红线,否则后果自负 关于Linux的那些事儿-Base2 一、Linux-Base2linux有哪些目录呢?不同目录下有哪些具体的文件呢…...

# 【STM32F1】——无线收发模块RF200与串口通信
【STM32F1】——无线收发模块RF200与串口通信 一、简介 本篇主要对调试无线收发模块RF200的过程进行总结,实现了以下功能。 串口普通收发:使用STM32F103C8T6的USART2串口接收中断,实现两个无线收发模块RF200间的通信。二、RF200介绍 电压:3.4-5.5V工作频率:418~455MHz发…...

计算机网络:运输层 —— TCP 协议概述与 TCP 报文段首部格式
文章目录 基本概念主要特点和功能TCP报文段的首部格式字段标志位扩展首部 传输控制协议(Transmission Control Protocol,TCP)协议是互联网上最常用的传输层协议之一,它负责提供可靠的端到端数据传输服务。TCP 协议采用连接导向的通…...

python正则表达式和递归
一、正则表达式 1.基础匹配 学习目标:了解什么是正则表达式,掌握re模块的基础使用 就是一种规则的定义,通过规则去验证给定的目标是否符合定义的规则。 正则的三个基础方法 match match是匹配开头,开头有python就算匹配成功&a…...

JAVA后端生成图片滑块验证码 springboot+js完整案例
前言 现在大部分网部都是图片滑块验证码,这个得要与后端联动起来才是确保接口安全性 通过我们系统在发送手机短息时都会选进行滑块验证,但是我们要保证发送短息接口的全安,具体路思如下 那么这个滑块的必须是与后端交互才能保证安全性&…...

Spring Boot中的自动装配机制
文章目录 1. 什么是自动装配?2. 自动装配是如何工作的?3. 如何开启自动装配?4. 自动装配的注意事项5. 结语推荐阅读文章 在Spring Boot的世界里,自动装配(Auto-configuration)就像春风拂面,轻轻…...