你熟練掌握的STL容器有哪些?(最詳細)

一 vector

1,vector介紹
vector是STL標準庫中的容器,是一個序列式容器,裏面的底層實現是一個順序表結構,可以動態增長長度的數組
2,vector的特性
數據自動初始化爲0,可以動態增長長度,支持隨機訪問數據,對內存邊界進行檢查
3,vector的使用
(1)頭文件
#include <vector>
(2)類的原型

template <typename T>
class vector{
    vector();
    vector(int size);
    vector<int size,fill);
};

(3)定義
vector<類型> 變量名
4,vector的初始化的方法
(1)創建了一個動態數組
vector<int> vec;
(2)創建了一個數組長度爲10
vector<char> vec2(10):
(3)進行聚合初始化
vector<int> vec3 = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

5,vector中的迭代器
(1)begin 指向首地址(有數據,指向第一個元素)
(2)end 指向結束的地址(指向空數據)
vector<int> v = { 50, 92, 13, 54, 15, 46, 87, 18, 19, 20 };
創建一個迭代器對象,指向vector容器中的第一個元素位置
迭代器的頭部
vector<int>::iterator itor = v.begin();
輸出元素的頭部
cout << *itor << endl;
迭代器的尾部
auto end = v.end();
迭代器是一個前閉後開的一個區間 [begin,end)
迭代器的遍歷

for (auto it = v.begin(); it != v.end(); it++)
{
    cout << (*it) << " ";
}

6,vector 兼容C語言
(1)通過data函數返回C語言指針

vector<int> vec(10);
int *p=vec.data();

(2)通過首地址和尾地址,進行拷貝原來的數組

int arr[10] = { 8, 2, 5, 25, 14, 51, 35, 20, 71, 33 };
vector<int> vec(arr,arr+10);
cout << vec << endl;

7,vector常用的函數
(1)vctor 數據的內存分配

push_back 從尾部插入一個數據
pop_back 從尾部刪除一個數據
insert 從指定的位置插入一個元素
erase 從指定的位置刪除(擦除)一個元素
size 獲取當前大小
resize 重新調整大小
assign 重新分配內存空間
capacity 獲取實際的內存空間
clear 清除所有數據

實例

void test()
{
	vector<int> vec = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	cout << vec;
	vec.push_back(11);	
	cout << vec;
	vec.pop_back();
	cout << vec;		
	vec.insert(vec.begin() + 6, 66);
	cout << vec;
	vec.erase(vec.begin() + 2);
	cout << vec;
	cout << "數組容器當前的長度是:" << vec.size() << endl;
	vec.resize(20);	//新添加的數據會自動補上0
	cout << vec;
	cout << "數組容器當前的長度是:" << vec.size() << endl;
	//resize數據比原來小會將後面的數據刪除
	vec.resize(5);
	cout << vec;
	vec.resize(10);
	cout << vec;
	cout << "vec的實際內存是:" << vec.capacity() << endl;
	vec.assign({ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 });
	cout << vec;
	vec.assign(10, 6);
	cout << vec;
	vec.clear();	
	cout << vec;
}

在這裏插入圖片描述
(2)vector數據的訪問操作

front 獲取頭部部元素
back 獲取尾部元素
at 相當於 []
data 返回數組指針
begin 迭代器首地址
end 迭代器結束地址
rbegin 反向迭代器
rend 反向迭代器結束地址

實例

void test()
{
	vector<int> vec = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	cout << vec.front() << endl;  //獲取頭部部元素
	cout << vec.back() << endl;	  //獲取尾部元素
	cout << vec.at(2) << ":" << vec[5] << endl;
	for (auto it = vec.begin(); it != vec.end(); it++)
	{
		cout << (*it) << " ";
	}
	cout << endl;
	for (auto it = vec.rbegin(); it != vec.rend(); it++)
	{
		cout << (*it) << " ";
	}
	cout << endl;
}

在這裏插入圖片描述

二 deque

1,deque介紹
雙端隊列,deque的底層實現是一個鏈表數組,序列式容器
2,deque的使用
(1)頭文件
#include <deque>
(2)定義:
deque<數據類型> 變量名;
3,deque的初始化
(1)定義了一個整型的雙端隊列;
deque<int> que;
deque<int> de = { 1, 2, 3, 4, 5, 6, 7 };
deque<int> de(10);
deque與vector類型,都可以進行數據的訪問,插入和刪除
deque可以進行高效的頭部插入和刪除數據
(2)從頭部添加數據
void push_front();
(3)從頭部刪除數據
void pop_front();
(4)從頭部添加數據
void emplace_front() ;
(5)實例

void test()
{
	deque<int> de = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
	cout << de << endl;
	de.push_back(30);
	cout << de << endl;
	de.pop_back();
	cout << de << endl;
	de.push_front(-1);
	cout << de << endl;
	de.pop_front();
	cout << de << endl;
}

在這裏插入圖片描述
4,deque中的迭代器
(1)begin 指向首地址(有數據,指向第一個元素)
(2)end 指向結束的地址(指向空數據)
(3)rbegin -> rend ,表示的是一個反向跌代器
反向迭代器的頭部指向最後一個元素
deque<int>::reverse_iterator rt = de.rbegin();
(4)實例

for (auto r = de.begin(); r != de.end(); r++)
{
	cout << (*r) << " ";
}	
for (auto r = de.rbegin(); r != de.rend(); r++)
{
	cout << (*r) << " ";
}

5, deque特點
deque是雙端隊列的數據結構,可以在隊首高效的添加和刪除元素,這是相對於vector的優勢。
deque內部採用分段連續的內存空間來存儲元素,在插入元素的時候隨時都可以重新增加一段新的空間並鏈接起來,因此雖然提供了隨機訪問操作,但訪問速度和vector相比要慢。
deque並沒有data函數,因爲deque的元素並沒有放在數組中。
deque因爲沒有固定的內存空間不提供capacity和reserve操作。
6,函數相關

void begin() 將迭代器返回到開頭(增長方向:begin -> end)
void end(); 將迭代器返回到結尾
void rbegin(); 返回反向迭代器以反向開始(增長方向:rbegin -> rend)
void rend(); 將反向迭代器返回到反向結束
void cbegin() 將const_iterator返回到開頭與begin類似,區別在於begin指向的值可以改變,cbegin指向的值不可改變
void cend() 將const_iterator返回到開頭末尾
void crbegin() 返回const_reverse_iterator以反向開始
void crend() 將const_reverse_iterator返回到反向結束
void swap() 交換兩個容器的內容
void emplace() 在迭代器位置插入元素
void emplace_front() 在首部添加一個元素
void emplace_back() 在尾部添加一個元素

實例

void test3()
{
	deque<int> de = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
	cout << de[4] << endl;
	//反向迭代器的頭部指向最後一個元素
	deque<int>::reverse_iterator rt = de.rbegin();
	cout << (*rt) << endl;
	for (auto r = de.begin(); r != de.end(); r++)
	{
		cout << (*r) << " ";
	}
	cout << endl;
	for (auto r = de.rbegin(); r != de.rend(); r++)
	{
		cout << (*r) << " ";
	}
	cout << endl;
	deque<int>::const_iterator ct = de.cbegin();
	cout << (*ct) << endl;	
	//常量反向迭代器
	deque<int>::const_reverse_iterator crt = de.crbegin();
}

在這裏插入圖片描述

三 list

1,list介紹
list是一個雙向鏈表,序列式容器
2,容器屬性
底層實現是一個雙向鏈表
3,list的使用
(1)頭文件
#include <list>
(2)定義
list <類型> 變量名
(3)初始化
創建一個list對象
list<int> l;
可以初始化長度
ist<int> l1(10);
list<int> l2 = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
4,特點
list和deque的元素在內存的組織形式不同,以鏈表的形式保存
list也可以在尾部和頭部加入元素,因此也支持push_back和push_front以及pop_front和push_back
list可以高效的在內部插入元素而不需要移動元素
list獨有的成員函數remove()可以移除和參數匹配的元素。
list刪除數據的時候會馬上回收資源
list在多線程的時候相對比較安全
5,相關函數
相比vector新增函數

void push_front() 從頭部插入數據
void pop_front() 從尾部插入數據
void remove() 刪除元素值
void unique() 刪除連續重複的數值
void merge() 歸併兩個有序鏈表
void sort() 調用list內部的排序算法

沒有at()不能進行內部的隨機訪問,即不支持[] 操作符和at()
list::iterator 沒有 operator+();
list::iterator 沒有 operator+=();
capcity(); 不能獲取空間長度
resize(); 不能修改長度
實例

void test6()
{
	list<int> l = { 1, 2, 3, 16, 8, 13, 2, 22, 30, 5, 4, 10 };
	cout << l;
	l.push_front(66);
	cout << l;
	l.push_back(99);
	cout << l;
	l.remove(3);	
	cout << l;
	list<int> l2 = { 2, 1, 1, 6, 1, 3, 3, 2, 3, 1, 4, 5, 5, 5, 5, 6, 1, 2, 3 };
	cout << l2;
	l2.unique();			//只刪除連續重複的數據
	cout << l2;
	l2.sort();	
	cout << l2 << endl;
	list<int> m1 = { 3, 6, 7, 8, 11 };
	list<int> m2 = { 1, 5, 10 };
	m2.merge(m1);
	cout << m2 << endl;
}

歸併兩個有序鏈表
兩個鏈表必須是有序的
在這裏插入圖片描述

四 迭代器拷貝

1,普通的容器可以進行迭代器進行拷貝
vector<int> vec ={1,2,3,4,5,6,7,8,9,10}
vector<int> vec2(vec.begin(),vec.end());
2,不同的容器之間也可以通過迭代器進行拷貝
(1)聲明一個vector容器
vector<int> vec={1,2,3,4,5,6,7,8,9,10};
​(2)將vector的數據拷貝到list容器中
list<int> li(vec.begin(),vec.end());
(3)將vector的數據拷貝到deque容器中
deque<int> de(vec.begin(),vec.end());
(4)list和deque可以反向的拷貝給vector使用
3,實例

void copy_test()
{
	vector<int> vec = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	vector<int> vec2(vec.begin(), vec.end());
	vector<int> vec3(vec.rbegin(), vec.rend());
	cout <<"vector:"<< vec2 << endl;
	cout << "vector:" << vec3 << endl;
	deque<int> de(vec.begin(), vec.end());
	cout << "deque:" << de << endl;
	list<int> l1(vec.begin(), vec.end());
	cout << "list:" << l1 << endl;
}

在這裏插入圖片描述

五 vector, list 和 deque之間的區別

5.1vector

特點:數據全部順序存儲,允許直接訪問其任何元素,高效的隨機訪問,快速的從尾部添加(刪除)數據,並且可以返回一個C語言指針
底層:是一個地址有序存儲的數組,以連續的地址存儲
缺點:內部插入數據和刪除數據的效率低下,內存大小有限,當該數組後的內存空間不夠時,需要重新申請一塊足夠大的內存並進行內存的拷貝,容量有限

5.2deque

特點:結合了數組和鏈表的兩個特點,既可快速在序列的首尾相對快速進行元素添加(刪除),又可以進行隨機訪問
底層:是一個數組鏈表,每段數組分段存儲在內存中
缺點:訪問速度略比vector要慢,從中間插入(刪除)數據效率低下

5.3list

特點:高效的進行內部的插入和刪除,並能夠真正的從內存中銷燬數據,數據插入和刪除不會影響其他迭代器的訪問
底層:是一個雙向鏈表,節點和節點之間進行鏈接
缺點:不支持隨機訪問,有一定的額外內存

六 map

6.1map

1,map介紹
map 字典 映射 ,map是一個關係式容器 ,以模板(泛型)方式實現 ,底層通常是由一顆紅黑樹組成,第一個可以稱爲鍵(key),第二個可以稱爲該鍵的值(value),在map內部所有的key都是有序的,並且不會有重複的值
2,map的特點
(1)map是一個容器
容器裏面存放元素,把這個元素分成兩個邏輯區塊,第一個稱爲鍵(key) ,每個鍵(key)只能在map中出現一次,並且會進行有序的排列,第二個稱爲該鍵值(value),這兩個區塊當成一個組來進行管理,每一個節點的內容是由一個pair<key,value>構成。
(2)key和value 的條件
key是唯一的,不重複的,裏面的key會自動去除重數據,map會根據key的值自動的進行一個排序,每個key對應着一個value
3,map的使用
map中每個鍵值對稱之爲節點
(1)頭文件
#include <map>
(2)map的存放的節點容器時一個pair的類
原型

template <typename K,typename V>
class pair{
public:
    K first;    //key的值
    V seconed;  //value的值
    pair(K& first,V& seconed): first(first),seconed(seconed){   
    }
}

(3)用法
pair節點通常是通過map返回的一數據類型
pair<T1, T2> p;
pair<T1, T2> p(v1, v2);
pair<T1, T2> p = {v1, v2}
make_pair(v1, v2);
4,初始化方式
(1)定義
map<模板1,模板2> 變量名
(2) 定義一個 m 的空對象
map<key,value> m;
(3)map聚合初始化方式初始化map

map<string,int> mymap = {
   { "abcd", 0 },
   { "efgh", 1 },
   { "hello", 2},
};

5,插入數據
(1)插入鍵值對
m.insert(pair<k,v>(key,value));
(2)該函數會自動識別類型
m.insert(make_pair(key,value));
(3)c++11 新增加的插入方式
m.insert({ key, value });
6,訪問數據
訪問和修改鍵值對
m.at(key);
如果不存在會自動插入一個新的元素進去
m[key]=value;
7,map常見用途
需要建立字符串與整數之間映射關係,通過一個唯一的key來建立存儲關係的
8,迭代器操作
map迭代器不能進行+ 和-運算符操作
可以使用 ++ – 運算符進行遍歷
9,遍歷方式
(1)for循環遍歷

for(map<T1,T>::iterator iter=m.begin();iter!=end;iter++){
      cout<<iter->first<<":"<<iter->seconed<<endl;
}

(2)foreach遍歷

for (auto e:map){
    cout<<e.first<<":"<<e.seconed<<endl;
}

10,map的相關函數

void insert() 插入元素
void erase(iterator iter) 刪除一個元素,參數是迭代器 ,通過find找到key的迭代器,然後刪除
iterator find() 查找一個元素 通過key的值查找 返回一個迭代器 ,如果沒有找到的話會返回 end()
Value at() 通過key訪問或修改數據
Value operator [Key k] 修改數據,訪問數據,插入數據 (不會自動創建)
iterator begin() 返回指向map頭部的迭代器
iterator end() 返回指向map末尾的迭代器
reverse_iterator rbegin() 返回一個指向map尾部的逆向迭代器
reverse_iterator rend() 返回一個指向map頭部的逆向迭代器
int size() 返回map中元素的個數
long max_size() 返回可以容納的最大元素個數
void clear() 刪除所有元素
void count() 返回指定key出現的次數 map中key只會出現一次,所以只會返回1或者是0
void empty() 如果map爲空則返回true
void swap() 交換兩個map
void lower_bound() 返回鍵值>=給定元素的第一個位置
void upper_bound() 返回鍵值>給定元素的第一個位置
void get_allocator() 返回map的配置器
void equal_range() 返回特殊條目的迭代器對

實例

void text()
{
	map<string, int> m =
	{
		{ "小明", 5 },
		{ "張三", 7 },
		{ "小麗", 23 },
		{ "小紅", 1 },
		{ "李四", 15 },
		{ "小兵", 45 },
		{ "小明", 21 },
		{ "小李", 13 },
	};
	m.insert({ "今天", 35 });
	for (auto it = m.begin(); it != m.end(); it++)
	{
		cout << it->first << "->" << it->second << endl;
	}
	cout << endl;
	m.erase(m.begin());
	auto tmp = m.find("小明");
	cout << tmp->first << "-" << tmp->second << endl;
	m.erase(m.find("小李"));
	cout << m << endl;
	auto it = m.find("www");
	{
		m.erase(it);
	}
	else
	{
		cout << "該Key的值不存在" << endl;
	}
	cout << m.at("小兵") << endl;		//訪問數據
	m.at("小明") = 89;						//修改數據
	cout << m.at("小明") << endl;
	for (auto it = m.rbegin(); it != m.rend(); it++)
	{
		//迭代器指向的是一個類的話,用 -> 運算符解引用
		cout << it->first << "->" << it->second << endl;
	}
	cout << "map有" << m.size() << "個節點" << endl;
	cout << m.max_size() << endl;
	cout << m << endl;
	cout << m.count("小明") << endl;
	cout << m.count("小李") << endl;
	m.insert({ "小李", 57 });
	cout << boolalpha << m.empty() << endl;
	map<string, int> m2 = {
		{ "C++", 222 },
		{ "QT", 333 },
		{ "數據庫", 777 },
		{ "數據結構", 666 },
	};
	//交換兩個容器內的數據
	m2.swap(m);
	cout << m;
	auto it2 = m2.lower_bound("222");
	cout << it2->first << "->" << it2->second << endl;
}

在這裏插入圖片描述
11,map的優缺點
(1)優點:
有序性:map結構的紅黑樹自身是有序的,不停的插入數據,總是獲取到一個有序的數據。時間複雜度低:內部結構時紅黑樹,紅黑樹很多操作都是在log(n)的時間複雜度下實現的,因此效率高。
(2)缺點:空間佔有率高:因爲map內部實現是紅黑樹,每一個節點都需要額外保存,這樣使得每一個節點都佔用大量的空間。

6.2multimap

1,多表映射
多表映射的結構與map 相似,但是多表映射可以允許有多個重複的鍵值
2,特點:,
key的值可以重複
key的值仍然會進行排序
可以用做多個key時使用,由於會排序,所有key的位置都是連續的並排下去
3,頭文件
#include <map>
4,在無序映射中不能進行直接的鍵值操作
不可以直接通過鍵的值進行訪問
int count() 返回指定key出現的次數,multimap中key會出現多次
5,實例

void test()
{
	multimap<int, string> mm = {
		{ 1, "c語言" },
		{ 2, "C++" },
		{ 5, "數據結構" },
		{ 7, "操作系統" },
		{ 3, "計算機網絡" },
		{ 9, "QT" }
	};
	mm.insert({ 3, "編譯原理" });
	mm.insert({ 6, "數據庫" });
	mm.insert({ 6, "計算機原理" });
	int i = 0;
	for (auto it = mm.find(6); i < mm.count(6); i++, it++)
	{
		cout << "[" << it->first << "]" << it->second << endl;
	}
	mm.erase(mm.find(6));
	for (auto p : mm)
	{ 
		cout << p.first << ":" << p.second << endl;
	}
}

在這裏插入圖片描述

6.3unordered_map

1,無序映射
2,頭文件
#include<unordered_map>
c++11以下的的版本使用
#include <hash_map>
3,特點:
無序映射的key的值不會進行排序,但是會去除重複的key值
優點:內部結構是哈希表,查找爲O(1),效率高.
缺點:哈希表的建立耗費時間。
應用場景:對於頻繁查找的問題,用unordered_map更高效.
4,底層:是一個哈希表完成的
unordered_map 函數操作基本上與map的值一致

void bucket_count() 回槽(Bucket)數
void bucket_size() 返回槽大小
void bucket() 返回元素所在槽的序號
void rehash() 設置槽數
void reserve() 請求改變容器容量

5,實例

void test()
{
	unordered_map<int, string > um = {
		{ 3, "123" },
		{ 6, "abc" },
		{ 1, "456" },
		{ 2, "789" },
		{ 3, "hello" },
		{ 2, "haha" }
	};
	for (auto p : um)
	{
		cout << p.first << ":" << p.second << endl;
	}
}

在這裏插入圖片描述

6.4unordered_multimap

1,無序多表映射
2,特點
該容器底層是哈希表實現,裏面的key既不會去重,也不會排序,用法與unordered_map類似,雖然不會排序,但重複的數據也會排列在一起
3,實例

void test()
{
	unordered_multimap<int, int> umm = {
		{ 1, 0 }, { 0, 1 },
		{ -1, 0 },{ 1, 0 }, 
		{ 0, 1 },{ -1, 0 },
		{ 999, 234 },{ 80, 56 },
	};
	for (auto p : umm)
	{
		cout << p.first << ":" << p.second << endl;
	}
}

在這裏插入圖片描述

七 set 集合

1,集合內的元素是不重複的 ,類似map中的key元素,但是沒有對應的value
2,定義
set<類型> s;
3,相關函數

s.inert() 插入元素
s.erase() 刪除元素
s.clear() 清空集合
s.size() 集合元素的個數
s.empty() 判斷集合是否爲空

4,set 對應map 去重排序

set<int> s = { 5, 8, 3, 6, 9, 0, 4, 1, 2, 7, 11, 15, 22, 88};
for (auto e : s)
{
  cout << e << " ";
} 
cout << endl;
    

在這裏插入圖片描述

八 總結

學習容器的基本用法,知道容器的特性,容器之間的區別以及在什麼情況下該使用哪個容器。我總結了兩天半,太累了。
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章