STL——配接器、常用算法使用
學習STL,必然會用到它裏面的適配器和一些常用的算法。它們都是STL中的重要組成部分。
適配器
在STL裏可以用一些容器適配得到適配器。例如其中的stack和queue就是由雙端隊列deque容器適配而來。其實適配器也是一種設計模式,該種模式是將一個類的接口轉換成用戶希望的另外一個接口。簡單的說:就是需要的東西就在眼前,但卻不能用或者使用不是很方便,而短時間又無法改造它,那我們就通過已存在的東西去適配它。
STL中的適配器一共有三種:
①應用於容器的即容器適配器;比如stack和queue就是對deque的接口進行了轉調 ②應用於叠代器的即叠代器適配器;比如反向叠代器就是對叠代器的接口進行了轉調 ③應用於仿函數的即函數適配器
不過我們平常用容器適配器用得比較多,所以我們著重容器適配器的使用,其它適配器可參考《STL源碼剖析》。
(1)stack/queue
我們都知道stack和queue都是一種特殊的線性數據結構,要求在其固定端進行數據的插入和刪除操
作。而在STL的容器當中,deque是雙開口的結構,因此STL就將它作為棧和隊列的底層結構,然後對deque稍加改裝後就實現出來了stack和queue。
像這種:將某個類的接口進行重新包裝而實現出的新結構,稱之為適配器
如stack原型定義:
常用接口:
queue原型定義類似:
註意:stack queue沒有叠代器
(2)priority_queue
priority_queue(優先級隊列)是一個擁有權值關鍵的隊列,允許用戶以任意次序將元素插入容器內,但取出時每次都是取優先級最高(低)的元素,這正是heap所具有該特性,因此priority_queue以vector作為底層存儲元素空間,將heap算法進行包裝,實現出了優先級隊列 。
定義原型:
註:它的頭文件包含在#include <queue>中
關於它的使用:
1.因為僅需取隊首和隊尾元素的操作,因此 priority_queue 優先隊列容器也不提供叠代器,對其他任意位置處的元素進行直接訪問操作。
2.調用top成員函數取隊頭元素時,由於隊列內部封裝的堆算法,取得的元素是權值最大的元素。
3.調用pop刪除頂部的元素時, 刪除的元素是具有最高權值的元素。並且通常pop前會調用成員top進行檢索。
pop成員函數有效地調用pop_heap算法來保留priority_queues的heap屬性,然後調用基礎容器對象的成員函數pop_back來刪除該元素。
4.它同樣支持定制比較的仿函數進行自定義順序比較。
結合這些操作,我們可以來實現一個小功能。
案例:根據出現次數,統計前k項編程語言
vector<string> GetTopKLanguage(const vector<string>& v, int k) { unordered_map<string, int> umap; //將每種語言次數對應統計起來 for( int i=0; i< v.size( ); ++i) { umap[v[i]]++; } UmapIte it1 = umap.begin( ); while( it1 != umap.end( )) { cout<<it1->first<<" :"<<it1->second<<endl; ++it1; } //用priority_queue對pair<string, int>按權值排序 priority_queue<string, vector<UmapIte>, CountCompare> pq; UmapIte it = umap.begin( ); int m = k; while( it != umap.end( )) { if(m > 0 && m--) pq.push(it); else { //取到當前隊列中權值最大(值最小)的元素來pop,相當於逐漸把出現次數較少的語言彈出,將出現次數較多的留下。 UmapIte top = pq.top( ); if( it->second > top->second) { pq.pop(); pq.push(it); } } ++it; } cout<<endl; while(!pq.empty( ) ) { cout<<pq.top( )->first<<" :"<<pq.top( )->second<<endl; pq.pop( ); } // vector<string> ret; // for( int i=0; i<k; ++i) // { // ret.push_back(pq.top()->first); // pq.pop(); // } // return ret; } int main( ) { vector<string> v; v.push_back("PHP"); v.push_back("Python"); v.push_back("Python"); v.push_back("Java"); v.push_back("PHP"); v.push_back("C/C++"); v.push_back("C/C++"); v.push_back("C/C++"); v.push_back("PHP"); v.push_back("Java"); v.push_back("PHP"); v.push_back("Go"); v.push_back("PHP"); v.push_back("Java"); v.push_back("PHP"); v.push_back("PHP"); v.push_back("PHP"); GetTopKLanguage(v, 3); return 0; }
這裏要統計出現次數前3的語言(其中由於優先級隊列遍歷的特殊性,世上最好的語言最後出現):
常用算法
STL中的算法是將常用的算法規範出來,它作用的元素範圍是可以通過叠代器或指針訪問的任何對象序列;但算法只關心操作的步驟,與數據的結構沒有任何的關系(即它不會影響容器的結構 如大小或存儲分配),而且STL在設計時就有一個目標,就是算法可復用,效率要盡可能的高。STL中收錄了極具復用價值的70多個算法,包括:排序,查找,排列組合,數據移動,拷貝,刪除,比較組合,運算等。
關於它的用法:
首先要包含頭文件<algorithm>,裏面定義了各種的算法的函數集合。
常見算法:
查找類算法 find/find_if/find_first_of/binary_search/count/count_if 排序和通用算法 sort/stable_sort/partial_sort/partial_sum/partition/merge/reverse 刪除和替換算法 copy/copy_backward/remove/remove_if/swap/unique 排列組合算法 prev_permutation/next_permutation 集合操作 set_difference/set_union/set_intersection
簡單應用
1.查找
//binary_search
void test_binary_search( )
{
int a[ ] = {11,2,4,5,9,0,3,6};
vector<int> v(a, a+ 8);
sort(v.begin( ), v.end( ));
cout<<binary_search(v.begin( ), v.end( ), 6);
//結果 1
}
2.排序
//partiton
bool IsBigerK(int curValue)
{
return curValue < 3;
}
void test_partition( )
{
vector<int> a;
a.push_back(6);
a.push_back(5);
a.push_back(4);
a.push_back(3);
a.push_back(2);
a.push_back(1);
//以3為邊界劃分
partition(a.begin( ), a.end( ), IsBigerK);
for( int i=0; i< a.size(); ++i)
cout<<a[ i]<<" ";
cout<<endl;
//結果:1 2 4 3 5 6
}
//sort
//template<class T> //形如這樣自定義來比較
//struct Greater
//{
// bool operator()(const T& l, const T& r)
// {
// return l > r;
// }
//};
void test_sort( )
{
vector<int> v;
v.push_back(1);
v.push_back(3);
v.push_back(4);
v.push_back(0);
v.push_back(10);
v.push_back(3);
sort(v.begin( ), v.end(), greater<int>());
for( int i=0; i< v.size( ); ++i)
cout<<v[i]<<" ";
cout<<endl;
//結果:
//10 4 3 3 1 0
}
3.排列組合問題
//排列問題
//next_permutation 求得下一個排列
void test_permutation( )
{
string str("abc");
do
{
cout<<str.c_str( )<<endl;
}while(next_permutation(str.begin( ), str.end( )));
//結果:
//abc
//acb
//bac
//bca
//cab
//cba
//註意:對於值重復,不造成影響. aab求得的全排列不會用重復
}
4.集合操作
set_difference
關於使用:
1. 在兩個集合中找出不同的元素,這些不同的元素全部來自與第一組中,不從第二組中來。
2.它的返回值為一個叠代器it,叠代器指向存儲的結果(即找出來的不同元素)最後一個位置。通常 用it-ret.begin()得到不同元素的個數。
void test_set_difference( ) { int a[ ] = {1, 4, -1, 5, 2}; int b[ ] = {3, 4, 1, 5, 6}; //+5 註意! sort(a, a+5); //-1 1 2 4 5 sort(b, b+5); //1 3 4 5 6 vector<int> v(10); vector<int>::iterator it; it =set_difference(a, a+5, b, b+5, v.begin()) ;//it是-1 2 0 0 0 0 0 0 0 0 的結尾 v.resize(it-v.begin()); //-1 2 it = v.begin( ); while( it != v.end( )) { cout<<*it<<" "; ++it; } //結果:-1 2 }
算法當中的相關分類總覽:
參考:http://www.cplusplus.com/reference/algorithm/
STL——配接器、常用算法使用