1. 程式人生 > >STL——配接器、常用算法使用

STL——配接器、常用算法使用

ren 模式 函數 兩個 C/C++ 剖析 學習 PE tar

學習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——配接器、常用算法使用