
目錄
list的介紹及使用
1.list的含義
2.list的介紹
3.list的使用
1.list的構造
2.list iterator的使用
3.list capacity
4.list element access
5 list modifiers
尾插尾刪 和 頭插頭刪
insert 和 erase
resize swap clear
6.list sort and reverse
7.list copy vector copy list
8.splice
9?list的迭代器失效
前言:
🎯個人博客:Dream_Chaser
🎈博客專欄:C++
📚本篇內容:list的介紹及使用
list的介紹及使用
1.list的含義
????????列表是序列容器,允許在序列內的任何位置進行常量時間的插入和刪除操作,以及兩個方向的迭代。
容器的分類:
2.list的介紹
1.list是可以在常數范圍內在任意位置進行插入和刪除的序列式容器,并且該容器可以前后雙向迭代。
2. list的底層是雙向鏈表結構,雙向鏈表中每個元素存儲在互不相關的獨立節點中,在節點中通過指針指向其前一個元素和后一個元素。
3. list與forward_list非常相似:最主要的不同在于forward_list是單鏈表,只能朝前迭代,已讓其更簡單高效。
4. 與其他的序列式容器相比(array,vector,deque),list通常在任意位置進行插入、移除元素的執行效率更好。
5. 與其他序列式容器相比,list和forward_list最大的缺陷是不支持任意位置的隨機訪問,比如:要訪問list的第6個元素,必須從已知的位置(比如頭部或者尾部)迭代到該位置,在這段位置上迭代需要線性的時間開銷;list還需要一些額外的空間,以保存每個節點的相關聯信息(對于存儲類型較小元素的大list來說這可能是一個重要的因素)
3.list的使用
list中的接口比較多,此處類似,只需要掌握如何正確的使用,然后再去深入研究背后的原理,已達到可擴展的能力。以下為list中一些常見的重要接口
1.list的構造
構造函數( (constructor)) | 接口說明 |
list (size_type n, const value_type& val = value_type()) | 構造的 list 中包含 n 個值為 val 的元素 |
list() | 構造空的 list |
list (const list& x) | 拷貝構造函數 |
list (InputIterator first, InputIterator last) | 用 [first, last) 區間中的元素構造 list |
void TestList1()
{list<int> l1; // 構造空的l1list<int> l2(4, 100); // l2中放4個值為100的元素list<int> l3(l2.begin(), l2.end()); // 用l2的[begin(), end())左閉右開的區間構造l3list<int> l4(l3); // 用l3拷貝構造l4// 以數組為迭代器區間構造l5int array[] = { 16,2,77,29 };list<int> l5(array, array + sizeof(array) / sizeof(int));// 列表格式初始化C++11list<int> l6{ 1,2,3,4,5 };// 用迭代器方式打印l5中的元素list<int>::iterator it = l5.begin();while (it != l5.end()){cout << *it << " ";++it;}cout << endl;// C++11范圍for的方式遍歷for (auto& e : l5)cout << e << " ";cout << endl;
}
2.list iterator的使用
此處,大家可暫時將迭代器理解成一個指針,該指針指向list中的某個節點。
函數聲明 | 接口說明 |
begin + end | 返回第一個元素的迭代器 + 返回最后一個元素下一個位置的迭代器 |
rbegin + rend | 返回第一個元素的 reverse_iterator, 即 end 位置 , 返回最后一個元素下一個位置的 reverse_iterator, 即 begin 位置 |
【注意】
1. begin與end為正向迭代器,對迭代器執行++操作,迭代器向后移動
2. rbegin(end)與rend(begin)為反向迭代器,對迭代器執行++操作,迭代器向前移動
void TestList2()
{int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };list<int> l(array, array + sizeof(array) / sizeof(array[0]));// 使用正向迭代器正向list中的元素// list<int>::iterator it = l.begin(); // C++98中語法auto it = l.begin(); // C++11之后推薦寫法while (it != l.end()){cout << *it << " ";++it;}cout << endl;// 使用反向迭代器逆向打印list中的元素// list<int>::reverse_iterator rit = l.rbegin();auto rit = l.rbegin();while (rit != l.rend()){cout << *rit << " ";++rit;}cout << endl;
}
3.list capacity
#include <iostream>
#include <list>int main() {// 創建一個std::list并添加幾個元素std::list<int> numbers = { 1, 2, 3, 4, 5 };// 使用size()方法打印列表中的元素數量std::cout << "The list contains " << numbers.size() << " elements." << std::endl;// 使用empty()方法檢查列表是否為空,并打印結果if (numbers.empty()) {std::cout << "The list is empty." << std::endl;}else {std::cout << "The list is not empty." << std::endl;}// 移除所有元素后,再次使用empty()檢查numbers.clear();if (numbers.empty()) {std::cout << "After clearing, the list is now empty." << std::endl;}return 0;
}
4.list element access
函數聲明 | 接口說明 |
front | 返回 list 的第一個節點中值的引用 |
back | 返回 list 的最后一個節點中值的引用 |
5 list modifiers
函數聲明 | 接口說明 |
push_front | 在 list 首元素前插入值為 val 的元素 |
pop_front | 刪除 list 中第一個元素 |
push_back | 在 list 尾部插入值為 val 的元素 |
pop_back | 刪除 list 中最后一個元素 |
insert | 在 list position 位置中插入值為 val 的元素 |
erase | 刪除 list position 位置的元素 |
swap | 交換兩個 list 中的元素 |
clear | 清空 list 中的有效元素 |
尾插尾刪 和 頭插頭刪
/ list迭代器的使用
// 注意:遍歷鏈表只能用迭代器和范圍for
void PrintList(const list<int>& l)
{// 注意這里調用的是list的 begin() const,返回list的const_iterator對象for (list<int>::const_iterator it = l.begin(); it != l.end(); ++it){cout << *it << " ";// *it = 10; 編譯不通過}cout << endl;
}//list插入和刪除
// push_back/pop_back/push_front/pop_front
void TestList3()
{int array[] = { 1, 2, 3 };list<int> L(array, array + sizeof(array) / sizeof(array[0]));// 在list的尾部插入4,頭部插入0L.push_back(4);L.push_front(0);PrintList(L);// 刪除list尾部節點和頭部節點L.pop_back();L.pop_front();PrintList(L);
}
insert 和 erase
// insert /erase
void TestList4()
{int array1[] = { 1, 2, 3 };list<int> L(array1, array1 + sizeof(array1) / sizeof(array1[0]));// 獲取鏈表中第二個節點auto pos = ++L.begin();cout << *pos << endl;// 在pos前插入值為4的元素L.insert(pos, 4);PrintList(L);// 在pos前插入5個值為5的元素L.insert(pos, 5, 5);PrintList(L);// 在pos前插入[v.begin(), v.end)區間中的元素vector<int> v{ 7, 8, 9 };L.insert(pos, v.begin(), v.end());PrintList(L);// 刪除pos位置上的元素L.erase(pos);PrintList(L);// 刪除list中[begin, end)區間中的元素,即刪除list中的所有元素L.erase(L.begin(), L.end());PrintList(L);
}
resize swap clear
void TestList5()
{// 用數組來構造listint array1[] = { 1, 2, 3 };list<int> l1(array1, array1 + sizeof(array1) / sizeof(array1[0]));PrintList(l1);// 交換l1和l2中的元素list<int> l2;l1.swap(l2);PrintList(l1);PrintList(l2);// 將l2中的元素清空l2.clear();cout << l2.size() << endl;
}
6.list sort and reverse
void test_list2()
{list<int> lt;lt.push_back(1);lt.push_back(2);lt.push_back(3);lt.push_back(4);lt.push_back(5); for (auto e : lt){cout << e << " ";}cout << endl;lt.reverse();for (auto e : lt){cout << e << " ";}cout << endl;//sort(lt.begin(), lt.end());//為什么這個不能用呢?那是因為,list是雙向迭代器//而標準庫里面的sort支持隨機迭代器,要用list自己的sort函數 lt.sort//升序 < lesslt.sort();//降序 > greater();//greater<int> gt;//lt.sort(gt);//匿名對象的排序,降序lt.sort(greater<int>());for (auto e : lt){cout << e << " ";}cout << endl;
}
7.list copy vector copy list
? ? ?代碼測這種性能要把它換成 Release,debug的優化沒有全開的,導致遞歸和循環,次數比較多,差異是比較大的,但是Release的差距不大。
void test_op()
{ // 初始化隨機數生成器,使用當前時間作為種子srand(time(0));// 定義常量N,表示要生成的隨機數個數const int N = 1000000;//創建兩個空的list容器list<int> lt1;list<int> lt2;// 循環N次,生成隨機數并將其添加到兩個list中for (int i = 0; i < N; i++){auto e = rand(); // 生成一個隨機數lt1.push_back(e); // 將隨機數添加到lt1的末尾lt2.push_back(e); // 將相同的隨機數添加到lt2的末尾}// 記錄開始時間,單位為clock ticks(時鐘滴答)int begin1 = clock();//返回程序所消耗的處理器時間。// 創建一個vector,使用lt2的begin和end迭代器初始化,復制lt2的內容vector<int> v(lt2.begin(), lt2.end());// 對vector v 進行排序,使用的是全局的std::sort函數,vector支持隨機訪問迭代器sort(v.begin(),v.end());// 將排序后的vector v的內容重新賦值給lt2,替換lt2當前的內容。lt2.assign(v.begin(),v.end());int end1 = clock(); // 記錄結束時間int begin2 = clock(); // 記錄另一個操作的開始時間lt1.sort(); // 直接對lt1進行排序,使用list容器自帶的sort成員函數int end2 = clock(); // 記錄結束時間// 輸出兩次排序操作所花費的時間(單位為clock ticks)printf("list copy vector sort copy list sort:%d\n",end1 - begin1);printf("list sort:%d\n",end2 - begin2);
}
8.unique and remove
void test_list4()
{list<int> lt;lt.push_back(1);lt.push_back(2);lt.push_back(3);lt.push_back(4);lt.push_back(3);lt.push_back(3);lt.push_back(3);lt.push_back(5);lt.push_back(5);lt.push_back(3);for (auto e : lt){cout << e << " ";}cout << endl;lt.sort();lt.unique();//去掉重復的for (auto e : lt){cout << e << " ";}cout << endl;//lt.remove(30);//刪除不存在的,不會報錯lt.remove(3);for (auto e : lt){cout << e << " ";}cout << endl;
}
8.splice
將元素從x轉移到容器中,并將它們插入位置。
void test_list5()
{list<int> mylist1, mylist2;list<int>::iterator it;for (int i = 1; i <= 4; ++i){mylist1.push_back(i);//mylist1: 1 2 3 4}for (int i = 1; i <= 3; i++){mylist2.push_back(i*10);//mylist2:10 20 30}it = mylist1.begin();++it; //指向mylist1里面的2元素for (auto e : mylist1){cout << e << " ";}cout << endl;for (auto e : mylist2){cout << e << " ";}cout << endl;//mylist1.splice(it,mylist2);//mylist1:1 10 20 30 2 3 4//mylist2(empty)//"it" still points to 2 (the 5th element)//mylist1.splice(it, mylist2, ++mylist2.begin());//mylist:1 20 2 3 4//mylist1: 1 20 30 2 3 4mylist1.splice(it, mylist2, ++mylist2.begin(), mylist2.end());//mylist1:1 20 30 2 3 4 for (auto e : mylist1){cout << e << " ";}cout << endl;for (auto e : mylist2){cout << e << " ";}cout << endl;
}
9?list的迭代器失效
? ? ?前面說過,此處大家可將迭代器暫時理解成類似于指針,迭代器失效即迭代器所指向的節點的無效,即該節點被刪除了。因為list的底層結構為帶頭結點的雙向循環鏈表,因此在list中進行插入時是不會導致list的迭代器失效的,只有在刪除時才會失效,并且失效的只是指向被刪除節點的迭代器,其他迭代器不會受到影響。
pos這個位置使用完畢就失效了:?
改正:
void TestListIterator()
{int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };list<int> l(array, array + sizeof(array) / sizeof(array[0]));auto it = l.begin();while (it != l.end()){l.erase(it++); // it = l.erase(it);}
}
🔧本文修改次數:0
🧭更新時間:2024年 5?月 14?日?