set
實現原理:
set 的特性是,所有元素都會根據元素的鍵值自動被排序。set 的元素不像 map 那樣可以同時擁有實值(value)和鍵值(key),set 元素的鍵值就是實值,實值就是鍵值,set不允許兩個元素有相同的值。
set 底層是通過紅黑樹(RB-tree)來實現的,由於紅黑樹是一種平衡二叉搜索樹,自動排序的效果很不錯,所以標準的 STL 的 set 即以 RB-Tree 爲底層機制。又由於 set 所開放的各種操作接口,RB-tree 也都提供了,所以幾乎所有的 set 操作行爲,都只有轉調用 RB-tree 的操作行爲而已。
用法:
一些用法與上面的類似,這裏就不全部列出來了
初始化:
set<int> s;
set<int, mycompare> s;
//mycompare其實就是排序規則,查找等操作全部是按照它來,最好用仿函數吧
賦值操作:
s.swap(s1); //交換集合s和s1
s = s1;
插入操作:
s.insert(10); //往集合中插入10
刪除操作:
s.erase(s.begin(), s.end()-1); //刪除……,返回下一個元素的迭代器
查找操作:
s.find(10);
//查找元素10是否存在,如果存在,返回其迭代器,否則返回s.end()
s.lower_bound(10);
//查找第一個大於等於10的迭代器,如果沒有,則返回s.end()
s.upper_bound(10);
//查找第一個大於10的迭代器,如果沒有,則返回s.end()
s.equal_range(10);
//返回 s.lower_bound(10);和s.upper_bound(10);的迭代器,以對組的方式返回,一樣需要判斷s.end()
對組:
pair<iterator, iterator> pair<int, int>等等等…… 可以 = 賦值,另外也可以使用make_pair函數
EG: pair<set::iterator, set::iterator> ret = s.equal_range(10);
我們可以通過first 以及 second 兩個公有函數來訪問pair的兩個值,即ret.first……
仿函數(函數對象,C++中可以使用這個來取代函數指針,畢竟挺強大的~):
仿函數的主要功能是爲了搭配STL算法使用,單獨使用仿函數的情況比較少。仿函數(functors)在C++標準中採用的名稱是函數對象(function objects)。
仿函數主要用於STL中的算法中,雖然函數指針雖然也可以作爲算法的參數,但是函數指針不能滿足STL對抽象性的要求,也不能滿足軟件積木的要求–函數指針,無法和STL其他組件搭配,產生更靈活變化。仿函數本質就是類重載了一個operator(),創建一個行爲類似函數的對象。
對於重載了()操作符的類,可以實現類似函數調用的過程,所以叫做仿函數,實際上仿函數對象僅僅佔用1字節,因爲內部沒有數據成員,僅僅是一個重載的方法而已。實際上可以通過傳遞函數指針實現類似的功能,但是爲了和STL內部配合使用,他提供了仿函數的特性。
函數對象超出了函數的概念,函數對象可以保存函數調用的狀態:
-
如果是普通函數且我們想獲取調用次數,那我們可以使用全局變量,但是真正開發中,我們是避免去使用全局變量的,因爲在多線程環境中我們需要去進行加鎖解鎖;
- 而仿函數(函數對象)是一個類,我們只需在裏面聲明一個變量即可。
- 像for_each、sort(algorithm)這類函數,我們也可以傳入函數對象(模板的思想)
EG:
class mycompare
{
public:
bool operator()(int v1, int v2){return v1 > v2;}
};
int main(void)
{
set<int, mycompare> s;
}
-
一元仿函數、二元仿函數……:根據參數的個數來稱呼
-
謂詞是指普通函數或重載的operator()返回值是bool類型的函數對象(仿函數)。如果operator接受一個參數,那麼叫做一元謂詞,如果接受兩個對象,那麼叫二元謂詞,謂詞可作爲一個判斷式。
multiset
multiset的特性以及用法和 set 完全相同,唯一的差別在於它允許鍵值重複,因此它的插入操作採用的是底層機制是 RB-tree 的 insert_equal()(即大於等於走右邊) 而非 insert_unique()。
map
實現原理:
map的特性是,所有元素都會根據元素的鍵值自動被排序。map 的所有元素都是 pair,同時擁有實值(value)和鍵值(key)。pair 的第一元素被視爲鍵值,第二元素被視爲實值。
map不允許兩個元素擁有相同的鍵值。由於 RB-tree 是一種平衡二叉搜索樹,自動排序的效果很不錯,所以標準的STL map 即以 RB-tree 爲底層機制。又由於 map 所開放的各種操作接口,RB-tree 也都提供了,所以幾乎所有的 map 操作行爲,都只是轉調 RB-tree 的操作行爲。
用法:
map的很多方法和set是類似的。
初始化:
map<int, string> m;
map<int, string, mycompare> m;
//mycompare其實就是排序規則,查找等操作全部是按照它來,最好用仿函數吧
插入:
map<int, string> mapStu;
mapStu.insert(pair<int, string>(3,"body1"));
//第一種方法,通過pair的方式插入對象
mapStu.insert(make_pair(1, "body2"));
//第二種方法,通過make_pair創建pair的方式插入對象
mapStu.insert(map<int, string>::value_type(5, "body3"));
//第三種方法,通過value_type的方式插入對象
mapStu[2] = "body4";
//第四種方法,通過數組的方式插入值
//**前三種方法區別不大,用數組方式插入時,如果發現key不存在,則會創建pair並插入到map中,如果存在,那麼會修改其實值**
//**注意,使用cout << mapStu[6] << endl; 時,如果通過[]方式去訪問一個不存在的key,那麼map會將這個訪問的key插入到map中,且value會採取默認值**
訪問:
map<int, string>::iterator it;
通過it->first 訪問鍵值,通過it->second 訪問實值
pair<int, string> p;
通過p.first 訪問鍵值,通過p.second 訪問實值,這裏要和迭代器區分開!
查找操作:
map<int, string> m;
m.find(10);
//查找鍵值10是否存在,如果存在,返回其迭代器,否則返回m.end()
m.lower_bound(10);
//查找第一個大於等於10的迭代器,如果沒有,則返回m.end()
m.upper_bound(10);
//查找第一個大於10的迭代器,如果沒有,則返回m.end()
m.equal_range(10);
//返回 m.lower_bound(10);和m.upper_bound(10);的迭代器,以對組的方式返回,一樣需要判斷m.end()
multimap
multimap 的特性以及用法與 map 完全相同,唯一的差別在於它允許鍵值重複,因此它的插入操作採用的是底層機制 RB-tree 的 insert_equal() 而非 insert_unique。
注意:
multimap的仿函數使用和set、multiset、map的不一樣!!
class Mycompare
{
public:
bool operator()(int v1, int v2)const //這裏必須加const
{
return v1>v2;
}
};
int main(void)
{
multimap<int,string, mycompare> m;
}
另外,multimap不支持[],即不能使用s[10]=10這類操作!!
hash_set 與 hash_multiset
hash_set 底層數據結構爲 hash 表,無序,不重複。hash_multiset 底層數據結構爲 hash 表,無序,不重複。(貌似是用拉鍊法)
hash_map 與 hash_multimap
hash_map 底層數據結構爲 hash 表,無序,不重複。hash_multimap 底層數據結構爲 hash 表,無序,不重複。
其它
-
STL容器所提供的都是值(value)寓意,而非引用(reference)寓意,也就是說當我們給容器中插入元素的時候,容器內部實施了拷貝動作,將我們要插入的元素再另行拷貝一份放到容器中,而不是將原數據元素直接放進容器中,也就是說我們提供的元素必須那個被拷貝。
-
通常STL不會拋出異常,需要使用者傳入正確參數
-
STL容器使用時機:
-
vector:單端數組,可隨機存取(有時候稱爲直接訪問),元素搜尋速度慢,元素安插移除在尾端比較快
-
deque:雙端數組,可隨機存取,元素搜尋速度慢,元素安插移除在頭尾兩端比較快
-
list:雙向鏈表,不可隨機存取,元素搜尋速度非常慢,元素安插移除在任何位置都快
-
set:紅黑樹,不可隨機存取,元素搜尋速度快
-
multiset:紅黑樹,不可隨機存取,元素搜尋速度快
-
map:紅黑樹,對key而言是可隨機存取,對key而言元素搜尋速度快
-
multimap:紅黑樹,不可隨機存取,對key而言元素搜尋速度快
-
-
函數對象(仿函數)
很多東西前面已經講了,這裏就不重複了。-
STL內建了一些函數對象。分爲:算法類函數對象,關係類函數對象,邏輯類函數對象。這些函數對象所產生的對象,用法和一般的函數完全相同,
當然我們還可以產生無名的臨時對象來履行函數功能。使用內建函數對象,需要引入頭文件 #include。-
函數對象適配器(重點)(functional庫)
函數對象適配器是完成一些配接工作,這些配接工作包括綁定(bind)、否定(negate),以及對一般函數或成員的修飾,使其成爲函數對象。-
綁定適配器:bind1st、bind2nd
-
功能:將一個二元函數對象轉變爲一元函數對象
-
區別:
-
bind1st(叫bind first)是將傳進來的參數綁定爲函數對象的第一個參數
-
bind2nd(叫bind second)是將傳進來的參數綁定爲函數對象的第二個參數
-
例子:
-
-
-
-
-
class Myprint : public binary_function<int, int, void>
//注意,使用綁定配接器必須使我們的函數對象繼承自binary_function,
binary_function是一個模板類,我們需要傳入<參數1類型, 參數二類型, 返回類型>
{public:
void operator()(int v, int val) const //注意,這裏必須使用const修飾
{
cout << v + val << endl;
}
}
……
int addNum = 200;
for_each(v.begin(), v.end(), bind2st(Myprint, addNum));
-
取反適配器:not1、not2
-
功能:對謂詞的返回進行取反
-
區別:not1是對一元謂詞進行取反,而not2是對二元謂詞進行取反
-
例子:
-
class Mycompare : public binary_function<int, int, bool>
//注意,使用not2配接器必須使我們的函數對象繼承自binary_function,
我們這裏是二元的,因此是繼承自binary_function
{public:
bool operator()(int v1, int v2) const
//注意,這裏必須使用const修飾
{
return v1 > v2;
}
}
class Mycompare5 : public binary_function<int, bool>
//注意,這裏繼承的父類是unary_function
{public:
bool operator()(int v1) const
//注意,這裏必須使用const修飾
{
return v1 > 5;
}
}
……
sort(v.begin(), v.end(), not2(Mycompare));
find_if(v.befin(), v.end(), not1(Mycompare5));
-
關於find_if算法(使用方法參考上面的例子)
這個算法的功能是返回第一個滿足條件的迭代器 -
函數對象適配器:ptr_fun
-
功能:把函數轉變爲函數對象
-
例子:
-
void myPrint(int val1, int val2)
{
cout << val1 + val2 << endl;
}
……
for_each(v.begin(), v.end(), bind2st(ptr_fun(myPrint), 10));
-
成員函數適配器 mem_fun、mem_fun_ref
-
功能:如果容器中存放的是對象或者對象指針,我們for_each算法打印的時候又想用類自己提供的打印函數
-
例子
class Stu { public: void Print(int val1, int val2); } …… vector<Stu> v1; for_each(v1.begin(), v1.end(), mem_fun_ref(&Stu::Print)); //注意使用的方法 vector<Stu*> v2; for_each(v2.begin(), v2.end(), mem_fun(&Stu::Print)); //注意mem_fun和mem_fun_ref的使用,必須加&符號!! //當容器內是對象時,使用mem_fun_ref;當容器內是對象指針時,使用mem_fun。
-
-
常用的算法
-
常用的遍歷算法
-
for_each(iterator beg, iterator end, _callback);
-
transform(iterator beg1, iterator end1, iterator beg2, _callback)
- 將指定容器區間元素搬運到另一個容器中,注意另一個容器的空間必須是初始化過的,可以使用resize,但不可以使用reserve,參考二者的區別
- _callback是搬運的方法,例子如下:
class Myplus {public: int operator()(int val){return val; } } …… transform(v1.begin(), v1.end(), v2.begin(), Myplus());
-
-
常用的查找算法
-
find(iterator beg, iterator, value);
- 需要注意的是(下面也一樣),自定義對象間的比較要求我們重載==運算符,如bool operator ==(const Person p)const{}
-
adjacent_find(iterator beg, iterator end, _callback);
- 查找相鄰重複元素,_callback爲回調函數或者謂詞,返回相鄰元素的第一個位置的迭代器
-
bool binary_search(iterator beg, iterator end, value);
- 二分查找,在無序序列中不能使用,查找成功返回true,否則返回false
-
find_if(iterator beg, iterator end, _callback);
- 查找第一個滿足條件的元素(_callback,謂詞),並返回其迭代器,如果沒有當然是返回end()
-
count(iterator beg, iterator end, value);
- 統計元素的出現個數並返回(int)
-
count_if(iterator beg, iterator end, _callback);
- 統計滿足條件的元素的個數並返回(int)
-
-
常用排序算法
-
merge(iterator beg1, iterator end1, iterator beg2, iterator end2, iterator dest, _callback);
- 將兩個已排序(必須都是從小到大或從大到小)合併,並存儲到另一個容器中
-
sort(iterator beg, iterator end, _callback);
- 容器元素排序
-
random_shuffle(iterator beg, iterator end);
- 對指定範圍內的元素隨機調整次序
-
reverse(iterator beg, iterator end);
- 反轉指定範圍的元素
-
-
常用拷貝和替換算法
-
copy(iterator beg, iterator end, iterator dest);
- 將容器內指定範圍的元素拷貝到另一容器中
-
replace(iterator beg, iterator end, oldvalue, newvalue);
- 將容器內指定範圍的舊元素修改爲新元素
-
replace_if(iterator beg, iterator end, _callback, newvalue);
- 將容器內指定範圍滿足條件的元素替換爲新元素
-
swap(container c1, container c2);
- 互換兩個容器的元素
-
-
常用算術生成算法
-
accumulate(iterator beg, iterator end, value);
- 計算容器中=元素累計總和+value
-
fill(iterator beg, iterator end, value);
- 向容器中添加元素
-
-
常用的集合算法
-
set_intersection(iterator beg1, iterator end1, iterator iterator beg2, iterator end2, iterator dest);
- 求兩個集合的交集,返回一個迭代器,該迭代器指向最後一個元素的下一個元素
-
set_union(iterator beg1, iterator end1, iterator iterator beg2, iterator end2, iterator dest);
- 求兩個集合的並集,返回一個迭代器,該迭代器指向最後一個元素的下一個元素
-
set_difference(iterator beg1, iterator end1, iterator iterator beg2, iterator end2, iterator dest);
- 求兩個集合的差集,返回一個迭代器,該迭代器指向最後一個元素的下一個元素
-
-