STL容器
整理博客不易,如果本文對你有所幫助,請給博主 點贊 + 收藏 + 關注!謝謝!
1、容器分類
STL中通常將容器分爲三類:順序容器、關聯容器和容器適配器。
1、順序容器
是一種各元素之間有順序關係的線性表,是一種線性結構的可序羣集。順序性容器中的每個元素均有固定的位置,除非用刪除或插入的操作改變這個位置。順序容器的元素排列次序與元素值無關,而是由元素添加到容器裏的次序決定。
順序容器包括:
vector(向量)、list(列表)、deque(隊列)。
2、關聯容器
關聯式容器是非線性的樹結構,更準確的說是二叉樹結構。排序的容器底層是通過紅黑樹實現的,散列的是通過哈希表實現的。
關聯容器可分爲兩類:
(1)有序的:map(集合)、set(映射)、multimap(多重集合)、multiset(多重映射)。
(2)無序的:unordered_map、unordered_set、unordered_multimap、unordered_multiset
3、容器適配器
C++提供了三種容器適配器(container adapter):stack,queue和priority_queue。stack和queue基於deque實現,priority_queue基於vector實現。
適配器包括:
stack(棧) 、queue(隊列) 、priority_queue(優先級隊列) 。
容器類自動申請和釋放內存,因此無需new和delete操作。
2、順序型容器
2.1 vector容器
1、簡介
頭文件 #include <vector>
vector是STL提供的動態數組,數組的大小可以動態的變化,類似與一個線性數組,索引效率高,插入,刪除的效率很低,需要遍歷數據列表。
2、優缺點:
優點:支持隨機訪問,即 [] 操作和 .at(),查詢效率高。
缺點:當向頭部或中部,插入或刪除元素,插入效率低。
3、插入、刪除的時間複雜度:
4、定義與初始化
vector<int> vec1; //默認初始化,vec1爲空
vector<int> vec2(vec1); //使用vec1初始化vec2
vector<int> vec3(vec1.begin(),vec1.end());//使用vec1初始化vec2
vector<int> vec4(10); //10個值爲0的元素
vector<int> vec5(10,4); //10個值爲4的元素
vector<string> vec6(10,"null"); //10個值爲null的元素
vector<string> vec7(10,"hello"); //10個值爲hello的元素
5、常用的操作方法
vec1.push_back(100); //添加元素
int size = vec1.size(); //元素個數
bool isEmpty = vec1.empty(); //判斷是否爲空
cout<<vec1[0]<<endl; //取得第一個元素
vec1.insert(vec1.end(),5,3); //從vec1.back位置插入5個值爲3的元素
vec1.pop_back(); //刪除末尾元素
vec1.erase(vec1.begin(),vec1.end());//刪除之間的元素,其他元素前移
cout<<(vec1==vec2)?true:false; //判斷是否相等==、!=、>=、<=...
vector<int>::iterator iter = vec1.begin(); //獲取迭代器首地址
vector<int>::const_iterator c_iter = vec1.begin(); //獲取const類型迭代器
vec1.clear(); //清空元素
6、遍歷方法
//下標法(vector的特有訪問方法,一般容器只能通過迭代器訪問)
int length = vec1.size();
for(int i=0;i<length;i++) {
cout<<vec1[i]<<endl;
}
//迭代器法
vector<int>::const_iterator iterator = vec1.begin();
for(;iterator != vec1.end();iterator++) {
cout<<*iterator<<endl;
}
2.2 list容器
1、簡介
頭文件 #include <list>
由 deque 實現而成,元素也存放在堆中。設計目的是令容器任何位置的添加和刪除操作都很快速,作爲代價不支持元素的隨機訪問——爲了訪問一個元素,只能遍歷整個容器。
2、優缺點:
優點:內存不連續,動態操作,可在任意位置插入或刪除且效率高。
缺點:不支持隨機訪問。
3、插入、刪除的時間複雜度:
4、定義與初始化
list<int> lst1; //創建空list
list<int> lst2(3); //創建含有三個元素的list
list<int> lst3(3,2); //創建含有三個元素的值爲2的list
list<int> lst4(lst2); //使用lst2初始化lst4
list<int> lst5(lst2.begin(),lst2.end()); //同lst4
5、常用的操作方法
lst1.assign(lst2.begin(),lst2.end()); //分配值
lst1.push_back(10); //添加值
lst1.pop_back(); //刪除末尾值
lst1.begin(); //返回首值的迭代器
lst1.end(); //返回尾值的迭代器
lst1.clear(); //清空值
bool isEmpty1 = lst1.empty(); //判斷爲空
lst1.erase(lst1.begin(),lst1.end()); //刪除元素
lst1.front(); //返回第一個元素的引用
lst1.back(); //返回最後一個元素的引用
lst1.insert(lst1.begin(),3,2); //從指定位置插入3個值爲2的元素
lst1.rbegin(); //返回第一個元素的前向指針
lst1.remove(2); //相同的元素全部刪除
lst1.reverse(); //反轉
lst1.size(); //含有元素個數
lst1.sort(); //排序
lst1.unique(); //刪除相鄰重複元素
6、遍歷方法
//迭代器法
for(list<int>::const_iterator iter = lst1.begin();iter != lst1.end();iter++) {
cout<<*iter<<endl;
}
2.3 deque容器
1、簡介
頭文件 #include <deque>
deque容器類與vector類似,支持隨機訪問和快速插入刪除,它在容器中某一位置上的操作所花費的是線性時間。與vector不同的是,deque還支持從開始端插入數據:push_front()。其餘類似vector操作方法的使用。
2、優缺點:
優點:支持隨機訪問,即 [] 操作和 .at(),查詢效率高;當向兩端,插入或刪除元素,插入效率高。
缺點:當向中部,插入或刪除元素,插入效率低。
3、插入、刪除的時間複雜度:
4、定義與初始化
deque<int> c ; //產生一個空的deque,其中沒有任何元素
deque<int> c1(c2); //產生另一個同型deque的副本(所有元素都被拷貝)
deque<int> c(n) ; //產生一個大小爲n的deque
deque<int> c(n , elem) ; //產生一個大小爲n的deque,每個元素值都是elem。
dequer<int> c(begin,end); //產生一個deque,以區間[begin ; end]做爲元素初值
5、常用的操作方法
c.size(); //返回當前的元素數量
c.empty(); //判斷大小是否爲零。等同於c.size() == 0,但可能更快
c.max_size(); //可容納元素的最大數量
c.at(idx) ; //返回索引爲idx所標示的元素。如果idx越界,拋出out_of_range
c[idx] ; //返回索引idx所標示的元素。不進行範圍檢查
c.front() ; //返回第一個元素,不檢查元素是否存在
c.back(); //返回最後一個元素
c.begin(); //返回一個隨機迭代器,指向第一個元素
c.end(); //返回一個隨機迭代器,指向最後元素的下一位置
c1 = c2 ; //將c2的所有元素賦值給c1;
c.assign(n , elem); //將n個elem副本賦值給c
c.assing(beg , end); //將區間[beg;end]中的元素賦值給c;
c.push_back(elem); //在尾部添加元素elem
c.pop_back() ; //移除最後一個元素(但不回傳)
c.push_front() ; //在頭部添加元素elem
c.pop_front() ; //移除頭部一個元素(但不回傳)
c.erase(pos) ; //移除pos位置上的元素,返回一元素位置,如 c.erase( c.begin() + 5)移除第五個元素
c.insert(pos , elem); //在pos位置插入一個元素elem,並返回新元素的位置
c.insert(pos , n , elem); //在pos位置插入n個元素elem,無返回值
c.insert(pos , beg , end);
c.resize(num); //將容器大小改爲num。可更大或更小。
c.resize(num , elem); //將容器大小改爲num,新增元素都爲 elem
c.clear(); //移除所有元素,將容器清空
3、有序關聯容器
3.1 set(集合)和 multiset(多重集合)
multiset 和 set用法幾乎一致,只是multiset可以允許值重複而已,在此就不重複介紹了。
1、簡介
頭文件 #include <set>
set(集合):由紅黑樹實現,其中每個元素只包含一個關鍵字並依據其值自動排序,支持高效的關鍵字查詢操作,每個元素值只能出現一次,不允許重複。插入和刪除效率比用其他序列容器高,因爲對於關聯容器來說,不需要做內存拷貝和內存移動。
multiset(多重集合):唯一的區別是插入的元素可以相同。
2、優缺點:
優點:關鍵字查詢高效,且元素唯一,以及能自動排序。
缺點:每次插入值的時候,都需要調整紅黑樹,效率有一定影響。
3、插入、刪除的時間複雜度:
4、定義與初始化
set<int> iset(ivec.begin(), ivec.end()); // 根據迭代器範圍內的元素初始化
set<int> set1; // 創建一個空的set集合
5、常用的操作方法
set1.insert("the"); //第一種方法:直接添加
iset2.insert(ivec.begin(), ivec.end());//第二種方法:通過指針迭代器
set1.size(); //元素個數
set1.empty(); //判斷空
set1.find(); // 返回一個迭代器,此迭代器指向集合中其值與指定值相等的元素的位置。
set1.begin(); // 返回一個迭代器,此迭代器指向集合中的第一個元素。
set1.end(); // 返回超過末尾迭代器。
set1.cbegin(); // 返回一個常量迭代器,此迭代器指向集合中的第一個元素。
set1.cend(); // 返回一個超過末尾常量迭代器。
6、遍歷方法
//正向迭代器
for(set<int>::iterator iter = set1.begin();iter!=set1.end();iter++) {
cout << *iter << endl;
}
//反向迭代
for(set<int>::iterator iter = set1.rbegin();iter!=set1.rend();iter++) {
cout << *iter << endl;
}
7、刪除方法
set1.erase(iterator it); //刪除迭代器指向的對象
set1.erase(iterator first,iterator last); //刪除first到last迭代器範圍中的內容
set1.erase(value); //通過值刪除
set1.clear(); // 清空所有元素,相對於set1.erase(set1.begin(),set1.end());
3.2 map(映射)和multimap(多重映射)
multimap 和 map用法幾乎一致,只是multimap可以允許值重複而已,在此就不重複介紹了。
1、簡介
頭文件 #include <map>
map(映射):由紅黑樹實現,其中每個元素都是一些 鍵值對(key-value):關鍵字起索引作用,值表示與索引相關聯的數據。每個元素有一個鍵,是排序準則的基礎。每一個鍵只能出現一次,不允許重複。插入和刪除效率比用其他序列容器高,因爲對於關聯容器來說,不需要做內存拷貝和內存移動。
multimap(多重映射):唯一的區別是插入的元素(值)可以相同,即同一個鍵可以對應多個值。
2、優缺點:
優點:關鍵字查詢高效,且元素唯一,以及能自動排序。把一個值映射成另一個值,可以創建字典。
缺點:每次插入值的時候,都需要調整紅黑樹,效率有一定影響。
3、插入、刪除的時間複雜度:
4、定義與初始化
map<k, v>m // 創建一個名爲m的空map對象,其鍵和值的類型分別爲k和v
map<k, v> m(m2) // 創建m2的副本m,m與m2必須有相同的鍵類型和值類型
map<k, v> m(b, e) // 創建map類型的對象m,存儲迭代器b和e標記的範圍內所有元素的副本。元素的類型必須能轉換爲pair<const k, v>
5、常用的操作方法
// 插入元素
map1[3] = "Saniya"; //添加一個鍵爲 3,值爲"Saniya"的鍵-值對
map1.insert(map<int,string>::value_type(2,"Diyabi"));//插入元素
map1.insert(pair<int,string>(1,"Siqinsini")); //插入一個pair類型的鍵-值對
map1.insert(make_pair<int,string>(4,"V5")); //通過make_pair插入一個pair類型的鍵-值對
map1.insert(iterator first,iterator last); // 插入另一個map在迭代器範圍內的數據
//其他
map<int,string>::iterator iter_map = map1.begin();//取得迭代器首地址
int key = iter_map->first; //取得key
string value = iter_map->second; //取得value
map1.size(); //元素個數
map1.empty(); //判斷空
map1.find(); // 返回一個迭代器,此迭代器指向映射中其鍵與指定鍵相等的元素的位置。
map1.begin(); // 返回一個迭代器,此迭代器指向映射中的第一個元素。
map1.end(); // 返回超過末尾迭代器。
map1.cbegin(); // 返回一個常量迭代器,此迭代器指向映射中的第一個元素。
map1.cend(); // 返回一個超過末尾常量迭代器。
map1.count(); // 返回映射中其鍵與參數中指定的鍵匹配的元素數量。
6、遍歷方法
//正向迭代器
for(map<int,string>::iterator iter = map1.begin();iter!=map1.end();iter++) {
int keyk = iter->first;
string valuev = iter->second;
}
//反向迭代
for(map<int,string>::iterator iter = map1.rbegin();iter!=map1.rend();iter++) {
int keyk = iter->first;
string valuev = iter->second;
}
7、刪除方法
map1.erase(iterator it); //刪除迭代器指向的對象
map1.erase(iterator first,iterator last); //刪除first到last迭代器範圍中的內容
map1.erase(const Key&key); //通過關鍵字刪除
map1.clear(); // 清空所有元素,相對於map1.erase(map1.begin(),map1.end());
4、無序關聯容器
4.1 unordered_map/unordered_multimap
unordered_multimap 和 unordered_map用法幾乎一致,只是unordered_multimap 可以允許值重複而已,在此就不重複介紹了。
1、簡介
頭文件 #include <unordered_map>
unordered_map和map類似,都是存儲的key-value的值,可以通過key快速索引到value。不同的是unordered_map不會根據key的大小進行排序,存儲時是根據key的hash值判斷元素是否相同,即unordered_map內部元素是無序的。unordered_map的底層是一個防冗餘的哈希表(開鏈法避免地址衝突)。
哈希表最大的優點,就是把數據的存儲和查找消耗的時間大大降低,時間複雜度爲O(1);而代價僅僅是消耗比較多的內存。哈希表的查詢時間雖然是O(1),但是並不是unordered_map查詢時間一定比map短,因爲實際情況中還要考慮到數據量。
2、優缺點:
優點: 因爲內部實現了哈希表,因此其查找速度非常的快
缺點: 哈希表的建立比較耗費時間
3、初始化
unordered_map<string, int> map1; map1[string("abc")] = 1; map1["def"] = 2;//創建空map,再賦值
unordered_map<string, int> map2(map1); //拷貝構造
unordered_map<string, int> map3(map1.find("abc"), map1.end()); //迭代器構造
unordered_map<string, int> map4(move(map2)); //移動構造
unordered_map<string, int> map5 {{"this", 100},{"can", 100},};//使用initializer_list初始化
4、常見用法
map1.at("abc"); //查找具有指定key的元素,返回value
map1.find("abc"); //查找鍵爲key的元素,找到返回迭代器,失敗返回end()
map1.count("abc"); //返回指定key出現的次數,0或1
map1.emplace(make_pair("str1", 1)); //使用pair的轉換移動構造函數,返回pair<unordered_map<string, int>::iterator, bool>
map1.emplace("str2", 2); //使用pair的模板構造函數,如果key已經存在則什麼都不做
map1.insert(pair<string ,int>("sex", 1));//插入元素,返回pair<unordered_map<string, int>::iterator, bool>
map1.insert(unordered_map<string, int>::value_type("sex", 1));//插入元素,如果key已經存在則什麼都不做
map1.insert(make_pair("sex", 1));//插入元素,返回pair<map<string, int>::iterator, bool>,插入成功second爲true,失敗爲flase
map1.insert({"sex", 1}); //使用initializer_list插入元素
map1.insert(map1.end(), {"sex", 1});//指定插入位置,如果位置正確會減少插入時間
map1.insert(map2.begin(), map2.end());//使用範圍迭代器插入
map1.erase("abc"); //刪除操作,成功返回1,失敗返回0
map1.erase(map1.find("abc")); //刪除操作,成功返回下一個pair的迭代器
map1.erase(map1.begin(), map1.end()); //刪除map1的所有元素,返回指向end的迭代器
map1.empty(); //是否爲空
map1.size(); //大小
map1.bucket_count(); //返回容器中的桶數
map1.bucket_size(1); //返回1號桶中的元素數
map1.bucket("abc"); //abc這個key在哪一個桶
map1.load_factor(); //負載因子,返回每個桶元素的平均數,即size/float(bucket_count);
map1.max_load_factor();//返回最大負載因子
map1.max_load_factor(2);//設置最大負載因子爲2,rehash(0)表示強制rehash
map1.rehash(20);//設置桶的數量爲20,並且重新rehash
map1.reserve(20);//將容器中的桶數設置爲最適合元素個數,如果20大於當前的bucket_count乘max_load_factor,則增加容器的bucket_count並強制重新哈希。如果20小於該值,則該功能可能無效。
unordered_map<string, int>::iterator it = map1.begin(); //返回指向map1首元素的迭代器
unordered_map<string, int>::const_iterator c_it = map1.cbegin(); //返回指向map1首元素的常量迭代器
unordered_map<string, int>::local_iterator it = map1.begin(1);//返回1號桶中的首元素迭代器
unordered_map<string, int>::const_local_iterator c_it = map1.cbegin(1);//返回1號桶中的首元素的常量迭代器
pair<unordered_map<string, int>::iterator, unordered_map<string, int>::iterator> it = map1.equal_range("abc");//返回一個pair,pair裏面第一個變量是lower_bound返回的迭代器,第二個迭代器是upper_bound返回的迭代器
map1.clear(); //清空
5、示例
4.2 unordered_set/unordered_multiset
unordered_multiset 和 unordered_set 用法幾乎一致,只是unordered_multiset 可以允許值重複而已,在此就不重複介紹了。
1、簡介
頭文件 #include <unordered_set>
unordered_set基於哈希表,數據插入和查找的時間複雜度很低,幾乎是常數時間,而代價是消耗比較多的內存,無自動排序功能。底層實現上,使用一個下標範圍比較大的數組來存儲元素,形成很多的桶,利用hash函數對key進行映射到不同區域進行保存。
2、優缺點:
優點: 因爲內部實現了哈希表,因此其查找速度非常的快
缺點: 哈希表的建立比較耗費時間
3、初始化
unordered_set<int> set1; //創建空set
unordered_set<int> set2(set1); //拷貝構造
unordered_set<int> set3(set1.begin(), set1.end()); //迭代器構造
unordered_set<int> set4(arr,arr+5); //數組構造
unordered_set<int> set5(move(set2)); //移動構造
unordered_set<int> set6 {1,2,10,10};//使用initializer_list初始化
4、常見用法
set1.find(2); //查找2,找到返回迭代器,失敗返回end()
set1.count(2); //返回指2出現的次數,0或1
set1.emplace(3); //使用轉換移動構造函數,返回pair<unordered_set<int>::iterator, bool>
set1.insert(3); //插入元素,返回pair<unordered_set<int>::iterator, bool>
set1.insert({1,2,3}); //使用initializer_list插入元素
set1.insert(set1.end(), 4);//指定插入位置,如果位置正確會減少插入時間,返回指向插入元素的迭代器
set1.insert(set2.begin(), set2.end());//使用範圍迭代器插入
set1.erase(1); //刪除操作,成功返回1,失敗返回0
set1.erase(set1.find(1)); //刪除操作,成功返回下一個pair的迭代器
set1.erase(set1.begin(), set1.end()); //刪除set1的所有元素,返回指向end的迭代器
set1.empty(); //是否爲空
set1.size(); //大小
set1.bucket_count(); //返回容器中的桶數
set1.bucket_size(1); //返回1號桶中的元素數
set1.bucket(1); //1在哪一個桶
set1.load_factor(); //負載因子,返回每個桶元素的平均數,即size/float(bucket_count);
set1.max_load_factor();//返回最大負載因子
set1.max_load_factor(2);//設置最大負載因子爲2,rehash(0)表示強制rehash
set1.rehash(20);//設置桶的數量爲20,並且重新rehash
set1.reserve(20);//將容器中的桶數設置爲最適合元素個數,如果20大於當前的bucket_count乘max_load_factor,則增加容器的bucket_count並強制重新哈希。如果20小於該值,則該功能可能無效。
unordered_set<int>::iterator it = set1.begin(); //返回指向set1首元素的迭代器
unordered_set<int>::const_iterator c_it = set1.cbegin(); //返回指向set1首元素的常量迭代器
unordered_set<int>::local_iterator it = set1.begin(1);//返回1號桶中的首元素迭代器
unordered_set<int>::const_local_iterator c_it = set1.cbegin(1);//返回1號桶中的首元素的常量迭代器
pair<unordered_set<int>::iterator, unordered_set<int>::iterator> it = set1.equal_range(1);//返回一個pair,pair裏面第一個變量是lower_bound返回的迭代器,第二個迭代器是upper_bound返回的迭代器
set1.clear(); //清空
5、容器適配器
5.1 stack容器
1、簡介
頭文件 #include <stack>
C++ Stack(堆棧) 是一個容器類的改編,爲程序員提供了堆棧的全部功能,——也就是說實現了一個先進後出(FILO)的數據結構。
2、插入、刪除的時間複雜度:
3、常用的操作方法
s1.empty() //堆棧爲空則返回真
s1.pop() //移除棧頂元素
s1.push() //在棧頂增加元素
s1.size() //返回棧中元素數目
s1.top() //返回棧頂元素
5.2 queue容器
1、簡介
頭文件 #include <queue>
queue具有先進先出的數據結構。持新增元素、移除元素、從最底端加入元素、取最頂端元素。
2、插入、刪除的時間複雜度:
3、常用的操作方法
q.push(x); //入隊,將x元素接到隊列的末端;
q.pop(); //出隊,彈出隊列的第一個元素,並不會返回元素的值;
q.front(); //訪問隊首元素
q.back(); //訪問隊尾元素
q.size(); // 訪問隊中的元素個數
q.empty(); //判斷隊列是否爲空
5.3 priority_queue容器
1、簡介
頭文件 #include <queue>
priority_queue實現了優先隊列這一ADT,也就是我們常說的堆。但要明晰的是,優先隊列是一種ADT,而堆是它的一種具體實現。在默認狀態下,priority_queue實現的是大根堆,但你可以通過模板特化從而實現小根堆。
定義:priority_queue<Type, Container, Functional>
Type 就是數據類型,Container 就是容器類型(Container必須是用數組實現的容器,比如vector,deque等等,但不能用 list。STL裏面默認用的是vector),Functional 就是比較的方式。
當需要用自定義的數據類型時才需要傳入這三個參數,使用基本數據類型時,只需要傳入數據類型,默認是大頂堆,如下所示:
//升序隊列,小頂堆
priority_queue <int,vector<int>,greater<int> > q;
//降序隊列,大頂堆
priority_queue <int,vector<int>,less<int> >q;
//greater和less是std實現的兩個仿函數(就是使一個類的使用看上去像一個函數。
//其實現就是類中實現一個operator(),這個類就有了類似函數的行爲,就是一個仿函數類了)
2、插入、刪除的時間複雜度:
3、常用的操作方法
q.push(x); //入隊,將x元素接到隊列的末端,(並排序)
q.pop(); //出隊,彈出隊列的第一個元素,並不會返回元素的值;
q.front(); //訪問隊首元素
q.back(); //訪問隊尾元素
q.size(); // 訪問隊中的元素個數
q.empty(); //判斷隊列是否爲空
整理博客不易,如果本文對你有所幫助,請給博主 點贊 + 收藏 + 關注!謝謝!
參考:https://blog.csdn.net/u014465639/article/details/70241850
https://blog.csdn.net/like_that/article/details/98446479
https://blog.csdn.net/weixin_43844677/article/details/104902417
https://blog.csdn.net/zhuikefeng/article/details/104738544