C++常用容器
零、前言
C++面試中經常會被問到容器的使用,面試官會從它們的使用場景、效率、使用方法、各個容器之間的區別等多方面進行考察,C++中的容器都是使用模板封裝的,底層設計又設計到算法的使用,從容器的使用問起,能夠迅速擴展開話題,並能夠試探出應聘者的水平到底有多深!
剛參加工作的面試者,可能只需要掌握怎麼使用就行了,有一定工作經驗的不僅要掌握怎麼使用,還要掌握容器的區別和實現原理,更要掌握它們的使用場景,根據它們的各自特點,讓它們在特定的場景中發揮自己的長處!
一、vector
1、vector概述
vector屬於序列式容器,實現了對數組的封裝,因此它的內存空間也是連續存儲的, 支持下標訪問,可以通過[]和at()來進行隨機訪問vector中的元素,由於空間是連續存儲的,vector的訪問速度極快;但是對插入支持不是很友好,在尾部插入速度很快,在頭部插入元素速度很慢,需要移動所有的數據。
2、vector優缺點
優點:
1)使用連續的存儲空間,訪問速度快。
2) 支持隨機訪問,可以通過[ ]操作符和at()方法訪問數據。
3)可以不指定內存大小即可實現對像數組一樣的操作,實際內部實現vector是預先分配了一塊固定大小的內存,當超過該內存塊時,vector會重新找一塊更大的內存,並把當前的釋放掉。
(之前寫過兩篇文章對vector內存進行過剖析,感興趣可以看看:
文章1:https://blog.csdn.net/toby54king/article/details/86737201,
文章2:https://blog.csdn.net/toby54king/article/details/88543270)
4)可以快速的在尾部進行插入和刪除,即通過push_back() 和pop_back()方法。
缺點:
1)vector內部進行插入刪除操作效率低。
2) 只能在vector的尾部進行push和pop。
3) 添加數據佔用內存超過vector預先分配的大小時,需要重新分配、拷貝與釋放。
3、vector代碼示例
// TestVector.cpp : 此文件包含 "main" 函數。程序執行將在此處開始並結束。
//
#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>
using namespace std;
// 打印函數
void MyPrintf(int &elem)
{
cout << elem << ", ";
}
// 修改元素函數
void MyAdd(int &elem)
{
elem = elem + 10;
}
int main()
{
cout << "------------------------C++序列化容器vector的使用--------------------" << endl;
// 1、增加元素
cout << "-----------------------vector增加元素--------------------" << endl;
vector<int> myVector;
for (int i=0; i<10; i++)
{
//myVector.push_back(i);
myVector.emplace_back(i); // C++11
}
for_each(myVector.begin(),myVector.end(), MyPrintf);
cout << endl;
// 2、排序元素
cout << "-----------------------vector排序元素--------------------" << endl;
sort(myVector.begin(), myVector.end(), greater<int>());
for_each(myVector.begin(), myVector.end(), MyPrintf);
cout << endl;
// 3、修改元素
cout << "-----------------------vector修改元素--------------------" << endl;
for_each(myVector.begin(), myVector.end(), MyAdd);
for_each(myVector.begin(), myVector.end(), MyPrintf);
cout << endl;
// 4、查找元素
cout << "-----------------------vector查找元素--------------------" << endl;
vector<int>::iterator index;
index = find(myVector.begin(), myVector.end(), 10); // 查找元素10
// 注意:元素的索引從0開始的
cout << "元素10的索引index是: " << (index - myVector.begin()) << endl;
index = find_if(myVector.begin(), myVector.end(), bind2nd(less<int>(),15));
cout << "第一個小於15的元素索引index是: " << (index - myVector.begin()) << endl;
// 5、刪除元素
cout << "-----------------------vector刪除元素--------------------" << endl;
while (!myVector.empty())
{
int backNum = myVector.back();
cout << backNum << ", ";
myVector.pop_back();
}
cout << endl;
std::cout << "Hello World!\n";
getchar();
}
運行結果:
二、list
1、list概述
list屬於序列式容器,實現了對雙向鏈表的封裝,因此它的內存空間是不連續的,不支持下標訪問,list對於隨機訪問速度慢,可能要遍歷整個鏈表纔行,但是插入速度很快,不需要拷貝和移動數據,只需要改變指針的指向就可以了。
2、list優缺點
優點:
1)不使用連續存儲空間來完成動態操作。
2)內部進行插入和刪除操作非常方便。
3)可以在兩端進行插入和刪除操作。
4])不需要預先分配內存空間。
缺點:
1)不能進行內部的隨機訪問,因爲不支持下標索引。
2)比verctor佔用內存多,因爲要存儲其前驅和後繼節點的信息。
3、list代碼示例
// Testlist.cpp : 此文件包含 "main" 函數。程序執行將在此處開始並結束。
//
#include <iostream>
#include <list>
#include <algorithm>
#include <functional>
using namespace std;
// 打印函數
void MyPrintf(int &elem)
{
cout << elem << ", ";
}
// 修改元素函數
void MyAdd(int &elem)
{
elem = elem + 10;
}
int main()
{
cout << "------------------------C++序列化容器list的使用--------------------" << endl;
// 1、增加元素
cout << "-----------------------list增加元素--------------------" << endl;
list<int> myList;
for (int i = 0; i < 10; i++)
{
//myList.push_back(i);
myList.emplace_back(i); // C++11
}
for_each(myList.begin(), myList.end(), MyPrintf);
cout << endl;
// 2、排序元素
cout << "-----------------------list排序元素--------------------" << endl;
myList.sort(greater<int>());
for_each(myList.begin(), myList.end(), MyPrintf);
cout << endl;
// 3、修改元素
cout << "-----------------------list修改元素--------------------" << endl;
for_each(myList.begin(), myList.end(), MyAdd);
for_each(myList.begin(), myList.end(), MyPrintf);
cout << endl;
// list反序排列
cout << "-----------------------list反序排列元素--------------------" << endl;
myList.reverse();
for_each(myList.begin(), myList.end(), MyPrintf);
cout << endl;
// 4、查找元素
cout << "-----------------------list查找元素--------------------" << endl;
list<int>::iterator index;
index = find(myList.begin(), myList.end(), 10); // 查找元素10
if (index != myList.end())
{
cout << "找到元素10了,刪除該元素" << endl;
myList.remove(10);
}
for_each(myList.begin(), myList.end(), MyPrintf);
cout << endl;
index = find_if(myList.begin(), myList.end(), bind2nd(greater<int>(), 15));
if (index != myList.end())
{
cout << "第一個大於15的元素找到了" << endl;
}
// 5、刪除元素
cout << "-----------------------list刪除元素--------------------" << endl;
myList.remove_if(bind2nd(equal_to<int>(), 15)); // 刪除等於15的元素
cout << "-----------------------list刪除等於15的元素後--------------------" << endl;
for_each(myList.begin(), myList.end(), MyPrintf);
cout << endl;
cout << "刪除所有的元素" << endl;
while (!myList.empty())
{
int backNum = myList.back();
cout << backNum << ", ";
myList.pop_back();
}
cout << endl;
std::cout << "Hello World!\n";
getchar();
}
運行結果:
三、map
1、map概述
map是採用紅黑樹實現的key-value一對一映射的關聯容器,紅黑樹能夠自動對數據進行排序,因此map內部所有的數據都是有序的;作爲鍵值key的元素不允許重複,比較函數只對元素的鍵值進行比較,元素的各項數據可以通過鍵值進行查找、重新賦值等操作;可以使用鍵值key作爲下標來獲取對應的值;map默認的排序準則是從小到大。
2、map優缺點
優點:
1)map的數據結構採用紅黑樹自身是有序的,插入數據時會自動排序。
2)內部結構採用紅黑樹,紅黑樹的時間複雜度是在log(n),時間複雜度低,效率高。
3)索引方便,可以通過下標key方便的進行存取。
缺點:
1)map內部採用紅黑數實現,需要額外空間保存節點,會佔用多餘的空間,典型的以“空間換時間”。
2)通過key值索引時,如果key值不存在,map會直接插入一個key-value鍵值對。
3、map代碼示例
// Testmap.cpp : 此文件包含 "main" 函數。程序執行將在此處開始並結束。
//
#include <iostream>
#include <map>
#include <vector>
#include <algorithm>
#include <functional>
using namespace std;
// 打印函數
void MyPrintf(pair<int, double> mpair)
{
cout << "( " << mpair.first << "," << mpair.second << ") " << endl;
}
// 以value值進行排序
bool CompVector(pair<int, double> mpair1, pair<int, double> mpair2)
{
return mpair1.second > mpair2.second;
}
int main()
{
cout << "------------------------C++關聯容器map的使用--------------------" << endl;
// 1、增加元素
cout << "-----------------------map增加元素--------------------" << endl;
map<int, double> myMap;
for (int i = 0; i < 10; i++)
{
double fNum = i + 0.5;
myMap.insert(pair<int, double>(i,fNum));
}
for_each(myMap.begin(), myMap.end(), MyPrintf);
cout << endl;
// 2、排序元素
cout << "------map排序元素,map沒有提供直接排序的函數,需要藉助vector排序-------" << endl;
vector<pair<int, double> >mapVector(myMap.begin(), myMap.end());
sort(mapVector.begin(), mapVector.end(), CompVector);
for_each(mapVector.begin(), mapVector.end(), MyPrintf);
cout << endl;
// 3、修改元素
cout << "-----------------------map修改元素--------------------" << endl;
for (int i = 0; i < 10; i++)
{
double fNum = i + 10.1;
myMap[i]=fNum;
}
for_each(myMap.begin(), myMap.end(), MyPrintf);
cout << endl;
// 4、查找元素
cout << "-----------------------map查找元素--------------------" << endl;
map<int,double>::iterator iterFind;
iterFind = myMap.find(1); // key值爲1的元素
if (iterFind != myMap.end())
{
cout << "找到了key值爲1的元素: " << iterFind->second << endl;;
}
// 隨機查找元素功能的函數lower_bound和upper_bound
iterFind = myMap.lower_bound(3); // 從鍵值key的最小開始,注意下標從0開始,即正序
cout << "lower_bound(3): " << "(" << iterFind->first << ", " << iterFind->second << ")" << endl;
iterFind = myMap.upper_bound(4);// 從鍵值key的最大開始,注意下標從最大開始,即倒序
cout << "upper_bound(4): " << "(" << iterFind->first << ", " << iterFind->second << ")" << endl;
// 5、刪除元素
cout << "-----------------------map刪除元素--------------------" << endl;
// 刪除第一個元素
cout << "-----------------------刪除map第一個元素--------------------" << endl;
myMap.erase(myMap.begin());
for_each(myMap.begin(), myMap.end(), MyPrintf);
// 刪除所有的元素
cout << "-----------------------刪除map中所有的元素--------------------" << endl;
myMap.erase(myMap.begin(),myMap.end());
if (myMap.empty())
{
cout << "map中所有的元素都已經刪除!" << endl;
}
std::cout << "Hello World!\n";
getchar();
}
運行結果:
四、set
1、set概述
set屬於關聯型容器,能夠按順序存儲一組值,該集合是一個有序的鏈表,元素以升序的順序存儲,集合中的每一個值都不允許重複,其內部實現與map一樣,可以認爲是一種key-value都一樣的特殊map。
2、set優缺點
優點:
1)set會自動排序功能。
2)內存採用紅黑樹,查詢效率高。
缺點:
1)不能直接修改 set 容器中元素的值,修改set容器中的某個值,需要先刪除該元素,再插入新元素。
2)需要額外空間保存節點,會佔用多餘的空間。
3、set代碼示例
// TestSet.cpp : 此文件包含 "main" 函數。程序執行將在此處開始並結束。
//
#include <iostream>
#include <set>
#include <vector>
#include <algorithm>
#include <functional>
using namespace std;
// 打印函數
void MyPrintf(int elem)
{
cout << elem << ", ";
}
int main()
{
cout << "------------------------C++關聯容器set的使用--------------------" << endl;
// 1、增加元素
cout << "-----------------------set增加元素--------------------" << endl;
set<int> myset;
for (int i = 0; i < 10; i++)
{
myset.insert(i);
}
for_each(myset.begin(), myset.end(), MyPrintf);
cout << endl;
// 2、排序元素
cout << "------set排序元素,set沒有提供直接排序的函數,需要藉助vector排序-------" << endl;
vector<int> setVector(myset.begin(), myset.end());
sort(setVector.begin(), setVector.end(), greater<int>());
for_each(setVector.begin(), setVector.end(), MyPrintf);
cout << endl;
// 3、查找元素
cout << "-----------------------set查找元素--------------------" << endl;
set<int>::iterator iterFind;
iterFind = myset.find(1); // key值爲1的元素
if (iterFind != myset.end())
{
cout << "找到了key值爲1的元素: " << *iterFind<< endl;;
}
// 隨機查找元素功能的函數lower_bound和upper_bound
iterFind = myset.lower_bound(3); // 從鍵值key的最小開始,注意下標從0開始,即正序
cout << "lower_bound(3): " << *iterFind << endl;
iterFind = myset.upper_bound(4);// 從鍵值key的最大開始,注意下標從最大開始,即倒序
cout << "upper_bound(4): " << *iterFind<< endl;
// 4、刪除元素
cout << "-----------------------set刪除元素--------------------" << endl;
// 刪除第一個元素
cout << "-----------------------刪除set第一個元素--------------------" << endl;
myset.erase(myset.begin());
for_each(myset.begin(), myset.end(), MyPrintf);
cout << endl;
// 刪除所有的元素
cout << "-----------------------刪除set中所有的元素--------------------" << endl;
myset.erase(myset.begin(), myset.end());
if (myset.empty())
{
cout << "set中所有的元素都已經刪除!" << endl;
}
std::cout << "Hello World!\n";
getchar();
}
運行結果:
五、deque
1、deque概述
deque屬於序列式容器,採用動態數組來管理序列中的元素,提供了對序列隨機訪問的功能,可以在序列的兩端進行快速的插入和刪除,並可以根據需要調整自身的大小,是典型的雙端隊列。
2、deque優缺點
優點:
1)支持隨機訪問,可以通過[ ]操作符和at()方法訪問數據。
2)可以在兩端方便的進行插入和刪除操作。
3)容器deque的迭代指針屬於智能型指針。
3)容器deque的迭代器屬於隨機存取迭代器。
缺點:
1)佔用內存多,要通過迭代器來維護內存的整體連續,實際內部並不是連續的。
2)在中間進行插入和刪除時比較慢。
3、deque代碼示例
#// TestDeque.cpp : 此文件包含 "main" 函數。程序執行將在此處開始並結束。
//
#include <iostream>
#include <deque>
#include <algorithm>
#include <functional>
using namespace std;
// 打印函數
void MyPrintf(int &elem)
{
cout << elem << ", ";
}
// 修改元素函數
void MyAdd(int &elem)
{
elem = elem + 10;
}
int main()
{
cout << "------------------------C++序列化容器deque的使用--------------------" << endl;
// 1、增加元素
cout << "-----------------------deque增加元素--------------------" << endl;
deque<int> myDeque;
for (int i = 0; i < 10; i++)
{
//myDeque.push_back(i);
//myDeque.push_front(i);
//myDeque.emplace_front(i); // C++11 頭部增加元素
myDeque.emplace_back(i); // C++11 尾部增加元素
}
for_each(myDeque.begin(), myDeque.end(), MyPrintf);
cout << endl;
// 2、排序元素
cout << "-----------------------deque排序元素--------------------" << endl;
sort(myDeque.begin(), myDeque.end(), greater<int>());
for_each(myDeque.begin(), myDeque.end(), MyPrintf);
cout << endl;
// 3、修改元素
cout << "-----------------------deque修改元素--------------------" << endl;
for_each(myDeque.begin(), myDeque.end(), MyAdd);
for_each(myDeque.begin(), myDeque.end(), MyPrintf);
cout << endl;
cout << "-----------------------打印myDeque2中的元素-----------------------" << endl;
deque<int> myDeque2;
myDeque2.assign(myDeque.rbegin(),myDeque.rend());
for_each(myDeque.begin(), myDeque.end(), MyPrintf);
cout << endl;
// 4、查找元素
cout << "-----------------------deque查找元素--------------------" << endl;
deque<int>::iterator index;
index = find(myDeque.begin(), myDeque.end(), 10); // 查找元素10
// 注意:元素的索引從0開始的
cout << "元素10的索引index是: " << (index - myDeque.begin()) << endl;
index = find_if(myDeque.begin(), myDeque.end(), bind2nd(less<int>(), 15));
cout << "第一個小於15的元素索引index是: " << (index - myDeque.begin()) << endl;
// 5、刪除元素
cout << "-----------------------deque刪除元素--------------------" << endl;
while (!myDeque.empty())
{
int backNum = myDeque.back();
cout << backNum << ", ";
myDeque.pop_back();
}
cout << endl;
std::cout << "Hello World!\n";
getchar();
}
運行結果:
六、各個容器的使用場景
1、容器使用場景
vector:當需要高效的隨機存取數據時,而不在乎插入和刪除的效率,可以使用vector。
list:當需要大量的插入和刪除數據時,而不關心隨機存取,可以使用list。
map:當需要以鍵值對的方式存儲數據時,可以使用map。
set:當需要存儲不重複的數據時,可以使用set。
deque:當需要隨機存取數據時,還關心兩端數據的插入和刪除,可以使用deque。
2、C++官方網址
C++的容器使用類模板實現,各個容器提供的方法也非常多,限於篇幅不可能一一列舉出來,文中只是列舉了部分使用方法,具體每個容器的使用方法可以根據官方提供的網址查看,官方網址:http://www.cplusplus.com/reference/vector/vector/?kw=vector
能力有限,如有錯誤,多多指教,覺得有用,點贊鼓勵一下吧。。。