原文地址: https://www.cnblogs.com/CnZyy/p/3317999.html
一、STL簡介
STL(Standard Template Library,標準模板庫)是惠普實驗室開發的一系列軟件的統稱。它是由Alexander Stepanov、Meng Lee和David R Musser在惠普實驗室工作時所開發出來
的?,F在雖說它主要出現在C++中,但在被引入C++之前該技術就已經存在了很長的一段時間。
STL的代碼從廣義上講分為三類:algorithm(算法)、container(容器)和iterator(迭代器),幾乎所有的代碼都采用了模板類和模版函數的方式,這相比于傳統的由函數和類
組成的庫來說提供了更好的代碼重用機會。在C++標準中,STL被組織為下面的13個頭文件:<algorithm>、<deque>、<functional>、<iterator>、<vector>、****<list>、<map>、
<memory>****、<numeric>****、<queue>****、<set>****、<stack>****和<utility>。
二、算法
大家都能取得的一個共識是函數庫對數據類型的選擇對其可重用性起著至關重要的作用。舉例來說,一個求方根的函數,在使用浮點數作為其參數類型的情況下的可重用性肯定比
使用整型作為它的參數類性要高。而C++通過模板的機制允許推遲對某些類型的選擇,直到真正想使用模板或者說對模板進行特化的時候,STL就利用了這一點提供了相當多的有用
算法。它是在一個有效的框架中完成這些算法的——你可以將所有的類型劃分為少數的幾類,然后就可以在模版的參數中使用一種類型替換掉同一種類中的其他類型。
STL提供了大約100個實現算法的模版函數,比如算法for_each將為指定序列中的每一個元素調用指定的函數,stable_sort以你所指定的規則對序列進行穩定性排序等等。這樣一來
,只要我們熟悉了STL之后,許多代碼可以被大大的化簡,只需要通過調用一兩個算法模板,就可以完成所需要的功能并大大地提升效率。
算法部分主要由頭文件<algorithm>,<numeric>和<functional>組成。
<algorithm>****是所有STL****頭文件中最大的一個(盡管它很好理解),它是由一大堆模版函數組成的,可以認為每個函數在很大程度上都是獨立的,其中常用到的功能范圍涉及到比較、交換、查找、遍歷操作、復制、修改、移除、反轉、排序、合并等等。
<numeric>****體積很小,只包括幾個在序列上面進行簡單數學運算的模板函數,包括加法和乘法在序列上的一些操作。
<functional>****中則定義了一些模板類,用以聲明函數對象。
三、容器
在實際的開發過程中,數據結構本身的重要性不會遜于操作于數據結構的算法的重要性,當程序中存在著對時間要求很高的部分時,數據結構的選擇就顯得更加重要。
經典的數據結構數量有限,但是我們常常重復著一些為了實現向量、鏈表等結構而編寫的代碼,這些代碼都十分相似,只是為了適應不同數據的變化而在細節上有所出入。STL容器
就為我們提供了這樣的方便,它允許我們重復利用已有的實現構造自己的特定類型下的數據結構,通過設置一些模版類,STL容器對最常用的數據結構提供了支持,這些模板的參數
允許我們指定容器中元素的數據類型,可以將我們許多重復而乏味的工作簡化。
容器部分主要由頭文件<vector>,<list>,<deque>,<set>,<map>,<stack>和<queue>組成。對于常用的一些容器和容器適配器(可以看作由其它容器實現的容器),可以通過下表總結一下它們和相應頭文件的對應關系。
向量(vector)連續存儲的元素<vector>
列表(list)由節點組成的雙向鏈表,每個結點包含著一個元素<list>
雙隊列(deque)連續存儲的指向不同元素的指針所組成的數組<deque>
集合(set)由節點組成的紅黑樹,每個節點都包含著一個元素,節點之間以某種作用于元素對的謂詞排列,沒有兩個不同的元素能夠擁有相同的次序 <set>
多重集合(multiset) 允許存在兩個次序相等的元素的集合 <set>
棧(stack)后進先出的值的排列 <stack>
隊列(queue)先進先出的執的排列 <queue>
優先隊列(priority_queue)元素的次序是由作用于所存儲的值對上的某種謂詞決定的的一種隊列 <queue>
映射(map) 由{鍵,值}對組成的集合,以某種作用于鍵對上的謂詞排列 <map>
多重映射(multimap)允許鍵對有相等的次序的映射 <map>
四、迭代器
下面要說的迭代器從作用上來說是最基本的部分,可是理解起來比前兩者都要費力一些(至少筆者是這樣)。軟件設計有一個基本原則,所有的問題都可以通過引進一個間接層來
簡化,這種簡化在STL中就是用迭代器來完成的
。
概括來說,迭代器在STL中用來將算法和容器聯系起來,起著一種黏和劑的作用。幾乎STL提供的所有算法都是通過迭代器存取元素序列進行工作的,每一個容器都定義了其本身所專有的迭代器,用以存取容器中的元素。
迭代器部分主要由頭文件<utility>,<iterator>和<memory>組成。
<utility>是一個很小的頭文件,它包括了貫穿使用在STL****中的幾個模板的聲明,
<iterator>中提供了迭代器使用的許多方法,而對于<memory>的描述則十分的困難,它以不同尋常的方式為容器中的元素分配存儲空間,同時也為某些算法執行期間產生的臨時對象提供機制,<memory>中的主要部分是模板類allocator,它負責產生所有容器中的默認分配器
----------------------------------------------------分割線---------------------------------------------------------------
學完c++快一年了,感覺很有遺憾,因為一直沒有感覺到c++的強大之處,當時最大的感覺就是這個東西的輸入輸出比C語言要簡單好寫。
后來我發現了qt,opencv,opengl,原來,c++好玩的狠。
在這些圖形庫之外,最常用的可能就是STL,這個東西由于當時學c++的時候迷迷糊糊,完全是一頭霧水,上學期數據結構之后開始有點兒開竅了,現在把才c++STL中常用的函數,用法貼一下,也是記錄一下,希望能給一樣迷糊的盆友們一些幫助。
整理自《ACM程序設計》
迭代器(iterator)
個人理解就是把所有和迭代有關的東西給抽象出來的,不管是數組的下標,指針,for里面的、list里面的、vector里面的,抽象一下變成了iterator
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> v;
for(int i = 0; i < 10; ++i )
{
v.push_back(i);
}
for(vector<int>::iterator it = v.begin(); it != v.end(); ++it)
{
cout << *it << " ";
}
cout << endl;
return 0;
}
求和(<numeric> accumulate)
accumulate(v.begin(),v.end(),0),把從 v.begin() 開始到 v.end()結束所有的元素加到 0上面去
#include <iostream>
#include <vector>
#include <numeric>
using namespace std;
int main()
{
vector<int> v;
for(int i = 0; i < 10; ++i )
{
v.push_back(i);
}
for(vector<int>::iterator it = v.begin(); it != v.end(); ++it)
{
cout << *it << " ";
}
cout << endl;
cout << accumulate(v.begin(),v.end(),0) << endl;
return 0;
}
vector(動態數組)
vector有內存管理的機制,也就是說對于插入和刪除,vector可以動態調整所占用的內存空間。
vector相關函數
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> v;
v.push_back(3); //數組尾部插入3
v.push_back(2);
v.push_back(1);
v.push_back(0);
cout << " 下標 " << v[3] << endl;
cout << " 迭代器 " << endl;
for(vector<int>::iterator i = v.begin();i!= v.end();++i)
{
cout << *i << " ";
}
cout << endl;
//在第一個元素之前插入111 insert begin+n是在第n個元素之前插入
v.insert(v.begin(),111);
//在最后一個元素之后插入222 insert end + n 是在n個元素之后插入
v.insert(v.end(),222);
for(vector<int>::iterator i = v.begin();i!= v.end();++i)
{
cout << *i << " ";
}
cout << endl;
vector<int> arr(10);
for(int i = 0; i < 10; i++)
{
arr[i] = i;
}
for(vector<int>::iterator i = arr.begin();i!= arr.end();++i)
{
cout << *i << " ";
}
cout << endl;
//刪除 同insert
arr.erase(arr.begin());
for(vector<int>::iterator i = arr.begin();i!= arr.end();++i)
{
cout << *i << " " ;
}
cout << endl ;
arr.erase(arr.begin(),arr.begin()+5);
for(vector<int>::iterator i = arr.begin();i!= arr.end();++i)
{
cout << *i << " " ;
}
cout << endl ;
return 0 ;
}
數組轉置 (<algorithm> reverse)
reverse(v.begin(),v.end())
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
int main()
{
vector<int> v;
for(int i = 0; i < 10; ++i)
{
v.push_back(i);
}
for(vector<int>::iterator it = v.begin(); it != v.end(); ++it)
{
cout << *it << " ";
}
cout << endl;
reverse(v.begin(),v.end());
for(vector<int>::iterator it = v.begin(); it != v.end(); ++it)
{
cout << *it << " ";
}
cout << endl;
return 0;
}
排序(<algorithm> sort)
sort(v.begin(),v.end())
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
bool Comp(const int &a,const int &b)
{
return a>b;
}
int main()
{
vector<int> v;
v.push_back(1);
v.push_back(3);
v.push_back(2);
v.push_back(55);
v.push_back(-1);
v.push_back(0);
v.push_back(2);
v.push_back(3);
v.push_back(4);
for(vector<int>::iterator it = v.begin(); it != v.end(); ++it)
{
cout << *it << " ";
}
cout << endl;
//默認升序
sort(v.begin(),v.end());
for(vector<int>::iterator it = v.begin(); it != v.end(); ++it)
{
cout << *it << " ";
}
cout << endl;
//用降序 需要自定義一個降序函數
sort(v.begin(),v.end(),Comp);
for(vector<int>::iterator it = v.begin(); it != v.end(); ++it)
{
cout << *it << " ";
}
cout << endl;
return 0;
}
字符串(<string>)
輸入
#include<iostream>
#include<string>
#include<cstdio>
using namespace std;
int main()
{
string s1;
s1 = "hello";
string s2;
char s[1024];
//scanf 輸入速度比cin快的多
//scanf 是C函數,不可以輸入string
scanf("%s",s);
s2 = s;
cout << s1 << endl;
cout << s2 << endl;
return 0;
}
#include<iostream>
#include<string>
using namespace std;
int main()
{
string s;
s = "0123456789";
cout << s << endl;
string::iterator it = s.begin();
//刪除s[3]
s.erase(it+3);
cout << s << endl;
//刪除s[1]~s[3]
s = "0123456789";
s.erase(it + 1,it + 4);
cout << s << endl;
//全部刪除
s.clear();
cout << "clear : " << s << endl;
return 0;
}
查找(find)
用find找到string里面第一個要找到元素(char或者串),找到返回數組下標,找不到返回end()迭代器
string和vector有很多相同的東西,比如length(),size(),empty(),reverse(),相對也容易,就不一一說了。
數字化處理(string)
經常會遇到這樣一種情況,有一個數字,需要把每一位給提取出來,如果用取余數的方法,花費的時間就會很長,所以可以當成字符串來處理,方便、省時。
例子:求一個整數各位數的和
#include<iostream>
#include<string>
using namespace std;
int main()
{
string s;
s = "123456789";
int sum = 0;
for(int i = 0; i < s.size(); ++i)
{
switch(s[i])
{
case '1': sum += 1;break;
case '2': sum += 2;break;
case '3': sum += 3;break;
case '4': sum += 4;break;
case '5': sum += 5;break;
case '6': sum += 6;break;
case '7': sum += 7;break;
case '8': sum += 8;break;
case '9': sum += 9;break;
}
}
cout << sum << endl;
return 0;
}
string與char *
#include<iostream>
#include<string>
#include<cstdio>
using namespace std;
int main()
{
string s_string;
char s_char[1000];
scanf("%s",s_char);
s_string = s_char;
//printf輸出char* 時用c_str處理
printf(s_string.c_str());
cout << endl;
printf("%s",s_char);
cout << endl;
cout << s_char << endl;
cout << s_string << endl;
return 0;
}
sscanf
#include<iostream>
#include<string>
#include<cstdio>
using namespace std;
int main()
{
string s1,s2,s3;
char sa[100],sb[100],sc[100];
sscanf("abc 123 wcd","%s%s%s",sa,sb,sc);
s1 = sa;
s2 = sb;
s3 = sc;
cout << s1 << " " << s2 << " " << s3 << endl;
//將字符串分離成數字,分隔符為',''$'
int a,b,c;
sscanf("4,5$6","%d,%d$%d",&a,&b,&c);
cout << a << " " << b << " " << c << endl;
return 0;
}
string與數值相互轉換( sprintf <sstream> )
#include<iostream>
#include<string>
#include<sstream>
#include<cstdio>
using namespace std;
//c++ 方法 把數轉換為string
string converToString(double x)
{
ostringstream o;
if( o << x)
{
// str()沒有'\0' c_str有
return o.str();
}
return "error";
}
double converFromString(const string &s)
{
istringstream i(s);
double x;
if( i >> x)
{
return x;
}
//if error
return 0.0;
}
int main()
{
char b[100];
string s1;
//c語言方法
sprintf(b,"%d",1987);
s1 = b;
cout << s1 << endl;
string s2 = converToString(1954);
cout << s2 << endl;
string s3 = "202";
int c = converFromString(s3);
cout << c << endl;
string s4 = "casacsa6";
int d = converFromString(s4);
cout << d << endl;
string s5 = "21abf4";
int f = converFromString(s5);
cout << f << endl;
return 0;
}
set容器
set是用紅黑樹的平衡二叉索引樹的數據結構來實現的,插入時,它會自動調節二叉樹排列,把元素放到適合的位置,確保每個子樹根節點的鍵值大于左子樹所有的值、小于右子樹所有的值,插入重復數據時會忽略。set迭代器采用中序遍歷,檢索效率高于vector、deque、list,并且會將元素按照升序的序列遍歷。set容器中的數值,一經更改,set會根據新值旋轉二叉樹,以保證平衡,構建set就是為了快速檢索(python中的set一旦建立就是一個常量,不能改的)。
multiset,與set不同之處就是它允許有重復的鍵值。
正反遍歷,迭代器iterator、reverse_iterator
#include<iostream>
#include<set>
using namespace std;
int main()
{
set<int> v;
v.insert(1);
v.insert(3);
v.insert(5);
v.insert(2);
v.insert(4);
v.insert(3);
//中序遍歷 升序遍歷
for(set<int>::iterator it = v.begin(); it != v.end(); ++it)
{
cout << *it << " ";
}
cout << endl;
for(set<int>::reverse_iterator rit = v.rbegin(); rit != v.rend(); ++rit)
{
cout << *rit << " ";
}
cout << endl;
return 0;
}
自定義比較函數,insert的時候,set會使用默認的比較函數(升序),很多情況下需要自己編寫比較函數。
1、如果元素不是結構體,可以編寫比較函數,下面這個例子是用降序排列的(和上例插入數據相同):
#include<iostream>
#include<set>
using namespace std;
struct Comp
{
//重載()
bool operator()(const int &a, const int &b)
{
return a > b;
}
};
int main()
{
set<int,Comp> v;
v.insert(1);
v.insert(3);
v.insert(5);
v.insert(2);
v.insert(4);
v.insert(3);
for(set<int,Comp>::iterator it = v.begin(); it != v.end(); ++it)
{
cout << *it << " ";
}
cout << endl;
for(set<int,Comp>::reverse_iterator rit = v.rbegin(); rit != v.rend(); ++rit)
{
cout << *rit << " ";
}
cout << endl;
return 0;
}
2、元素本身就是結構體,直接把比較函數寫在結構體內部,下面的例子依然降序:
#include<iostream>
#include<set>
#include<string>
using namespace std;
struct Info
{
string name;
double score;
//重載 <
bool operator < (const Info &a) const
{
return a.score < score;
}
};
int main()
{
set<Info> s;
Info info;
info.name = "abc";
info.score = 123.3;
s.insert(info);
info.name = "EDF";
info.score = -23.53;
s.insert(info);
info.name = "xyz";
info.score = 73.3;
s.insert(info);
for(set<Info>::iterator it = s.begin(); it != s.end(); ++it)
{
cout << (*it).name << ":" << (*it).score << endl;
}
cout << endl;
for(set<Info>::reverse_iterator rit = s.rbegin(); rit != s.rend(); ++rit)
{
cout << (*rit).name << ":" << (*rit).score << endl;
}
cout << endl;
return 0;
}
multiset與set的不同之處就是key可以重復,以及erase(key)的時候會刪除multiset里面所有的key并且返回刪除的個數。
map
map也是使用紅黑樹,他是一個鍵值對(key:value映射),便利時依然默認按照key程序的方式遍歷,同set。
#include<iostream>
#include<map>
#include<string>
using namespace std;
int main()
{
map<string,double> m;
//聲明即插入
m["li"] = 123.4;
m["wang"] = 23.1;
m["zhang"] = -21.9;
m["abc"] = 12.1;
for(map<string,double>::iterator it = m.begin(); it != m.end(); ++it)
{
//first --> key second --> value
cout << (*it).first << ":" << (*it).second << endl;
}
cout << endl;
return 0;
}
用map實現數字分離
string --> number
之前用string進行過數字分離,現在使用map
#include<iostream>
#include<map>
#include<string>
using namespace std;
int main()
{
map<char,int> m;
m['0'] = 0;
m['1'] = 1;
m['2'] = 2;
m['3'] = 3;
m['4'] = 4;
m['5'] = 5;
m['6'] = 6;
m['7'] = 7;
m['8'] = 8;
m['9'] = 9;
/*
等價于
for(int i = 0; i < 10; ++i)
{
m['0' + i] = i;
}
*/
string sa;
sa = "9876543210";
int sum = 0;
for( int i = 0; i < sa.length(); ++i)
{
sum += m[sa[i]];
}
cout << sum << endl;
return 0;
}
number --> string
#include <iostream>
#include <map>
#include <string>
using namespace std;
int main()
{
map<int,char> m;
for(int i = 0; i < 10; ++i)
{
m[i] = '0' + i;
}
int n = 7;
string out = "the number is :";
cout << out + m[n] << endl;
return 0;
}
multimap
multimap由于允許有重復的元素,所以元素插入、刪除、查找都與map不同。
插入insert(pair<a,b>(value1,value2))
#include <iostream>
#include <map>
#include <string>
using namespace std;
int main()
{
multimap<string,double> m;
m.insert(pair<string,double>("Abc",123.2));
m.insert(pair<string,double>("Abc",123.2));
m.insert(pair<string,double>("xyz",-43.2));
m.insert(pair<string,double>("dew",43.2));
for(multimap<string,double>::iterator it = m.begin(); it != m.end(); ++it )
{
cout << (*it).first << ":" << (*it).second << endl;
}
cout << endl;
return 0;
}
至于刪除和查找,erase(key)會刪除掉所有key的map,查找find(key)返回第一個key的迭代器
deque
deque和vector一樣,采用線性表,與vector唯一不同的是,deque采用的分塊的線性存儲結構,每塊大小一般為512字節,稱為一個deque塊,所有的deque塊使用一個Map塊進行管理,每個map數據項記錄各個deque塊的首地址,這樣以來,deque塊在頭部和尾部都可已插入和刪除元素,而不需要移動其它元素。使用push_back()方法在尾部插入元素,使用push_front()方法在首部插入元素,使用insert()方法在中間插入元素。一般來說,當考慮容器元素的內存分配策略和操作的性能時,deque相對vectore更有優勢。(下面這個圖,我感覺Map塊就是一個list< map<deque名字,deque地址> >)
插入刪除
遍歷當然可以使用下標遍歷,在這里使用迭代器。
#include <iostream>
#include <deque>
using namespace std;
int main()
{
deque<int> d;
//尾部插入
d.push_back(1);
d.push_back(3);
d.push_back(2);
for(deque<int>::iterator it = d.begin(); it != d.end(); ++it )
{
cout << (*it) << " ";
}
cout << endl << endl;
//頭部插入
d.push_front(10);
d.push_front(-23);
for(deque<int>::iterator it = d.begin(); it != d.end(); ++it )
{
cout << (*it) << " ";
}
cout << endl << endl;
d.insert(d.begin() + 2,9999);
for(deque<int>::iterator it = d.begin(); it != d.end(); ++it )
{
cout << (*it) << " ";
}
cout << endl << endl;
//反方向遍歷
for(deque<int>::reverse_iterator rit = d.rbegin(); rit != d.rend(); ++rit )
{
cout << (*rit) << " ";
}
cout << endl << endl;
//刪除元素pop pop_front從頭部刪除元素 pop_back從尾部刪除元素 erase中間刪除 clear全刪
d.clear();
d.push_back(1);
d.push_back(2);
d.push_back(3);
d.push_back(4);
d.push_back(5);
d.push_back(6);
d.push_back(7);
d.push_back(8);
for(deque<int>::iterator it = d.begin(); it != d.end(); ++it )
{
cout << (*it) << " ";
}
cout << endl;
d.pop_front();
d.pop_front();
for(deque<int>::iterator it = d.begin(); it != d.end(); ++it )
{
cout << (*it) << " ";
}
cout << endl;
d.pop_back();
d.pop_back();
for(deque<int>::iterator it = d.begin(); it != d.end(); ++it )
{
cout << (*it) << " ";
}
cout << endl;
d.erase(d.begin() + 1);
for(deque<int>::iterator it = d.begin(); it != d.end(); ++it )
{
cout << (*it) << " ";
}
cout << endl;
return 0;
}
list
list<int> l
插入:push_back尾部,push_front頭部,insert方法前往迭代器位置處插入元素,鏈表自動擴張,迭代器只能使用++--操作,不能用+n -n,因為元素不是物理相連的。
遍歷:iterator和reverse_iterator正反遍歷
刪除:pop_front刪除鏈表首元素;pop_back()刪除鏈表尾部元素;erase(迭代器)刪除迭代器位置的元素,注意只能使用++--到達想刪除的位置;remove(key) 刪除鏈表中所有key的元素,clear()清空鏈表。
查找:it = find(l.begin(),l.end(),key)
排序:l.sort()
刪除連續重復元素:l.unique() 【2 8 1 1 1 5 1】 --> 【 2 8 1 5 1】
bitset
從來沒用過,上兩幅圖吧就:
stack(后進先出)
這個印象深刻,學數據結構的時候做表達式求值的就是用的棧。
#include <iostream>
#include <stack>
using namespace std;
int main()
{
stack<int> s;
s.push(1);
s.push(2);
s.push(4);
s.push(5);
cout << s.size() << endl;
while(s.empty() != true)
{
cout << s.top() << endl;
s.pop();
}
return 0;
}
stack然我唯一費解之處在于,貌似它沒有iterator,可以試試s.begin()編譯器報錯的。
queue(先進先出)
queue有入隊push(插入)、出隊pop(刪除)、讀取隊首元素front、讀取隊尾元素back、empty,size這幾種方法
priority_queue(最大元素先出)
#include <iostream>
#include <queue>
using namespace std;
int main()
{
priority_queue<int> pq;
pq.push(1);
pq.push(3);
pq.push(2);
pq.push(8);
pq.push(9);
pq.push(0);
cout << "size: " << pq.size() << endl;
while(pq.empty() != true)
{
cout << pq.top() << endl;
pq.pop();
}
return 0;
}
重載操作符同set重載操作符。