1.STL的三種類型容器
順序容器:
vector 向量容器
deque 雙端隊列
list 鏈表
容器適配器:
stack 棧
queue 隊列
priority_queue 優先級隊列
關聯容器:
set/multiset 集合
map/multimap 映射表
2.各容器的底層實現
具體實現請參考侯捷的《STL源碼剖析》,此處不貼源碼,只是對底層實現做簡單介紹。
順序容器:
<1>vector 向量容器
vector底層是動態開闢的可2倍自增長的一維數組,默認不申請空間。
第一次向vector中增加元素時,開闢一個格子,以後如果內存不足,每次擴大2倍。因此,這種數據結構在剛開始內存增長時的效率很低。vector底層內存連續。
<2>deque 雙端隊列容器
deque底層時動態開闢的一維可2倍自增長的二維數組,默認部申請空間。deque底層內存不連續。
第一次向deque中增加元素時,一維開闢兩個格子,二維開闢的元素個數遵守以下規則:如果變量的類型<4096,開闢4096/sizeof(_Ty)個格子;否則開闢一個格子,格子大小爲sizeof(_Ty)。size=sizeof(_Ty) < 4096 ? 4096/sizeof(_Ty) : sizeof(_Ty);
因爲是雙端隊列,所以底層有兩個指針(_start和_finish)。_start和_finish都向最中間。頭插時,_start向上移動,尾插時,_finish向下移動。二維數組不夠用時,再開闢二維空間。如果二維已經開闢滿了,則一維成2倍增長(cur *= 2),並將二維空間移動到新的一維空間的最中間,減少push_front和push_back時,開闢內存和移動的次數(pos = cur/2 -1)。
<3>list
list的底層是雙向鏈表。對list的操作就是對鏈表的操作,所以list容器的添加和修改效率很高。list默認分配一個頭節點,在插入值的時候,動態的申請節點,掛在鏈表中。
容器適配器:
容器適配器底層沒有自己的數據結構,他們是對某種容器的代理。
<1>stack默認的底層數據結構是deque
<2>queue默認的底層結構是queue
<3>priority_queue默認的底層結構是用vector實現的大根堆
關聯容器:
關聯容器的底層是RBtree,key有序排列。對關聯容器的操作都是對紅黑樹的操作。插入元素時,最多調整2次;刪除元素時,最多調整3次。
set/multiset只保存key,set中key不能重複,multiset中key可以重複
map/multimap保存的是key-value,map中key不能重複,multimap中key可以重複
Boost庫中實現了hash_map和multimap,是用鏈地址法實現的哈希表,鏈表的每個元素保存的都是鍵值對。
3.各類型容器的操作接口函數
<1>vector
定義:vector<int> vec;
插入:vec.push_back(value); vec.insert(it, value);
刪除:vec.erase(it); vec.erase(vec.begin(), vec.end());
獲取首元素:vector::itreator it = vec.begin();
vector末尾後一個元素:vector::iterator it = vec.end();
迭代器運算:it++; it–; ++it; –it;
vector的reserver方法和resize的區別:
vec.reserver(size);//只開闢內存
vec.resize(size);//開闢空間並構建size個對象
<2>deque
定義:deque<int> dq;
插入:dq.push_front(value); dq.push_back(value); dq.insert(it,value);
刪除:dq.erase(it); dq.erase(dq.begin(), dq.end());
獲取首元素:deque::itreator it = dq.begin();
deque末尾後一個元素:deque:;iterator it = dq.end();
迭代器運算:it++; it–; ++it; –it;
<3>list
定義:list <int> list1;
插入:list1.push_front(value); list1.push_front(value); list1.insert(it, value);
刪除:list1.erase(it); list1.erase(list1.begin(), list2.end());
切片函數:list.splice(list.begin(), list.end(), list2, list2.end());
//將list1從begin到end的節點都拼接到list2的最後面
<4>stack
定義:stack<int> st;
添加元素:st.push(value);
獲取棧頂元素:st.top();
刪除棧頂元素:st.pop();
判斷棧是否空:st.empty();
求棧的元素個數:st.size();
<5>queue
定義:queue<int> qu;
添加元素:qu.push(value);
獲取隊列首元素:qu.front();
獲取隊列尾元素:qu.back();
刪除對頭元素:qu.pop();
判斷隊列是否空:qu.empty();
求隊列元素個數:qu.size();
<6>priority_queue
定義:priority_queue<int, vector<int>> queue; 大根堆
priority_queue<int, vector<int>, greater<int>> queue; 小根堆
添加元素:queue.push(value);
獲取首元素:queue.top();
刪除首元素:queue.pop();
判斷隊列是否空:queue.empty();
求隊列的元素個數:queue.size();
<7>set/multiset
定義:set<int> myset;
插入:myset.insert(key);
刪除:myset.erase(it); myset.erase(key);
查找:myset.find(key);
<8>map/multimap
定義:map<int, int> mymap;
插入:mymap.insert(makepair(key, value));
mymap[key] = value;
刪除:mymap.erase(it); mymap.erase(key);
訪問:map<int, int> ::iterator it = mymap.begin();
cout<begin<<” “<second;
4.泛型算法
需要頭文件#include <algorithm>
常用泛型算法:
find(vec.begin(), vec.end(), value);
find_if(vec.begin(), vec.end(), bind1st(greater<int>(), num));
sort(vec.begin(), vec.end()); 即:sort(vec.begin(), vec.end(), less<int>());//從小到大
sort(vec.begin(), vec.end(), greater<int>);//從大到小
copy(vec1.begin(), vec1.end(), back_inserter(vec2));
copy(vec1.begin(), vec1.end(), inserter(vec2, vec2.end()));
unique(it.begin(), it.end()); ///刪除相鄰重複的元素,實則是將重複的元素挪到後面,返回值是指向重複的第一個元素的迭代器
5.STL和泛型算法的實際應用
#include <iostream>
#include <algorithm>
#include <functional>
#include <vector>
#include <iterator>
using namespace std;
int main()
{
//默認構造一個int vector vec;
vector<int> vec;
//隨機添加10個1-100整數
for(int i=0;i<10; ++i)
{
vec.push_back(rand()%100+1);
}
//從小到大排序
sort(vec.begin(), vec.end());
//打印容器
vector<int>::iterator it1;
for(it1=vec.begin(); it1!=vec.end(); ++it1)
{
cout<<*it1<<" ";
}
cout<<endl;
//從大到小排序
sort(vec.begin(), vec.end(), greater<int>());
//打印容器
for(it1=vec.begin(); it1!=vec.end(); ++it1)
{
cout<<*it1<<" ";
}
cout<<endl;
//找到第一個小於50的數字,並打印出來,並刪除掉
vector<int>::iterator it2 = find_if(vec.begin(), vec.end(), bind1st(greater<int>(), 50));
cout<<*it2<<endl;
vec.erase(it2);
//找容器裏面有沒有60這個元素,找到的話打印出來,沒找到打印失敗信息
vector<int>::iterator it3 = find(vec.begin(), vec.end(), 60);
if(it3 == vec.end())
{
cout<<"don't find 60"<<endl;
}
else
{
vec.erase(it3);
}
//生成一個新的vector容器
vector<int> vec2;
//把vec1容器裏面的所有元素拷貝到新容器當中
copy(vec2.begin(), vec2.end(), vec.begin());
//打印新容器
vector<int>::iterator it4;
for(it4=vec.begin(); it4!=vec.end(); ++it4)
{
cout<<*it4<<" ";
}
cout<<endl;
//清空舊容器裏面所有的元素
vec.clear();
return 0;
}
#include <iostream>
#include <algorithm>
#include <functional>
#include <vector>
#include <iterator>
using namespace std;
int main()
{
//新建一個vector int實例化,默認構造
vector<int> vec;
//隨機100個0-10之間的數字
for(int i=0; i<100; ++i)
{
vec.push_back(rand()%10);
}
vector<int>::iterator it1;
for(it1=vec.begin(); it1!=vec.end(); ++it1)
{
cout<<*it1<<" ";
}
cout<<endl;
//給容器去重複的數字
sort(vec.begin(), vec.end());
vector<int>::iterator it2 = unique(vec.begin(), vec.end());
for(it1=vec.begin(); it1!=it2; ++it1)
{
cout<<*it1<<" ";
}
cout<<endl;
return 0;
}
6.迭代器
迭代器的類型:
<1>正向迭代器 iterator
<2>逆向迭代器 reserver_oterator
<3>插入型迭代器
back_insert_iterator /back_inserter push_back(value)
front_insert_iterator /front_inserter push_front(value)
isert_iterator /inserter insert(it,value)
插入型迭代器的使用
#include <iostream>
#include <algorithm>
#include <functional>
#include <vector>
#include <iterator>
using namespace std;
int main()
{
vector<int> v1;
vector<int> v2;
vector<int> v3;
for(int i=0;i<10;++i)
{
v1.push_back(i);
}
for(int i=0;i<10;++i)
{
v2.push_back(i+10);
}
for(int i=0;i<10;++i)
{
v3.push_back(i+20);
}
copy(v2.begin(), v2.end(),back_inserter(v1));
vector<int>::iterator it1 = v1.begin();
for(; it1!=v1.end(); ++it1)
{
cout<<*it1<<" ";
}
cout<<endl;
copy(v3.begin(), v3.end(), inserter(v1, v1.end()));
for(it1 = v1.begin(); it1!=v1.end(); ++it1)
{
cout<<*it1<<" ";
}
cout<<endl;
return 0;
}
<4>流迭代器
istream_iterator
ostream_iterator
#include <iostream>
#include <algorithm>
#include <functional>
#include <vector>
#include <iterator>
#include <set>
using namespace std;
int main()
{
set<int> myset;
copy(istream_iterator<int>(cin),
istream_iterator<int>(),
inserter(myset, myset.begin()));
copy(myset.begin(), myset.end(),
ostream_iterator<int>(cout, " "));
}