C++中STL使用
1.1概述
STL = Standard Template Library,標準模板庫,惠普實驗室開發的一系列軟體的統稱。它是由Alexander Stepanov、Meng Lee和David R Musser在惠普實驗室工作時所開發出來的。從根本上說,STL是一些“容器”的集合,這些“容器”有list,vector,set,map等,STL也是演算法和其他一些元件的集合。這裡的“容器”和演算法的集合指的是世界上很多聰明人很多年的傑作。STL的目的是標準化元件,這樣就不用重新開發,可以使用現成的元件。STL現在是C++的一部分。
在C++標準中,STL被組織為下面的17個頭檔案:<algorithm>(演算法)、<deque>(雙佇列)、<functional>(功能)、<iterator>(迭代器)、<array>(陣列)、<vector>(向量)、<list>(連結串列)、<forward_list>、<map>(對映)、<unordered_map>(無序)、<memory>(記憶)、<numeric>(數字)、<queue>(列隊)、<set>(集合)、<unordered_set>(無序)、<stack>(堆)和<utility>(實用)。
1.2組成部分
STL可分為容器(containers)、迭代器(iterators)、空間配置器(allocator)、配接器(adapters)、演算法(algorithms)、仿函式(functors)六個部分。
1.2.1容器
在實際的開發過程中,資料結構本身的重要性不會遜於操作於資料結構的演算法的重要性,當程式中存在著對時間要求很高的部分時,資料結構的選擇就顯得更加重要。
經典的資料結構數量有限,但是我們常常重複著一些為了實現向量、連結串列等結構而編寫的程式碼,這些程式碼都十分相似,只是為了適應不同資料的變化而在細節上有所出入。STL容器就為我們提供了這樣的方便,它允許我們重複利用已有的實現構造自己的特定型別下的資料結構,通過設定一些模板類,STL容器對最常用的資料結構提供了支援,這些模板的引數允許我們指定容器中元素的資料型別,可以將我們許多重複而乏味的工作簡化。
容器部分主要由標頭檔案<vector>,<list>,<deque>,<set>,<map>,<stack>和<queue>組成。對於常用的一些容器和容器介面卡(可以看作由其它容器實現的容器),可以通過下表總結一下它們和相應標頭檔案的對應關係。
序列式容器
向量(vector) 連續儲存的元素<vector>;
列表(list) 由節點組成的雙向連結串列,每個結點包含著一個元素<list>;
雙端佇列(deque) 連續儲存的指向不同元素的指標所組成的陣列<deque>;
容器介面卡
棧(stack) 後進先出的值的排列 <stack>;
佇列(queue) 先進先出的值的排列 <queue>;
優先佇列(priority_queue) 元素的次序是由作用於所儲存的值對上的某種謂詞決定的的一種佇列 <queue>;
關聯式容器
集合(set) 由節點組成的紅黑樹,每個節點都包含著一個元素,節點之間以某種作用於元素對的謂詞排列,沒有兩個不同的元素能夠擁有相同的次序 <set>;
多重集合(multiset) 允許存在兩個次序相等的元素的集合 <set>;
對映(map) 由{鍵,值}對組成的集合,以某種作用於鍵對上的謂詞排列 <map>;
多重對映(multimap) 允許鍵對有相等的次序的對映 <map>;
1.2.2迭代器
迭代器用於遍歷物件集合的元素。這些集合可能是容器,也可能是容器的子集
下面要說的迭代器從作用上來說是最基本的部分,可是理解起來比前兩者都要費力一些(至少筆者是這樣)。軟體設計有一個基本原則,所有的問題都可以通過引進一個間接層來簡化,這種簡化在STL中就是用迭代器來完成的。概括來說,迭代器在STL中用來將演算法和容器聯絡起來,起著一種黏和劑的作用。幾乎STL提供的所有演算法都是通過迭代器存取元素序列進行工作的,每一個容器都定義了其本身所專有的迭代器,用以存取容器中的元素。
迭代器部分主要由標頭檔案<utility>,<iterator>和<memory>組成。<utility>是一個很小的標頭檔案,它包括了貫穿使用在STL中的幾個模板的宣告,<iterator>中提供了迭代器使用的許多方法,而對於<memory>的描述則十分的困難,它以不同尋常的方式為容器中的元素分配儲存空間,同時也為某些演算法執行期間產生的臨時物件提供機制,<memory>中的主要部分是模板類allocator,它負責產生所有容器中的預設分配器。
1.2.3演算法
大家都能取得的一個共識是函式庫對資料型別的選擇對其可重用性起著至關重要的作用。舉例來說,一個求方根的函式,在使用浮點數作為其引數型別的情況下的可重用性肯定比使用整型作為它的引數型別要高。而C++通過模板的機制允許推遲對某些型別的選擇,直到真正想使用模板或者說對模板進行特化的時候,STL就利用了這一點提供了相當多的有用演算法。它是在一個有效的框架中完成這些演算法的——你可以將所有的型別劃分為少數的幾類,然後就可以在模版的引數中使用一種型別替換掉同一種類中的其他型別。
STL提供了大約100個實現演算法的模版函式,比如演算法for_each將為指定序列中的每一個元素呼叫指定的函式,stable_sort以你所指定的規則對序列進行穩定性排序等等。這樣一來,只要我們熟悉了STL之後,許多程式碼可以被大大的化簡,只需要通過呼叫一兩個演算法模板,就可以完成所需要的功能並大大地提升效率。
演算法部分主要由標頭檔案<algorithm>,<numeric>和<functional>組成。<algorithm>是所有STL標頭檔案中最大的一個(儘管它很好理解),它是由一大堆模版函式組成的,可以認為每個函式在很大程度上都是獨立的,其中常用到的功能範圍涉及到比較、交換、查詢、遍歷操作、複製、修改、移除、反轉、排序、合併等等。<numeric>體積很小,只包括幾個在序列上面進行簡單數學運算的模板函式,包括加法和乘法在序列上的一些操作。<functional>中則定義了一些模板類,用以宣告函式物件。
2 STL容器操作
2.1 Vector
2.1.1向量的介紹
向量 vector 是一種物件實體, 能夠容納許多其他型別相同的元素, 因此又被稱為容器。 與string相同, vector 同屬於STL(StandardTemplate Library, 標準模板庫)中的一種自定義的資料型別, 可以廣義上認為是陣列的增強版。在使用它時, 需要包含標頭檔案vector, #include<vector>。
2.1.2向量的宣告及初始化
vector<int>a ; //宣告一個int型向量a vector<int> a(10) ; //宣告一個初始大小為10的向量 vector<int> a(10,1) ; //宣告一個初始大小為10且初始值都為1的向量 vector<int> b(a) ; //宣告並用向量a初始化向量b vector<int> b(a.begin(),a.begin()+3) ; //將a向量中從第0個到第2個(共3個)作為向量b的初始值
除此之外,還可以直接使用陣列來初始化向量:
int n[]= {1,2,3,4, 5} ;
vector<int>a(n, n+5) ; //將陣列n的前5個元素作為向量a的初值
vector<int>a(&n[1], &n[4]);//將n[1]-n[4]範圍內的元素作為向量a的初值
2.1.3元素的輸入及訪問
元素的輸入和訪問可以像操作普通的陣列那樣, 用cin>>進行輸入, cout<<a[n]這樣進行輸出:
2.1.4例項
#include<iostream> #include<vector> using namespace std ; //使用c++標準程式庫三種方法之一 int main() { vector<int> a(10,0) ; //大小為10初值為0的向量a //對其中部分元素進行輸入 cin >>a[2]; //第三個數 cin >>a[5];//第六個數 cin >>a[6];//第七個數 //全部輸出 int i ; for(i=0; i<a.size(); i++) // a.size() 陣列大小 cout<<a[i]<<" " ; return 0 ; }
輸入:
1 2 3
輸出:
0 0 1 0 0 2 30 0 0
例項:
#include <vector>
#include<stdio.h>
#include <iostream>
using namespace std;
int main(){
std::vector<int> vec;
for(int i=0;i<100;i++) vec.push_back(i);
printf("10:%d\n",vec[10]);
printf("size:%d\n",vec.size());
printf("**********************************\n");
std::vector<int>::iterator it =vec.begin()+10;
vec.erase(it);
printf("10:%d\n",vec[10]);
printf("size:%d\n",vec.size());
return 0;
}
結果:
10:10
size:100
**********************************
10:11
size:99
2.2 List
2.2.1 list的介紹
Lists將元素按順序儲存在連結串列中.與向量(vectors)相比, 它允許快速的插入和刪除,但是隨機訪問卻比較慢.list是雙向迴圈連結串列,每一個元素都知道前面一個元素和後面一個元素。在STL中,list和vector一樣,是兩個常被使用的容器。和vector不一樣的是,list不支援對元素的任意存取。list中提供的成員函式與vector類似,不過list提供對錶首元素的操作push_front(前端插入元素)、pop_front,這是vector不具備的。和vector另一點不同的是,list的迭代器不會存在失效的情況,他不像vector會保留備份空間,在超過容量額度時重新全部分配記憶體,導致迭代器失效;list沒有備份空間的概念,出入一個元素就申請一個元素的空間,所以它的迭代器不會失效。
2.2.2建構函式
1)建構函式
list<Elem> c:建立一個空的list;
list<Elem> c1(c2):複製另一個同類型元素的list;
list<Elem>c(n):建立n個元素的list,每個元素值由預設建構函式確定;
list<Elem>c(n,elem):建立n個元素的list,每個元素的值為elem;
list<Elem>c(begin,end):由迭代器建立list,迭代區間為[begin,end);
2)大小、判斷函式
Int size() const:返回容器元素個數;
bool empty() const:判斷容器是否為空,若為空則返回true;
3) 增加、刪除函式
void push_back(const T&x):list元素尾部增加一個元素x;
void push_front(const T&x):list元素首元素錢新增一個元素X;
void pop_back():刪除容器尾元素,當且僅當容器不為空;
void pop_front():刪除容器首元素,當且僅當容器不為空;
void remove(constT& x):刪除容器中所有元素值等於x的元素;
void clear():刪除容器中的所有元素;
iterator insert(iterator it,const T& x ):在迭代器指標it前插入元素x,返回x迭代器指標;
void insert(iteratorit,size_type n,const T& x):迭代器指標it前插入n個相同元素x;
void insert(iteratorit,const_iterator first,const_iteratorlast):把[first,last)間的元素插入迭代器指標it前;
iterator erase(iterator it):刪除迭代器指標it對應的元素;
iterator erase(iteratorfirst,iterator last):刪除迭代器指標[first,last)間的元素;
2.2.3例項
#include<iostream>
#include<string>
#include<list>
usingnamespace std;
typedef list<string> LISTSTR;
int main()
{
LISTSTR test;
test.push_back("back"); //元素尾部加"back"
test.push_back("middle");//元素尾部加"middle"
test.push_back("front"); //元素尾部加"front" backmiddle front
cout<<test.front()<<endl; // test裡的一個元素
cout<<*test.begin()<<endl; // test.begin指向空間的一個值
cout<<endl;
cout<<test.back()<<endl; //輸出最後一個元素
cout<<*(test.rbegin())<<endl; //指標指向最後一個元素
cout<<endl;
test.pop_front(); //刪除首元素
test.pop_back(); //刪除尾元素
cout<<test.front()<<endl; //輸入首元素
return 0;
}
結果:
back
back
front
front
middle
removes例項
#include <iostream>
#include <map>
#include <string>
#include <list>
using namespace std;
int main()
{
list<int> numbers;
list<int>::iterator number_iter;
for ( int number = 0; number <= 6;number ++ )
{
numbers.push_back(number);
}
numbers.push_back(2);
numbers.push_back(3);
cout<<"size為 "<<numbers.size<<endl;
cout<<"remove前 ";
for(number_iter = numbers.begin();number_iter != numbers.end(); number_iter++)
{
cout<< " "<< *number_iter;
}
cout << endl;
numbers.remove(2);
cout<<"remove後 ";
for(number_iter = numbers.begin();number_iter != numbers.end(); number_iter++)
{
cout << " "<< *number_iter;
}
cout << endl;
return 0;
}
結果:
size為 9
remove前 0 1 2 3 4 5 6 2 3
remove後 0 1 3 4 5 6 3
2.3 Map2.3.1 map的介紹
Map是c++的一個標準容器,她提供了很好一對一的關係,在一些程式中建立一個map可以起到事半功倍的效果,總結了一些map基本簡單實用的操作!標頭檔案#include<map>. map是模板,一個map變數key和value兩個值,你在這裡是想用類似map<int,int> m_map的變數來表示揹包裡的東西,m_map->first可以取得key值,m_map->second可以取得value值;map自動按照key值按升序排列,key的值不能修改,有且只有一個key,可以修改value的值,value的值可以覆蓋。
2.3.2 定義
map<string, int>my_Map;
或者是typedef map<string, int>MY_MAP;
MY_MAP my_Map;
1)map最基本的建構函式map<string , int >mapstring; map<int ,string >mapint;
map<sring, char>mapstring; map< char ,string>mapchar;
map<char ,int>mapchar; map<int ,char >mapint;
2)map新增資料map<int ,string> maplive; //map<int,string>型別,maplive變數名
1.maplive.insert(pair<int,string>(102,"aclive"));
2.maplive.insert(map<int,string>::value_type(321,"hai"));
3.maplive[112]="April"; //map中最簡單最常用的插入新增!
2.3.3例項例項:
#include<map>
#include<iostream>
using namespace std ; //使用c++標準程式庫三種方法之一
int main()
{
map<int,string> mapStudent;
mapStudent.insert(make_pair(1,"student_one"));
mapStudent.insert(pair<int,string>(2,"student_two"));
mapStudent.insert(pair<int,string>(3,"student_three"));
map<int,string>::iterator iter;
for(iter = mapStudent.begin();iter != mapStudent.end();iter++)
cout<<iter->first<<" "<<iter->second<<endl;
return 0 ;
}
輸出:1 student_one
2 student_two
3student_three
3)map中元素的查詢
find()函式返回一個迭代器指向鍵值為key的元素,如果沒找到就返回指向map尾部的迭代器。
map<int ,string >::iterator l_it;// iterator迭代器
l_it=maplive.find(112);
if(l_it==maplive.end())
cout<<"we do not find 112"<<endl;
else cout<<"wo find 112"<<endl;
例項:
#include<map>
#include<iostream>
using namespace std ; //使用c++標準程式庫三種方法之一
int main()
{
map<int,string> mapData;
mapData.insert(make_pair(1,"SKT1"));
mapData.insert(make_pair(2,"SKT2"));
for(map<int,string>::iterator l_it = mapData.begin();l_it != mapData.end();l_it++)
{
l_it = mapData.find(2);
if(l_it != mapData.end())
{
cout<<"we find 2"<<endl;
}
else
cout<<"we can not find 2"<<endl;
break;
}
return 0 ;
}
結果:
we find 2
4)map中元素的刪除
如果刪除112;
map<int ,string >::iterator l_it;;
l_it=maplive.find(112);
if(l_it==maplive.end())
cout<<"we do not find 112"<<endl;
else maplive.erase(l_it); //delete 112;
例項:
#include <iostream>
#include <map>
#include <string>
using namespace std;
int main()
{
map<string, string> mapData;
map<string ,string >::iterator iter;
mapData.insert(make_pair("a","aaa" ));
mapData["b"] = "bbb";
mapData["c"] = "ccc";
// // iterator迭代器
/*for(map<string, string>::iterator pIter=mapData.begin(); pIter!=mapData.end();pIter++)
{
if(pIter->first == "b")
{
mapData.erase(pIter++);
}
else
pIter++;
}*/
mapData.erase("a");
for(iter = mapData.begin();iter !=mapData.end();iter++)
{
cout<<iter->first<<""<<iter->second<<endl;
}
}
結果:
b bbb
c ccc
結構體例項:
#include<stdio.h>
#include <iostream>
#include <map>
#include <string>
using namespace std;
#define MAX_NUMBER_PEOPLE 3
typedef struct stu
{
string name;
int height;
}STU,*PSTU;
// 構造資料
STUstudents[MAX_NUMBER_PEOPLE] = {
{"LiMing", 172},
{"XiaoHua", 160},
{"XiaoWang", 180}
};
int main()
{
map<int, STU> mapstudent;
map<int, STU>::iterator student_iter;
mapstudent.insert(pair<int, STU>(172,students[0]));
mapstudent.insert(pair<int, STU>(160,students[1]));
mapstudent.insert(pair<int, STU>(180,students[2]));
mapstudent.erase(160);
for(student_iter = mapstudent.begin();student_iter!=mapstudent.end();student_iter++)
{
cout<<student_iter->first<<""<<student_iter->second.name<<endl;
}
}
結果:
172 LiMing
180 XiaoWang
2.3.4 map的sort問題
Map中的元素是自動按key升序排序,所以不能對map用sort函式:
sort函式見下表:
例項:
#include <map>
#include <iostream>
usingnamespace std;
int main( )
{
map <int, int> m1;
map <int,int>::iterator m1_Iter;
m1.insert ( pair <int,int> ( 1, 20 ) );
m1.insert ( pair<int, int> ( 4, 40 ));
m1.insert ( pair<int, int> ( 3, 60 ));
m1.insert ( pair<int, int> ( 2, 50 ));
m1.insert ( pair<int, int> ( 6, 40 ));
m1.insert ( pair<int, int> ( 7, 30 ));
cout << "The original mapm1 is:"<<endl;
for ( m1_Iter =m1.begin( ); m1_Iter != m1.end( ); m1_Iter++ )
cout<< m1_Iter->first<<""<<m1_Iter->second<<endl;
}
結果:
1 20
2 50
3 60
4 40
6 40
7 30
3.總結
1. Vector的資料模型就是陣列
優點:記憶體和C完全相容、高效隨機訪問、節省空間;
缺點:內部插入刪除元素代價巨大、動態大小查過自身容量需要申請大量記憶體做大量拷貝。
2. List的資料結構模型是連結串列
優點:任意位置插入刪除元素常量時間複雜度、兩個容器融合是常量時間複雜度;
缺點:不支援隨機訪問、比vector佔用更多的儲存空間。
3. Map的資料結構模型是二叉樹
優點:元素會按照鍵值排序、查詢是對數時間複雜度、通過鍵值查元素、map提供了下標訪問。
4. List每次增加一個元素,不存在重新申請記憶體的情況,它的成本是恆定的。而vector每當增加關鍵元素的時候,都需要重新申請新的更大的記憶體空間,會呼叫元素的自身的複製建構函式,存在構造成本。在銷燬舊記憶體的時候,會呼叫解構函式,存在析構成本。所以在儲存複雜型別和大量元素的情況下,list比vector更有優勢!
List是一個雙向連結串列,雙鏈表既可以向前又向後連結他的元素。
List將元素按順序儲存在連結串列中.與向量(vector)相比,它允許快速的插入和刪除,但是隨機訪問卻比較慢。
[微信打賞]