標準模板庫STL
8.1 STL概述
1.泛型程序設計
C++ 語言的核心優勢之一就是便於軟件的重用
C++中有兩個方面體現重用:1.面向對象的思想:繼承和多態,標準類庫 2.泛型程序設計(generic programming) 的思想: 模板機制,以及標準模板庫 STL
簡單地說就是使用模板的程序設計法。
將一些常用的數據結構(比如鏈表,數組,二叉樹)和算法(比如排序,查找)寫成模板,以後則不論數據結構裏放的是什麼對象,算法針對什麼樣的對象,則都不必重新實現數據結構,重新編寫算法。
標準模板庫 (Standard Template Library) 就是一些常用數據結構和算法的模板的集合。
2.STL中的基本的概念
容器:可容納各種數據類型的通用數據結構,是類模板
迭代器:可用於依次存取容器中元素,類似於指針
算法:用來操作容器中的元素的函數模板。算法本身與他們操作的數據的類型無關,因此他們可以在從簡單數組到高度複雜容器的任何數據結構上使用。
int array[100];
該數組就是容器,而 int * 類型的指針變量就可以作爲迭代器,sort算法可以作用於該容器上,對其進行排序:
sort(array,array+70); //將前70個元素排序 array和array+70即是迭代器
3.容器概述
可以用於存放各種類型的數據(基本類型的變量,對象等)的數據結構,都是類模版,分爲三種:
- 順序容器
vector,deque 雙向隊列,list 雙向鏈表 - 關聯容器
set,multiset,map,multimap - 容器適配器
stack,queue,priority_queue
4.順序容器簡介
容器並非排序的,元素的插入位置同元素的值無關。
有vector,deque,list 三種
vector 頭文件<vector>
- 動態數組。元素在內存連續存放。
- 隨機存取任何元素都能在常數時間完成。
- 在尾端增刪元素具有較佳的性能(大部分情況下是常數時間)。 但是當存儲空間不夠時,需要開闢新空間,把原來的元素拷過來後再插入,時間複雜度則是O(n)。
- 在頭部或中部插入或刪除元素時間複雜度是O(n),因爲需要移動元素。
deque 頭文件 <deque>
- 雙向隊列。元素在內存連續存放。
- 隨機存取任何元素都能在常數時間完成(但次於vector)。
次於vector的原因是,它是雙向隊列,計算元素地址時需要判斷是否超出末尾然後減去長度。 - 在兩端增刪元素具有較佳的性能(大部分情況下是常數時間)。
list 頭文件 <list>
- 雙向鏈表。元素在內存不連續存放。
- 在任何位置增刪元素都能在常數時間完成。
- 不支持隨機存取。
5.關聯容器簡介
- 元素是排序的
- 插入任何元素,都按相應的排序規則來確定其位置
- 在查找時具有非常好的性能。通常以平衡二叉樹方式實現,插入和檢索的時間都是 O(log(N))
- set/multiset 頭文件 <set>
set 即集合。set中不允許相同元素,multiset中允許存在相同的元素。 - map/multimap 頭文件 <map>
map 與 set 的不同在於map中存放的元素有且僅有兩個成員變量,一個名爲first,另一個名爲second, map根據first值對元素進行從小到大排序,並可快速地根據first來檢索元素。
map 同 multimap 的不同在於是否允許相同first值的元素。
6.容器適配器簡介
- stack
棧。後進先出。 頭文件 <stack> - queue
隊列。先進先出。 頭文件 <queue> - priority_queue
優先級隊列。優先級最高的元素總是位於隊首。 頭文件 <queue>
7.順序容器和關聯容器中都有的成員函數
- begin 返回指向容器中第一個元素的迭代器
- end 返回指向容器中最後一個元素後面的位置的迭代器
- rbegin 返回指向容器中最後一個元素的迭代器
- rend 返回指向容器中第一個元素前面的位置的迭代器
- erase 從容器中刪除一個或幾個元素
- clear 從容器中刪除所有元素
8.順序容器的常用成員函數
- front :返回容器中第一個元素的引用
- back : 返回容器中最後一個元素的引用
- push_back : 在容器末尾增加新元素
- pop_back : 刪除容器末尾的元素
- erase :刪除迭代器指向的元素(可能會使該迭代器失效),或刪除一個區間,返回被刪除元素後面的那個元素的迭代器
9.迭代器
- 用於指向順序容器和關聯容器中的元素
- 迭代器用法和指針類似
- 有const 和非 const兩種
- 通過迭代器可以讀取它指向的元素
- 通過非const迭代器還能修改其指向的元素
容器類名::iterator 變量名;
或:容器類名::const_iterator 變量名;
訪問一個迭代器指向的元素:* 迭代器變量名
迭代器上可以執行 ++ 操作, 以使其指向容器中的下一個元素。如果迭代器到達了容器中的最後一個元素的後面,此時再使用它,就會出錯,類似於使用NULL或未初始化的指針一樣。常用兩種迭代器:雙向迭代器和隨機訪問迭代器。
10.雙向迭代器
- ++p, p++ 使p指向容器中下一個元素
- --p, p-- 使p指向容器中上一個元素
- * p 取p指向的元素
- p = p1 賦值
- p == p1 , p!= p1 判斷是否相等、不等
11.隨機訪問迭代器
- 雙向迭代器的所有操作
- p += i 將p向後移動i個元素
- p -= i 將p向向前移動i個元素
- p + i 值爲: 指向 p 後面的第i個元素的迭代器
- p - i 值爲: 指向 p 前面的第i個元素的迭代器
- p[i] 值爲: p後面的第i個元素的引用
- p < p1, p <= p1, p > p1, p>= p1
8.2 STL概述續
1.容器上的迭代器
容器 容器上的迭代器類別
vector 隨機訪問
deque 隨機訪問
list 雙向
set/multiset 雙向
map/multimap 雙向
stack 不支持迭代器
queue 不支持迭代器
priority_queue 不支持迭代器
list 的迭代器是雙向迭代器,
正確的遍歷list的方法:
list<int> v;
list<int>::const_iterator ii;
for( ii = v.begin(); ii != v.end ();++ii )
cout << * ii; //用的是解引用方式取元素。循環的判斷條件是!=
錯誤的做法:
for( ii = v.begin(); ii < v.end ();ii ++ )
cout << * ii;
//雙向迭代器不支持 <,list沒有 [] 成員函數
for(int i = 0;i < v.size() ; i ++)
cout << v[i];
可以看到,list 的迭代器是雙向迭代器,不支持 < 操作,沒有 [ ] 成員函數。2.算法簡介
- 算法就是一個個函數模板,大多數在<algorithm> 中定義
- STL中提供能在各種容器中通用的算法,比如查找,排序等
- 算法通過迭代器來操縱容器中的元素。許多算法可以對容器中的一個局部區間進行操作,因此需要兩個參數,一個是起始元素的迭代器,一個是終止元素的後面一個元素的迭代器。比如,排序和查找
- 有的算法返回一個迭代器。比如 find() 算法,在容器中查找一個元素,並返回一個指向該元素的迭代器
- 算法可以處理容器,也可以處理普通數組
3.算法示例:find()
template<class InIt, class T>
InIt find(InIt first, InIt last, const T& val);
- first 和 last 這兩個參數都是容器的迭代器,它們給出了容器中的查找區間起點和終點[first,last)。區間的起點是位於查找範圍之中的,而終點不是。find在[first,last)查找等於val的元素
- 用 == 運算符判斷相等
- 函數返回值是一個迭代器。如果找到,則該迭代器指向被找到的元素。如果找不到,則該迭代器等於last
#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;
int main() { //find算法示例
int array[10] = {10,20,30,40};
vector<int> v;
v.push_back(1); v.push_back(2);
v.push_back(3); v.push_back(4);
vector<int>::iterator p;
p = find(v.begin(),v.end(),3);
if( p != v.end())
cout << * p << endl; //輸出3
p = find(v.begin(),v.end(),9);
if( p == v.end())
cout << "not found " << endl;
p = find(v.begin()+1,v.end()-2,1); //整個容器:[1,2,3,4], 查找區間:[2,3)
if( p != v.end()) //沒有找到,p則爲v.end()-2,指向3
cout << * p << endl;
int * pp = find( array,array+4,20);//數組名是迭代器
cout << * pp << endl;
}
輸出:
3
not found
3
20
4.STL中“大”“小” 的概念
- 關聯容器內部的元素是從小到大排序的
- 有些算法要求其操作的區間是從小到大排序的,稱爲“有序區間算法”
例:binary_search - 有些算法會對區間進行從小到大排序,稱爲“排序算法”
例: sort - 還有一些其他算法會用到“大”,“小”的概念
- 使用STL時,在缺省的情況下,以下三個說法等價:
1) x比y小
2) 表達式“x<y”爲真
3) y比x大
5.STL中“相等”的概念
- 有時,“x和y相等”等價於“x==y爲真”
例:在未排序的區間上進行的算法,如順序查找find…… - 有時“x和y相等”等價於“x小於y和y小於x同時爲假”
例:有序區間算法,如binary_search,
關聯容器自身的成員函數 find……(關聯容器就是用來查找的,自身帶有成員函數find,你不能用find算法或其他的binary_search算法去查找關聯容器)
6.STL中“相等” 概念演示
#include <iostream>
#include <algorithm>
using namespace std;
class A {
int v;
public:
A(int n):v(n) { }
bool operator< ( const A & a2) const
{
cout << v << "<" << a2.v << "?" << endl;
return false;
}
bool operator ==(const A & a2) const
{
cout << v << "==" << a2.v << "?" << endl;
return v == a2.v;
}
};
int main()
{
A a [] = { A(1),A(2),A(3),A(4),A(5) };
cout << binary_search(a,a+4,A(9)); //折半查找
return 0;
}
//輸出結果:
3<9?
2<9?
1<9?
9<1?
1
這裏可以看到,binary_search調用的是 < 操作符,沒有使用 == 操作符。這樣在查找中的相當概念即是“x小於y和y小於x同時爲假”8.3 順序容器 vector
1.vector
- 可變長的動態數組
- 必須包含頭文件 #include <vector>
- 支持 隨機訪問迭代器
根據下標隨機訪問某個元素時間爲常數
在尾部添加速度很快
在中間插入慢 - 所有STL算法 都能對vector操作
2.vector的成員函數
構造函數初始化
vector(); 無參構造函數, 將容器初始化成空的
vector(int n); 將容器初始化成有n個元素
vector(int n, const T & val); 假定元素類型是T, 將容器初始化成有n個元素, 每個元素的值都是val
vector(iterator first, iterator last); 將容器初始化爲與別的容器上區間[first, last)一致的內容
其他常用函數
void pop_back(); 刪除容器末尾的元素
void push_back(const T & val); 將val添加到容器末尾
int size(); 返回容器中元素的個數
T & font(); 返回容器中第一個元素的引用
T & back(); 返回容器中最後一個元素的引用
3.二維動態數組
8.4 List 和 Deque
1.list 容器
- 雙向鏈表 頭文件 #include <list>
- 在任何位置插入/刪除都是常數時間
- 不支持根據下標隨機存取元素
- 具有所有順序容器都有的成員函數
push_front 在鏈表最前面插入
pop_front 刪除鏈表最前面的元素
sort 排序 (list 不支持 STL 的算法 sort)
remove 刪除和指定值相等的所有元素
unique 刪除所有和前一個元素相同的元素
merge 合併兩個鏈表, 並清空被合併的鏈表
reverse 顛倒鏈表
splice 在指定位置前面插入另一鏈表中的一個或多個元素,並在另一鏈表中刪除被插入的元素
2.list容器之sort函數
list容器的迭代器不支持完全隨機訪問—>不能用標準庫中sort函數對它進行排序
list自己的sort成員函數
list<T> classname
classname.sort(compare); //compare函數可以自己定義
classname.sort(); //無參數版本, 按<排序
——>不支持大於/小於比較運算符, []運算符和隨機移動(即類似 “list的迭代器+2” 的操作)
很長的例子
3.deque 容器
- 雙向隊列
- 必須包含頭文件 #include <deque>
- 所有適用於vector的操作——>都適用於deque
- deque還有 push_front (將元素插入到容器的頭部)和 pop_front (刪除頭部的元素) 操作
8.5 函數對象
1.函數對象
class CMyAverage { //函數對象類
public:
double operator() ( int a1, int a2, int a3 ) {
return (double)(a1 + a2+a3) / 3;
}
};
CMyAverage average; //函數對象
cout << average(3,2,3); // average.operator()(3,2,3)
輸出 2.66667
2.函數對象的應用
template<typename _InputIterator, typename _Tp>
_Tp accumulate(_InputIterator __first, _InputIterator __last,_ Tp __init)
{
for ( ; __first != __last; ++__first)
__init = __init + *__first;
return __init;
}
// typename 等價於class
Dev C++ 中的 Accumulate 源代碼2:
template<typename _InputIterator, typename _Tp, typename _BinaryOperation>
_Tp accumulate(_InputIterator __first, _InputIterator __last, _Tp __init, _BinaryOperation __binary_op)
{
for ( ; __first != __last; ++__first)
__init = __binary_op(__init, *__first);
return __init;
}
調用accumulate時, 和__binary_op對應的實參可以是個函數名、函數指針或函數對象
3.函數對象的應用示例
#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>
#include <functional>
using namespace std;
int sumSquares( int total, int value) { return total + value * value; }
template <class T>
void PrintInterval(T first, T last)
{ //輸出區間[first,last)中的元素
for( ; first != last; ++ first)
cout << * first << " ";
cout << endl;
}
template<class T>
class SumPowers
{
private:
int power;
public:
SumPowers(int p):power(p) { }
const T operator() ( const T & total, const T & value)
{ //計算 value的power次方,加到total上
T v = value;
for( int i = 0;i < power - 1; ++ i)
v = v * value;
return total + v;
}
};
int main()
{
const int SIZE = 10;
int a1[ ] = { 1,2,3,4,5,6,7,8,9,10 };
vector<int> v(a1,a1+SIZE);
cout << "1) "; PrintInterval(v.begin(),v.end());
int result = accumulate(v.begin(),v.end(),0,SumSquares);
cout << "2) 平方和:" << result << endl;
result =accumulate(v.begin(),v.end(),0,SumPowers<int>(3));
cout << "3) 立方和:" << result << endl;
result =accumulate(v.begin(),v.end(),0,SumPowers<int>(4));
cout << "4) 4次方和:" << result;
return 0;
}
//輸出:
1) 1 2 3 4 5 6 7 8 9 10
2) 平方和:385
3) 立方和:3025
4) 4次方和:25333
int result = accumulate(v.begin(),v.end(),0,SumSquares);實例化出:
int accumulate(vector<int>::iterator first, vector<int>::iterator last, int init,int ( * op)( int,int))
{
for ( ; first != last; ++first)
init = op(init, *first);
return init;
}
實例化出:
int accumulate(vector<int>::iterator first,vector<int>::iterator last, int init, SumPowers<int> op)
{
for ( ; first != last; ++first)
init = op(init, *first);
return init;
}
4.STL中的函數對象類模板
equal_to
greater
less
…….
頭文件: <functional>
greater 函數對象類模板
struct greater : public binary_function<T, T, bool> {
bool operator()(const T& x, const T& y) const {
return x > y;
}
};
greater 的應用
void sort(); 將list中的元素按 “<” 規定的比較方法升序排列。
template <class Compare> void sort (Compare op);將list中的元素按 op 規定的比較方法升序排列。即要比較x,y大小時,看op(x,y) 的返回值,爲true則認爲 x小於y
#include <list>
#include <iostream>
using namespace std;
class MyLess {
public:
bool operator()( const int & c1, const int & c2 )
{
return (c1 % 10) < (c2 % 10);
}
};
template <class T>
void Print(T first,T last) {
for( ; first != last ; ++ first ) cout << * first << ",";
}
int main()
{
const int SIZE = 5;
int a[SIZE] = {5,21,14,2,3};
list<int> lst(a,a+SIZE);
lst.sort(MyLess()); //MyLess()是一個函數對象,臨時對象的()成員函數
Print( lst.begin(),lst.end());
cout << endl;
lst.sort(greater<int>()); //greater是一個類模板,greater<int>是一個類,greater<int>()是個臨時對象,它是函數對象
Print( lst.begin(),lst.end());
cout << endl;
return 0;
}
//輸出:
21,2,3,14,5,
21,14,5,3,2,
5.在STL中使用自定義的“大”,“小”關係
的:
1) x小於y
2) op(x,y)返回值爲true
3) y大於x
例題:寫出MyMax模板
#include <iostream>
#include <iterator>
using namespace std;
class MyLess {
public:
bool operator() (int a1,int a2) {
if( ( a1 % 10 ) < (a2%10) )
return true;
else
return false;
}
};
bool MyCompare(int a1,int a2)
{
if( ( a1 % 10 ) < (a2%10) )
return false;
else
return true;
}
int main()
{
int a[] = {35,7,13,19,12};
cout << * MyMax(a,a+5,MyLess())
<< endl;
cout << * MyMax(a,a+5,MyCompare)
<< endl;
return 0;
}
//輸出:19
12
怎麼寫這個模板呢?template <class T, class Pred>
T MyMax( T first, T last, Pred myless)
{
T tmpMax = first;
for(; first != last; ++ first)
if( myless( * tmpMax,* first))
tmpMax = first;
return tmpMax;
};
8.6 Set 和 Multiset
1.關聯容器
內部元素有序排列,新元素插入的位置取決於它的值,查找速度快。
除了各容器都有的函數外,還支持以下成員函數:
find: 查找等於某個值 的元素(x小於y和y小於x同時不成立即爲相等)
lower_bound : 查找某個下界
upper_bound : 查找某個上界
equal_range : 同時查找上界和下界
count : 計算等於某個值的元素個數(x小於y和y小於x同時不成立即爲相等)
insert: 用以插入一個元素或一個區間
2.預備知識: pair 模板
template<class _T1, class _T2>
struct pair
{
typedef _T1 first_type;
typedef _T2 second_type;
_T1 first;
_T2 second;
pair(): first(), second() { }
pair(const _T1& __a, const _T2& __b): first(__a), second(__b) { }
template<class _U1, class _U2>
pair(const pair<_U1, _U2>& __p): first(__p.first), second(__p.second) { }
};
map/multimap容器裏放着的都是pair模版類的對象,且按first從小到大排序
第三個構造函數用法示例:
pair<int,int> p(pair<double,double>(5.5,4.6));// p.first = 5, p.second = 4
3.multiset
template<class Key, class Pred = less<Key>, class A = allocator<Key> >
class multiset { …… };
Pred類型的變量決定了multiset 中的元素,“一個比另一個小”是怎麼定義的。
multiset運行過程中,比較兩個元素x,y的大小的做法,就是生成一個 Pred類型的變量,假定爲 op,若表達式op(x,y) 返回值爲true,則 x比y小。
Pred的缺省類型是 less<Key>。
less 模板的定義:template<class T>
struct less : public binary_function<T, T, bool>
{
bool operator()(const T& x, const T& y)
{ return x < y ; } const;
};
less模板是靠 < 來比較大小的multiset的成員函數
- iterator find(const T & val);
在容器中查找值爲val的元素,返回其迭代器。如果找不到,返回end()。 - iterator insert(const T & val);
將val插入到容器中並返回其迭代器。 - void insert( iterator first,iterator last);
將區間[first,last)插入容器。 - int count(const T & val);
統計有多少個元素的值和val相等。 - iterator lower_bound(const T & val);
查找一個最大的位置 it,使得[begin(),it) 中所有的元素都比 val 小。 - iterator upper_bound(const T & val);
查找一個最小的位置 it,使得[it,end()) 中所有的元素都比 val 大。 - pair<iterator,iterator> equal_range(const T & val);
同時求得lower_bound和upper_bound。 - iterator erase(iterator it);
刪除it指向的元素,返回其後面的元素的迭代器(Visual studio 2010上如此,但是在C++標準和Dev C++中,返回值不是這樣)。
注意,刪除後,該迭代器失效。
multiset 的用法
#include <set>
using namespace std;
class A { };
int main() {
multiset<A> a;
a.insert( A()); //error
}
multiset <A> a; 就等價於multiset<A, less<A>> a;插入元素時,multiset會將被插入元素和已有元素進行比較。由於less模板是用 < 進行比較的,所以,這都要求 A 的對象能用 < 比較,即適當重載了 < 很長的例子...
4.set
template<class Key, class Pred = less<Key>,class A = allocator<Key> >
class set { … }
插入set中已有的元素時,忽略插入。8.7 map和multimap
1.預備知識: pair 模板
2.multimap
template<class Key, class T, class Pred = less<Key>,class A = allocator<T> >
class multimap {
….
typedef pair<const Key, T> value_type;
…….
}; //Key 代表關鍵字的類型
multimap中的元素由 <關鍵字,值>組成,每個元素是一個pair對象,關鍵字就是first成員變量,其類型是Key
multimap 中允許多個元素的關鍵字相同。元素按照first成員變量從小到大排列,缺省情況下用 less<Key> 定義關鍵字的“小於”關係。
8.8 容器適配器
1.容器適配器
用某種順序容器來實現
讓已有的順序容器以棧/隊列的方式工作
- stack: 頭文件 <stack>
棧 -- 後進先出 - queue: 頭文件 <queue>
隊列 -- 先進先出 - priority_queue: 頭文件 <queue>
優先級隊列 -- 最高優先級元素總是第一個出列
- push: 添加一個元素;
- top: 返回棧頂部或隊頭元素的引用
- pop: 刪除一個元素
2.stack
- stack 是後進先出的數據結構
- 只能插入,刪除,訪問棧頂的元素
- 可用 vector, list, deque來實現
缺省情況下, 用deque實現
用 vector和deque實現, 比用list實現性能好template<class T, class Cont = deque<T> > class stack { … };
- stack 中主要的三個成員函數:
void push(const T & x); 將x壓入棧頂
void pop(); 彈出(即刪除)棧頂元素
T & top(); 返回棧頂元素的引用. 通過該函數, 可以讀取棧頂元素的值, 也可以修改棧頂元素
3.queue
- 和stack 基本類似, 可以用 list和deque實現
- 缺省情況下用deque實現
template<class T, class Cont = deque<T> >
class queue {
……
};
- 同樣也有push, pop, top函數
push發生在隊尾
pop, top發生在隊頭, 先進先出
4.priority_queue
- 和 queue類似, 可以用vector和deque實現
- 缺省情況下用vector實現
- priority_queue 通常用堆排序技術實現, 保證最大的元素總是在最前面
執行pop操作時, 刪除的是最大的元素
執行top操作時, 返回的是最大元素的引用
不允許修改隊頭元素 - 因爲是堆排序,它只能保證最大的元素在隊頭,不能保證內部元素的有序。特別適用於在一堆元素中間不停取最大值的情況。
- 默認的元素比較器是小於運算符 less<T>,最大的元素在隊頭
8.8 算法
1.STL 算法分類
STL中的算法大致可以分爲以下七類:
- 不變序列算法
- 變值算法
- 刪除算法
- 變序算法
- 排序算法
- 有序區間算法
- 數值算法
2.算法
大多重載的算法都是有兩個版本的
- 用 “==” 判斷元素是否相等, 或用 “<” 來比較大小
- 多出一個類型參數 “Pred” 和函數形參 “Pred op”
如下面的有兩個版本的min_element:
iterator min_element(iterator first, iterator last);
iterator min_element(iterator first, iterator last, Pred op);
2-1 不變序列算法
- 該類算法不會修改算法所作用的容器或對象
- 適用於順序容器和關聯容器
- 時間複雜度都是O(n) (因爲它們一般都會從頭到尾遍歷一下整個容器)
find:
template<class InIt, class T>
InIt find(InIt first, InIt last, const T& val);
返回區間 [first,last) 中的迭代器 i ,使得 * i == val;沒有找到,返回last
find_if:
template<class InIt, class Pred>
InIt find_if(InIt first, InIt last, Pred pr);
返回區間 [first,last) 中的迭代器 i, 使得 pr(*i) == true
for_each:
template<class InIt, class Fun>
Fun for_each(InIt first, InIt last, Fun f);
對[first, last)中的每個元素e, 執行f(e), 要求 f(e)不能改變e
count:
template<class InIt, class T>
size_t count(InIt first, InIt last, const T& val);
計算[first, last) 中等於val的元素個數(x==y爲true算等於)
count_if:
template<class InIt, class Pred>
size_t count_if(InIt first, InIt last, Pred pr);
計算[first, last) 中符合pr(e) == true 的元素e的個數
min_element:
template<class FwdIt>
FwdIt min_element(FwdIt first, FwdIt last);
返回[first,last) 中最小元素的迭代器,以 “<” 作比較器
最小指沒有元素比它小, 而不是它比別的不同元素都小
因爲即便a!= b, a<b 和b<a有可能都不成立
max_element:
template<class FwdIt>
FwdIt max_element(FwdIt first, FwdIt last);
返回[first,last) 中最大元素(不小於任何其他元素)的迭代器
以 “<” 作比較器
#include <iostream>
#include <algorithm>
using namespace std;
class A {
public:
int n;
A(int i):n(i) { }
};
bool operator<( const A & a1, const A & a2) {
cout << “< called” << endl;
if( a1.n == 3 && a2.n == 7 ) return true;//只有3<7成立
return false;
}
int main() {
A aa[] = { 3,5,7,2,1 };
cout << min_element(aa,aa+5)->n << endl;
cout << max_element(aa,aa+5)->n << endl;
return 0;
}
輸出:
< called
< called
< called
< called
3 //開始時假設3是最小值,經過4次比較<都不成立,所以3是最小值
< called //3<5不成立,最大值是3
< called //3<7成立,最大值是7
< called
< called
7
2-2 變值算法
- 此類算法會修改源區間或目標區間元素的值
- 值被修改的那個區間, 不可以是屬於關聯容器的 (因爲關聯容器是有序的,如果進行修改了,有序性就破壞了,再進行其他操作就會有問題。)
transform
template<class InIt, class OutIt, class Unop>
OutIt transform(InIt first, InIt last, OutIt x, Unop uop);
對[first,last)中的每個迭代器I,
• 執行 uop( * I ); 並將結果依次放入從 x 開始的地方
• 要求 uop( * I ) 不得改變 * I 的值
本模板返回值是個迭代器, 即 x + (last-first),即目標區域最後一個元素後面的那個位置。
• x可以和 first相等
#include <vector>
#include <iostream>
#include <numeric>
#include <list>
#include <algorithm>
#include <iterator>
using namespace std;
class CLessThen9 {//函數對象,小於9
public:
bool operator()( int n) { return n < 9; }
};
void outputSquare(int value ) { cout << value * value << " "; }//輸出平方
int calculateCube(int value) { return value * value * value; }//計算立方
int main() {
const int SIZE = 10;
int a1[] = { 1,2,3,4,5,6,7,8,9,10 };
int a2[] = { 100,2,8,1,50,3,8,9,10,2 };
vector<int> v(a1,a1+SIZE);
ostream_iterator<int> output(cout," ");
random_shuffle(v.begin(),v.end());//隨機打亂
cout << endl << "1) ";
copy( v.begin(),v.end(),output);
copy( a2,a2+SIZE,v.begin());
cout << endl << "2)";
cout << count(v.begin(),v.end(),8);
cout << endl << "3)";
cout << count_if(v.begin(),v.end(),CLessThen9());
cout << endl << "4) ";
cout << * (min_element(v.begin(), v.end()));
cout << endl << "5) ";
cout << * (max_element(v.begin(), v.end()));
cout << endl << "6) ";
cout << accumulate(v.begin(), v.end(), 0); //求和
cout << endl << "7) ";
for_each(v.begin(), v.end(), outputSquare);
vector<int> cubes(SIZE);
transform(a1, a1+SIZE, cubes.begin(), calculateCube);
cout << endl << "8) ";
copy(cubes.begin(), cubes.end(), output);
return 0;
}
輸出:
1) 5 4 1 3 7 8 9 10 6 2
2) 2
3) 6
//1) 是隨機的
4) 1
5) 100
6) 193
7)10000 4 64 1 2500 9 64 81 100 4
8)1 8 27 64 125 216 343 512 729 1000
ostream_iterator<int> output(cout ,“ ”);定義了一個 ostream_iterator<int> 對象,可以通過cout輸出以 “ ”(空格) 分隔的一個個整數
copy (v.begin(), v.end(), output);導致v的內容在 cout上輸出
copy 函數模板(算法)
OutIt copy(InIt first, InIt last, OutIt x);
本函數對每個在區間[0, last - first)中的N執行一次*(x+N) = *(first + N), 返回 x + N
對於copy(v.begin(),v.end(),output);first 和 last 的類型是 vector<int>::const_iteratoroutput 的類型是 ostream_iterator<int>
copy 的源代碼:
template<class _II, class _OI>
inline _OI copy(_II _F, _II _L, _OI _X)
{
for (; _F != _L; ++_X, ++_F)
*_X = *_F;
return (_X);
}
例子#include <iostream>
#include <fstream>
#include <string>
#include <algorithm>
#include <iterator>
using namespace std;
int main(){
int a[4] = { 1,2,3,4 };
My_ostream_iterator<int> oit(cout,"*");
copy(a,a+4,oit); //輸出 1*2*3*4*
ofstream oFile("test.txt", ios::out);
My_ostream_iterator<int> oitf(oFile,"*");
copy(a,a+4,oitf); //向test.txt文件中寫入 1*2*3*4*
oFile.close();
return 0;
}
// 如何編寫 My_ostream_iterator?
上面程序中調用語句 “copy( a,a+4,oit)” 實例化後得到copy如下:My_ostream_iterator<int> copy(int * _F, int * _L, My_ostream_iterator<int> _X)
{
for (; _F != _L; ++_X, ++_F)
*_X = *_F;
return (_X);
}
My_ostream_iterator類應該重載 “++” 和 “*” 運算符“=” 也應該被重載
#include <iterator>
template<class T>
class My_ostream_iterator:public iterator<output_iterator_tag, T>{
private:
string sep; //分隔符
ostream & os;
public:
My_ostream_iterator(ostream & o, string s):sep(s), os(o){ }
void operator ++() { }; // ++只需要有定義即可, 不需要做什麼
My_ostream_iterator & operator * () { return * this; }
My_ostream_iterator & operator = ( const T & val)
{ os << val << sep; return * this; }
};
(上面這個示例不懂可能需要看視頻講解了。)2-3 刪除算法
- 刪除一個容器裏的某些元素
- 刪除 -- 不會減少容器裏元素的個數
將所有應該被刪除的元素看做空位子
用留下的元素從後往前移, 依次去填空位子
元素往前移後, 它原來的位置也就算是空位子,也應由後面的留下的元素來填上
最後, 沒有被填上的空位子, 維持其原來的值不變 - 刪除算法不應作用於關聯容器 (也是因爲關聯容器是有序的)
或者是 sort + unique + erase。sort排序,unique刪除連續相等的元素、返回有效元素的下一個位置,erase刪除從有效元素下一個位置到最後一個元素這樣的範圍。(這個在課後一題裏用到了)
unique
template<class FwdIt>
FwdIt unique(FwdIt first, FwdIt last);
用 == 比較是否等template<class FwdIt, class Pred>
FwdIt unique(FwdIt first, FwdIt last, Pred pr);
用 pr (x,y)爲 true說明x和y相等對[first,last) 這個序列中連續相等的元素, 只留下第一個
返回值是迭代器, 指向元素刪除後的區間的最後一個元素的後面
int main(){
int a[5] = { 1,2,3,2,5 };
int b[6] = { 1,2,3,2,5,6 };
ostream_iterator<int> oit(cout,",");
int * p = remove(a,a+5,2);
cout << "1) "; copy(a,a+5,oit); cout << endl; //輸出 1) 1,3,5,2,5,
cout << "2) " << p - a << endl; //輸出 2) 3
vector<int> v(b,b+6);
remove(v.begin(), v.end(),2);
cout << "3) "; copy(v.begin(), v.end(), oit); cout << endl;
//輸出 3) 1,3,5,6,5,6,
cout << "4) "; cout << v.size() << endl;
//v中的元素沒有減少,輸出 4) 6
return 0;
}
在cout<<"1)"的地方可以看到,remove後 a 數組是1,3,5,2,5,因爲刪除兩個2後、其他元素往前移、最後兩個元素維持原樣。
可以想一下,remove可以作用於容器可能可以真的刪除了元素,但remove也可以作用於數組,對於數組怎麼把元素刪了呢。所以它只能告訴你最後一個有效元素的位置,而不能改變容器中元素的個數。
2-4 變序算法
- 變序算法改變容器中元素的順序
- 但是不改變元素的值(意思應該是元素值不變,但容器中元素的順序會變的)
- 變序算法不適用於關聯容器
- 算法複雜度大部分是O(n)的
stable_patition
把區間內滿足某個條件的元素移到前面不滿足該條件的移到後面
而對這兩部分元素, 分別保持它們原來的先後次序不變
random_shuffle
template<class RanIt>
void random_shuffle(RanIt first, RanIt last);
隨機打亂[first,last) 中的元素, 適用於能隨機訪問的容器
reverse
template<class BidIt>
void reverse(BidIt first, BidIt last);
顛倒區間[first,last)順序
next_permutation
template<class InIt>
bool next_permutaion (Init first,Init last);
求下一個排列#include <iostream>
#include <algorithm>
#include <string>
using namespace std;
int main(){
string str = "231";
char szStr[] = "324";
while (next_permutation(str.begin(), str.end())){
cout << str << endl;
}
cout << "****" << endl;
while (next_permutation(szStr,szStr + 3)){
cout << szStr << endl;
}
sort(str.begin(), str.end());
cout << "****" << endl;
while (next_permutation(str.begin(), str.end()))
{
cout << str << endl;
}
return 0;
}
//輸出:
312
321
****
342
423
432
//輸出:
132
213
231
312
321
#include <iostream>
#include <algorithm>
#include <string>
#include <list>
#include <iterator>
using namespace std;
int main(){
int a[] = { 8,7,10 };
list<int> ls(a, a+3);
while( next_permutation(ls.begin(), ls.end())) {
list<int>::iterator i;
for( i = ls.begin(); i != ls.end(); ++i)
cout << * i << " ";
cout << endl;
}
}
輸出:
8 10 7
10 7 8
10 8 7
2-5 排序算法
- 比前面的變序算法複雜度更高, 一般是O(nlog(n))
- 排序算法需要隨機訪問迭代器的支持
- 不適用於關聯容器和list (list上的迭代器是雙向的,不是隨機訪問迭代器)
template<class RanIt>
void sort(RanIt first, RanIt last);
按升序排序判斷x是否應比y靠前, 就看 x < y 是否爲true
template<class RanIt, class Pred>
void sort(RanIt first, RanIt last, Pred pr);
按升序排序判斷x是否應比y靠前, 就看 pr(x,y) 是否爲true
#include <iostream>
#include <algorithm>
using namespace std;
class MyLess {
public:
bool operator()( int n1,int n2) {
return (n1 % 10) < ( n2 % 10);
}
};//按個位數大小排序,按降序排序
int main() {
int a[] = { 14,2,9,111,78 };
sort(a, a + 5, MyLess());
int i;
for( i = 0;i < 5;i ++)
cout << a[i] << " ";
cout << endl;
sort(a, a+5, greater<int>()); //greater之前說了,誰在數學上大,反而是小。
for( i = 0;i < 5;i ++)
cout << a[i] << " ";
}
<pre name="code" class="cpp">//輸出:
111 2 14 78 9
111 78 14 9 2
sort 實際上是快速排序, 時間複雜度 O(n*log(n))
- 平均性能最優
- 但是最壞的情況下, 性能可能非常差 O(n*n)
- stable_sort 實際上是歸併排序, 特點是能保持相等元素之間的先後次序
- 平均複雜度和最壞複雜度都是 O(n*log(n))
- 在有足夠存儲空間的情況下, 複雜度爲 n * log(n), 否則複雜度爲 n * log(n) * log(n)
- stable_sort 用法和 sort相同。
2-6 有序區間算法
- 要求所操作的區間是已經從小到大排好序的
- 需要隨機訪問迭代器的支持
- 有序區間算法不能用於關聯容器和list
template<class FwdIt, class T>
bool binary_search(FwdIt first, FwdIt last, const T& val);
上面這個版本, 比較兩個元素x, y 大小時, 看 x < y 和 y<x 同時不成立纔是相等。template<class FwdIt, class T, class Pred>
bool binary_search(FwdIt first, FwdIt last, const T& val, Pred pr);
上面這個版本, 比較兩個元素x, y 大小時, 若 pr(x,y) 爲true, 則認爲x小於ylower_bound:
template<class FwdIt, class T>
FwdIt lower_bound(FwdIt first, FwdIt last, const T& val);
要求[first,last)是有序的查找[first,last)中的, 最大的位置 FwdIt, 使得[first,FwdIt)中所有的元素都比 val 小
即,對[first,FwdIt)中任一元素x,都有 x<val 成立
upper_bound
template<class FwdIt, class T>
FwdIt upper_bound(FwdIt first, FwdIt last, const T& val);
要求[first,last)是有序的查找[first,last)中的, 最小的位置 FwdIt, 使得[FwdIt,last)中所有的元素都比 val 大
即,對[FwdIt,last)中任一元素x,都有 val<x 成立
equal_range
template<class FwdIt, class T>
pair<FwdIt, FwdIt> equal_range(FwdIt first, FwdIt last,const T& val);
要求[first,last)是有序的,返回值是一個pair, 假設爲 p, 則:
• [first,p.first) 中的元素都比 val 小
• [p.second,last)中的所有元素都比 val 大 (即同時求得lower_bound和upper_bound的結果)
• p.first 就是lower_bound的結果
• p.last 就是 upper_bound的結果
merge
template<class InIt1, class InIt2, class OutIt>
OutIt merge(InIt1 first1, InIt1 last1, InIt2 first2, InIt2 last2,OutIt x);
用 < 作比較器template<class InIt1, class InIt2, class OutIt, class Pred>
OutIt merge(InIt1 first1, InIt1 last1, InIt2 first2, InIt2 last2,OutIt x, Pred pr);
用 pr 作比較器把[first1,last1), [ first2,last2) 兩個升序序列合併, 形成第3 個升序序列, 第3個升序序列以 x 開頭(後面必須要有足夠的空間)
includes
template<class InIt1, class InIt2>
bool includes(InIt1 first1, InIt1 last1, InIt2 first2, InIt2 last2);
template<class InIt1, class InIt2, class Pred>
bool includes(InIt1 first1, InIt1 last1, InIt2 first2, InIt2 last2,Pred pr);
判斷 [first2,last2)中的每個元素, 是否都在[first1,last1)中• 第一個用 <作比較器,x<y和y<x都不成立
• 第二個用 pr 作比較器, pr(x,y) == true說明 x,y相等
set_difference
template<class InIt1, class InIt2, class OutIt>
OutIt set_difference(InIt1 first1, InIt1 last1, InIt2 first2,InIt2 last2, OutIt x);
template<class InIt1, class InIt2, class OutIt, class Pred>
OutIt set_difference(InIt1 first1, InIt1 last1, InIt2 first2,InIt2 last2, OutIt x, Pred pr);
求出[first1,last1)中, 不在[first2,last2)中的元素, 放到 從 x開始的地方如果 [first1,last1) 裏有多個相等元素不在[first2,last2)中,則這多個元素也都會被放入x代表的目標區間裏
“在”和“不在”的概念與前面類似,用<或自定義比較器來判斷。
後面的set_intersection、set_symmetric_difference、set_union類似
set_intersection求共有的元素。若某個元素e 在[first1,last1)裏出現 n1次, 在[first2,last2)裏出現n2次, 則該元素在目標區間裏出現min(n1,n2)次;
set_symmetric_difference把兩個區間裏相互不在另一區間裏的元素放入x開始的地方;
set_union求兩個區間的並。若某個元素e 在[first1,last1)裏出現 n1次, 在[first2,last2)裏出現n2次, 則該元素在目標區間裏出現max(n1,n2)次。
3. bitset
template<size_t N>
class bitset
{
…..
};
實際使用的時候, N是個整型常數如:
• bitset<40> bst;
• bst是一個由40位組成的對象
• 用bitset的函數可以方便地訪問任何一位