1. 程式人生 > >關於C++標準模板庫(STL)的一些基本使用

關於C++標準模板庫(STL)的一些基本使用

vector

vector可以理解成變長陣列,即長度根據需要而自動改變的陣列

  • 標頭檔案:#include <vector>
  • 定義:vector<typename>name;
  • vector內可以通過下標或者迭代器(iterator)訪問(只有vector和string才允許使用v.begin()+3這種迭代器加整數的寫法)
  • v.push_back(value) 時間複雜度:$O(1)$
  • v.pop_back() 時間複雜度:$O(1)$
  • v.size() 返回的是unsigned型別 時間複雜度:$O(1)$
  • v.clear() 時間複雜度:$O(N)$ $N$是vector中元素的個數
  • v.insert(it,value) 時間複雜度:$O(N)$
  • v.erase(it) 時間複雜度:$O(N)$
  • v.erase(first,last) 即刪除[first,last)內元素 時間複雜度:$O(N)$

 

set

set可以理解成集合,一個內部自動有序且不含重複元素的容器

  • 標頭檔案:#include <set>
  • 定義:set<typename>name;
  • set只能通過迭代器訪問
  • s.insert(value) 時間複雜度:$O(logN)$ $N$為set內元素個數
  • s.find(value) 時間複雜度:$O(logN)$ $N$為set內元素個數
  • s.erase(it) 時間複雜度:$O(1)$
  • s.erase(value) 時間複雜度:$O(logN)$
  • s.erase(first,last) 時間複雜度:$O(last-first)$
  • s.size() 時間複雜度:$O(1)$
  • s.clear() 時間複雜度:$O(N)$
  • set中元素是唯一的,如果需要處理不唯一的情況,則需要使用multiset。另外,C++11標準中還增加了unordered_set,以雜湊代替set內部的紅黑樹(一種自平衡二叉查詢樹),使其可以用來處理只去重但不排序的需求,速度比set要快得多。

 

string

string是裝載字串的容器

  • 標頭檔案:#include <string>
  • 定義:string str = "abcd";
  • string可以通過下標和迭代器進行訪問
  • 可以使用c_str()將string型別轉換為字元陣列進行輸出 printf("%s\n",str.c_str());
  • cin讀入以遇到空格符便結束,getline(cin,str)以換行符為結束標誌,因此可以讀入空格
  • 使用'+' 可以拼接兩個string。使用==,!=,<,<=,>,>=可以對兩個string直接按照字典序進行比較
  • str.length() 或者 str.size() 可以返回string的長度 時間複雜度:$O(1)$
  • str.insert(pos,string) 時間複雜度:$O(N)$
  • str.insert(it,it2,it3) it為目的字串的插入位置迭代器 it2和it3為待插字串的首尾迭代器[it2,it3) 時間複雜度:$O(N)$
  • str.erase(it) 時間複雜度:$O(N)$
  • str.erase(first,last) 時間複雜度:$O(N)$
  • str.erase(pos,length) pos為開始刪除的初始位置,length為刪除字元的個數 時間複雜度:$O(N)$
  • str.clear() 時間複雜度:$O(1)$
  • str.substr(pos,len) 返回從pos開始,長度為len的子串 時間複雜度:$O(len)$
  • string::npos 這是一個常數,本身的值為-1,但由於是unsigned_int型別,因此也可以認為是unsigned_int型別最大值即4294967295。string::npos用作find()失配時的返回值。
  • str.find(str2) 返回str2在str中第一次出現的位置 或者 返回 string::npos 時間複雜度:$O(nm)$ 其中n和m分別為str和str2的長度
  • str.find(str2,pos) 從str的pos位開始匹配 同str.find(str2) 時間複雜度:$O(nm)$
  • str.replace(pos,len,str2) 把str從pos號位開始、長度為len的子串替換為str2 時間複雜度:$O(str.length())$
  • str.replace(it1,it2,str2) 把str[it1,it2)範圍的子串替換為str2 時間複雜度:$O(str.length())$

 

map

map可以將任何基本資料型別(包括STL容器)對映到任何基本型別(包括STL容器),但若要表示字串的話只能用string,而不能用char陣列。

  • 標頭檔案:#include <map>
  • 定義:map<typename1,typename2> m; typename1稱為鍵 typename2稱為值
  • map會按鍵的大小順序自動排序,這是因為map內部是以紅黑樹實現的
  • m.find(key) 返回鍵為key的對映的迭代器,時間複雜度:$O(logN)$ N是map中對映的個數
  • m.erase(it) 時間複雜度:$O(1)$
  • m.erase(key) 時間複雜度:$O(logN)$ N是map中對映的個數
  • m.erase(first,last) 時間複雜度:$O(last-first)$
  • m.size() 時間複雜度:$O(1)$
  • m.clear() 時間複雜度:$O(N)$ N是map中元素的個數
  • mup的鍵和值是唯一的,如果一個鍵需要對應多個值,可以使用multimap。另外C++11標準中增加了unordered_map,以雜湊代替內部的紅黑樹實現,使其可以用來處理只對映而不按key排序的需求,速度比map要快得多

 

queue

queue實現了一個FIFO(先進先出)的容器

  • 標頭檔案:#include <queue>
  • 定義:queue<typename> q;
  • queue中只能通過 q.front()來訪問隊首元素,q.back()來訪問隊尾元素 時間複雜度均為:$O(1)$
  • q.push(value) 時間複雜度:$O(1)$
  • q.pop() 時間複雜度:$O(1)$
  • q.empty() 時間複雜度:$O(1)$
  • q.size() 時間複雜度:$O(1)$
  • 使用q.front() 和 q.pop() 之前必須用q.empty()判斷queue是否為空

 

priority_queue

priority_queue指優先佇列,原理是用堆實現。在優先佇列中,隊首元素一定是優先順序最高那個,然後這個優先順序可以自己定義。

  • 標頭檔案:#include <queue>
  • 定義:priority_queue<typename> pq;
  • priority_queue只能通過pq.top()來訪問隊首元素,之前要用pq.empty()進行判斷 時間複雜度:$O(1)$
  • pq.push(value) 時間複雜度:$O(logN)$ N是當前佇列裡的元素個數
  • pq.pop() 記得之前要用pq.empty()進行判斷 時間複雜度:$O(logN)$
  • pq.empty() 時間複雜度:$O(1)$
  • pq.size() 時間複雜度:$O(1)$
  • 當優先佇列裡面的元素為基本資料型別(int,double,char等等)時,預設是數字或字典序越大優先順序越高所以越排在前面,而且以下兩種定義是等價的:priority_queue<int> pq  以及 priority_queue<int,vector<int>,less<int> > pq,可以發現第二種定義方法多了兩個引數,vector<int>是用來承載底層資料結構堆(heap)的容器,vector的資料型別與優先佇列的保持一致,less<int>則是對第一個引數的比較類,less<int>表示數字越大優先順序越大,相反,greater<int>表示數字越小優先順序越大。
  • 然後講如果資料型別是一個結構體的時候怎樣設定優先順序,當然當前型別即使是基本資料型別也可以使用這種方法,只不過第三個引數的寫法不一樣了。

 

1 struct fiuit
2 {
3     string name;
4     int price;
5     friend bool operator < (fruit f1,fruit f2)
6     {
7         return f1.price < f2.price;
8     }
9 };
View Code

 

現在希望按水果的價格高的為優先順序高,那麼就需要過載小於號"<",注意過載大於號會編譯錯誤,因為從數學上來說只需要過載小於號,即$f1>f2$等價於判斷$f2<f1$,而$f1==f2$等價於判斷$!(f1<f2)\&\&!(f2<f1)$,函式內為return f1.price < f2.price ,因此過載後小於號還是小於號的作用。此時就可以直接定義fruit型別的優先佇列:priority_queue<fruit> pq 其內部就是以價格高的水果為優先順序高。相反,如果想以價格低的水果為優先順序高,只要把return中的<改為>即可。沒錯,他們效果看上去與直覺相違背,不過語法上就是這樣寫的。我們可以理解成優先佇列預設優先順序高的在隊首,那麼假若我們把<改為>,則相當於把規則翻轉了,那麼原先大值優先也自然變成了小值優先。
還有一種方法是把過載的函式放在結構體外面:

 

1 struct cmp
2 {
3     bool operator () (fruit f1,fuit f2)
4     {
5         return f1.price < f2.price;
6     }
7 };
View Code

 

可以看到我們去掉了friend(友元),然後把<改為了一對小括號,記得要用struct把所有包起來。
還有在這個時候我們就不能寫priority_queue<fruit> pq 來定義優先隊列了,要改為priority_queue<fruit,vector<fruit>,cmp > pq 可以看到這跟上面基本元素的定義方法其實是可以差不多的。哪怕資料型別是其他STL容器也可以用這種過載符號的方法來使用priority_queue。
最後,如果結構體內的資料較大(使用了字串或者很大的陣列),我們可以使用“引用”來提高效率,即加上const和&,如下:

 

 1 friend bool operator < (const fruit &f1,const fruit &f2)
 2 {
 3     return f1.price < f2.price;
 4 }
 5 
 6 // 兩種過載方法 
 7 
 8 bool operator () (const fruit &f1,const fruit &f2)
 9 {
10     return f1.price < f2.price;
11 }
View Code

 

 

stack

stack是一個實現LIFO(後進先出)的容器。

  • 標頭檔案:#include <stack>
  • 定義:stack<typename> s
  • stack中只能通過s.top()來訪問棧頂元素 時間複雜度:$O(1)$ 注意先用s.empty()檢測是否棧為空
  • s.push(value) 時間複雜度:$O(1)$
  • s.pop() 時間複雜度:$O(1)$
  • s.empty() 時間複雜度:$O(1)$
  • s.size() 時間複雜度:$O(1)$

 

pair

pair相當於一個內部有兩個可以自定型別的元素的結構體(而不是真的需要用struct去實現,pair使這看起來更優美)。

  • 標頭檔案:#include <utility> 值得注意的是map標頭檔案包含utility標頭檔案,記不住可以用map標頭檔案代替,這也說明了map的內部實現用到了pair
  • 定義:pair<typename1,typename2> p  也可以同時進行初始化:pair<string,int> p("hello",5);
    如果想臨時構建一個pair有兩種方法:pair<string,int>("hello",5) 或者使用自帶的make_pair("hello",5)
  • 關於pair中元素的訪問跟結構體差不多,p.first 和 p.second
  • 兩個pair型別資料可以直接用==、!=、<、<=、>、>=比較大小,規則是先比較first的大小,只有當first相等的時候才會去比較second的大小
  • pair常用來作為map的鍵值對資料