1. 程式人生 > >【演算法筆記】第六章:C++標準模板庫(STL)介紹

【演算法筆記】第六章:C++標準模板庫(STL)介紹

【演算法筆記】第六章:C++標準模板庫(STL)介紹

標籤(空格分隔):【演算法筆記】


第六章:C++標準模板庫(STL)介紹

  • 標準模板庫(Standard Template Library,STL)
    :封裝了很多相當實用的容器。

6.1 vector的常見用法詳解

  • vector:翻譯為向量,但是使用“變長陣列”的稱呼可能更準確,也就是“長度根據需要而自動改變的陣列”。在很多題目中,我們可以使用vector來避免陣列超出記憶體的情況。另外,vector還可以使用鄰接表的方式來儲存圖,這對於無法使用鄰接矩陣的題目,和害怕使用指標實現的鄰接表的讀者是非常友好的。

  • 如果想要使用vector,需要新增標頭檔案#inlucde<vector>

  • vector的建立:格式vecotr<typename> name;這個定義其實相當於一個一維陣列name[size],只不過長度可以依據需求而發生變化,比較節省空間,即變長陣列。
    typename可以實任何基本型別,例如int、double、char、結構體、也可以是STL容器,例如vector、set、queue等。要注意的一點:如果typename也可是一個STL容器,定義的時候要在>>符號之間加上空格

    ,因為C++11標準下會把它視為移位操作。
    例如vector< vector< int> > name;
    定義vecotr陣列的方法:vector<typedef> Arryname[arraySize];
    二維vecotr陣列中,Arryname[]中的每一個元素都是vecotr。這裡Arryname[0] ~ Arryname[arraySize - 1]都是vector容器。
    與 vector< vector< int> > name; 不同的是,上述寫法的一維長度已經是Arryname, 是一個定值,而這種寫法長度可變。

  • vector容器內元素的訪問
    兩種方式:通過下標訪問或者通過迭代器訪問。特別說明,二者是等價的.

    1. 通過下標訪問:
      和普通陣列一樣,對於vector< int> name;而言,直接可訪問的範圍是 0 ~ name.size() - 1,訪問這個範圍外的元素執行可能會出錯。
    2. 通過迭代器訪問:
      迭代器(iterator)可以理解為一種類似於指標的東西,定義是:vector<typename>::iterator it;,在這裡 it 就是一個vector::iterator型的變數其中typenma就是vector的型別。可以通過 *it 來訪問 vector中的元素。另外,迭代器還是實現了自加,自減操作:it++,++it,it–,–it.
      例如:
      vector<int> vi;
      for( int i = 0; i < 5; i++)
          vi.push_back( i);
      vector<int>::iterator it;
      it = vi.begin();
      //begin()函式的作用為取vi的首元素地址
      /*另外注意,end()函式並不是vi尾元素地址,而是取尾元素地址的下一個地址,end()作為迭代起末尾標誌,不儲存任何元素*/
      //因為美國人習慣左閉右開
      cout<<*(it + 2);
      //其輸出結果為 2 .     
      for(vector<int>::iterator it2 = vi.begin() + 3; it2 != vi.end(); it2++)
          cout<<*it2<<' ';
      //輸出結果為 3 4.
      //vector迭代起不支援it2 < vi.end()的寫法,迴圈只能使用上述寫法。
      //另外,在常用STL中,只有vector 和string支援 "vi.begin() + 3"這樣的寫法
  • vector常用函式解析
    1. push_back()
      push_back(x)就是在vector後面新增一個元素x,時間複雜度為 O ( 1 ) .
    2. pop_back()
      pop_back()用來刪除vector的尾部元素,時間複雜度為 O ( 1 ) .
    3. size()
      size()用來獲得vector中元素的個數,時間複雜度為 O ( 1 ) .size()返回的是unsigned型別,不過一般而言用%d不會有很大問題,這一點對於所有的STL容器都是一樣的。
    4. clear()
      clear()用來清空vector中所有的元素,時間複雜度為 O ( N ) ,其中N是vector中元素的個數。
    5. insert()
      insert(it,x)用來向vector的任意迭代器it處插入一個元素x,時間複雜度為 O ( N ) .
    6. erase()
      erase()函式有兩種用法:刪除單個元素、刪除一個區間內所有的元素。時間複雜度為 O ( N ) .
      • 刪除單個元素:earse(it),刪除迭代器為it處的元素.
      • 刪除一個區間的所有元素:earse(first,last),刪除[first,last)區間內所有元素。如果要刪除vector內所有元素,正確的寫法應該是earse(a.begin(),a.end())
        例如:
 vector< int> a;
    for(int i = 0;i<=5;i++)
        a.push_back(i);
    a.insert( a.begin() + 2, 10);
    for(int i = 0;i<=5 + 1;i++)
        cout<<a[i]<<' ';
  //輸出結果為0 1 10 2 3 4 5
    cout<<endl;
    a.erase( a.begin() + 2);
    for(int i = 0;i<=5 ;i++)
        cout<<a[i]<<' ';
  //輸出結果為0 1 10 3 4 5 
    cout<<endl;
    a.erase( a.begin() + 2, a.begin() + 4);
    for(int i = 0;i<=3 ;i++)
        cout<<a[i]<<' ';
  //輸出結果為0 1 4 5 
  • vector的常見用途
    1. 存數資料
      vecotr本身可以作為陣列使用,而且在一些元素個數不確定的場合可以很好的節省空間。
      同時,有些場合需要根據一些條件把部分資料輸出在同一行,資料中間由空格隔開。由於輸出的資料的個數是不確定的,為了更方便地處處理最後一個滿足條件的資料周免不輸出額外的空格,可以先用vector記錄所有需要輸出的資料,然後一次性輸出。
    2. 用鄰接表儲存圖
      使用vector實現鄰接表可以讓一些對指標不熟悉的讀者有一個比較方便的寫法。

6.2 set的常見用法詳解

  • set:翻譯為集合,是一個內部自動有序且不含重複元素的容器。
    對於題目中,需要去掉重複元素的情況,有可能因為某些元素過大或者型別不是int而無法直接開散列表,在這種情況下就可以使用set來保留元素本身而不考慮它的個數。(當然,上述情況也可以再開一個數組來進行下表和元素對應解決)

  • 如果想要使用set,需要新增#include<set>.

  • set的定義:
    格式:set<typename> name;,這個寫法和vector基本是一樣的(或者說大部分STL都是這樣定義的)。這裡的typename依然可以是任何基本型別,例如int,double,char,結構體等,或者是STL標準容器,例如vector,set,queue等。:如果typename也是一個STL容器,定義的時候要在>>符號之間加上空格,因為C++11標準下會把它視為移位操作。
    例如:set<int> name;.
    set陣列的定義格式set<typename> Arrayname[arraySize];,這樣來看Arrayname[0] ~ Arrayname[arraySize - 1]之中每一個都是 set 容器。 例如:set<int> a[100];

  • set容器內元素的訪問:set之可以通過迭代器(iter)來訪問,迭代器的定義格式set<typename>::iterator it;如此一來,便得到了迭代器it,並且可以通過 *it 來訪問set裡面的元素。
    再次提醒,除了vector和string之外的STL容器,都不支援*(it+i)的訪問方式。 因此只能按照如下方式列舉:

    set<int> st;
    for(set<int>::iterator it = st.begin();it != st.end(); it++){
        printf("%d",*it);
    } 
  • set常用函式例項解析

    1. inset()
      insert(x)可以將x插入到set容器中,並自動遞增排序和去重,時間複雜度為 O ( l o g N ) ,其中 N 為set內元素的個數。
    2. find()
      find(value)返回set中對應值為value的迭代器,時間複雜度為 O ( l o g N ) ,其中 N 為set內元素的個數。
    3. earse()
      earse()有兩種用法:刪除單個元素、刪除一個區間內所有元素。
      • 刪除單個元素:兩種方法,1. st.erase(it) ,其中 it是一個被刪除元素的迭代器。時間複雜度為 O ( 1 ) ,可以搭配find()函式使用。st.earse(st.find(x));.2. st.erase(value),其中value為被刪除元素的值,時間複雜度為 O ( l o g N ) ,N 為set內元素的個數。
      • 刪除一個區間內所有元素:st.erase(first,last),其中first為所需要刪除區間的起始迭代器,last指向末尾需要被刪除元素的迭代器的下一個地址,即刪除[first,last),時間複雜度為 O ( f i r s t l a s t ) .
    4. size()
      size()函式用來獲得set內元素的個數,時間複雜度為 O ( 1 ) .
    5. clear()
      clear()函式用來清空set內所有元素,時間複雜度為 O ( N ) ,其中 N 為set內元素的個數。
  • set的常見用途
    set最主要的作用是自動去重並按升序排序,因此碰到需要去重但是不方便直接開陣列的問題,可以嘗試使用set解決。

  • 延申:set 內元素是唯一的,如果需要處理不唯一的情況,則需要使用multiset,另外,C++11 中還增加了 unordered_set,以雜湊代替set內部的紅黑樹(Red Black Tree,一種自平衡二叉查詢樹)實現,可以用來處理單單去重但是不排序的需求,速度比set快很多。

6.3 string的常見用法詳解

  • 在C語言中,一般使用字元陣列 char str[]來存放字串,但是使用字元陣列有時候操作十分麻煩,而且會因為經驗不足而發生一些錯誤。C++在STL中引入了string型別,對字串常用的需求功能進行了封裝,方便操作且不易出錯。
  • 如果想要使用string函式,需要新增string標頭檔案,即#include<string>

  • string的定義
    格式string後面跟上變數名即可,例如:srtring str;如果需要初始化,可以直接賦值,例如string str="abc123";

  • string 中的內容訪問

    1. 通過下標訪問:對於string而言,可以直接向字串陣列那樣,用下標訪問。例如
    2. 如果要寫入和讀出整個字串,**只能使用**cin和cout.如果想要使用printf和scanf實現寫入和讀出,需要首先使用 c_str() 函式將string轉化為字串陣列。
      例如:

      string a;
      a.c_str();
      scanf("%s",a);
      printf("%s",a);
    3. 通過迭代器訪問:在大多數情況下,通過下標訪問即可以滿足大部分需求。但是例如insert() 和 erase() 函式要求使用的迭代器為引數。
      string不需要其他的STL容器那樣需要引數,因此可以直接定義,格式:string::iterator it;.這樣,得到迭代器it之後,便可以通過*it來訪問string函式中的每一位。需要注意的是,string 和 vector 一樣,支援對迭代器進行加減某個數字,例如str.begin() + 3,這種寫法是可行的。

  • string 常用函式例項解析
    string 函式有很多,只介紹集中常用函式。

    1. operator+=
      operator+- 函式用於string的加法,可以將兩個字串直接拼接起來。例如:str1 = str1 + str2;
    2. compare operator
      兩個string型別可以直接使用 ==,!=,<=,>=,<,> 比較大小,比較規則是字典序.
    3. length() 和 size()
      length() 和size() 函式返回的是 string 中存放的字元數,時間複雜度為 O ( 1 ) .length() 和 size() 功能基本相同。
    4. insert()
      string 的insert()有很多種寫法,這裡僅僅給出幾個。時間複雜度為 O ( N )

      • insert(pos,string):在pos號位置之前插入字串string.例如:

        string a ="qwer";
        string b ="asdf";
        b.insert(3,a);
        cout<<b;//輸出結果是asdqwerf
      • insert(it,it2,it3):it為原字串想要插入的位置,迭代器it2和it3為待插入字串的首尾借貸期,用來表示將[it2,it3)將被插入在it的位置上。例如:

        string a ="qwer";
        string b ="asdf";
        b.insert(b.begin() + 1, a.begin(), a.begin() + 2);
        cout<<b;//輸出結果為aqwsdf
    5. erase()
      erase()有兩種用法:刪除單個元素,刪除一個區間內的所有元素。時間複雜度為 O ( l o g N ) .

      • 刪除單個元素:str.erase(it),用於刪除單個元素,it為被刪除元素的迭代器。
      • 刪除一個區間內所有元素:1. str.erase(first,last),其中first為被刪除區間的起始迭代器,last是被刪除元素的迭代器的下個地址,即刪除[first,last). 2. str.erase(pos,length),其中pos為需要開始刪除的元素的起始位置,length為刪除的字元個數。
    6. claer()
      clear()用來清空string中的資料,時間複雜度一般而言為 O ( 1 ) .
    7. substr()
      substr(pos,len),返回從pos號開始、開始為len的字串,時間複雜度為O(len).
    8. string::npos
      string::npos 是一個常數,本身的值為-1,但是由於是unsigned_int 型別,因此實際上可以認為是unsigned_int 型別的最大值。string::npos 可以作為find函式失配時的返回值。
    9. find()
      • str.find(str2),當str2 是str字串時,返回其在str中第一次出現的位置,如果str2不是str字串時,返回str::npos.
      • str.find(str2,pos),從str的pos號位置開始匹配str2,返回值與上相同。
        時間複雜度為 O ( s t r . s i z e ( ) s t r 2. s i z e ( ) ) .
    10. replace()
      • str.replace(pos,len,str2),把str從pos號位置開始,長度為len的字串替換為str2.
      • str.repalce(it1,it2,str2),把str的迭代器[it1,it2)範圍的字串替換為str2.時間複雜度為 O ( s t r . l e n g t h ) .

6.4 map的常見用法詳解

  • map翻譯為對映,是常用的STL容器。map可以將任何基本型別(包括STL容器)對映到任何基本型別(包括STL容器)。在定義陣列時(例如 int array[100];),其實是定義了一個從int型別到int型別的對映,例如array[0] = 25;其實是將 0 對映到25. 同理,double型陣列則是將int型銀蛇到double型,例如db[0] = 3.14;.
    可是無論什麼型別,它總是將int型對映到其他型別,這存在一個弊端:當需要以其他型別作為關鍵字來作為對映時,會十分不方便。使用map便不會出現如此問題。
    另外,由於map內部使用紅黑樹實現的(和set相同),map會以鍵從小到大的順序自動排序,在建立對映的過程中會自動實現從小到大的排序功能。

  • 如果需要使用map,需要新增map的標頭檔案,即#include<map>;

  • map的定義格式:map<typename1, typename2> mp;
    map和其他型別的STL容器的定義有些不同,因為map需要確定對映前型別(鍵key)和對映後型別(值value),所以在<>內右兩個型別,第一個是key的型別,第二個是value的型別。如果是int到int型,便相當於普通的int陣列。
    注意,如果是字串到整型的對映,不能使用char陣列 而必須使用string,因為char陣列作為陣列型別,是不可以被作為鍵值的。即,如果字串型別想做對映,必須使用string型別。
    同樣,STL容器也可以作為map的鍵和值。

  • map容器內元素的訪問:兩種方式,通過下標訪問或者通過迭代器訪問。

    1. 通過下標訪問:和訪問普通的陣列一樣,例如對於定義為 map<char,int> mp;而言,可以直接使用 mp[‘c’] 方式來直接訪問它對應的整數。注意:map中的鍵是唯一的
    2. 通過迭代器訪問:map迭代器的定義和其他STL容器迭代器的定義方式相同:map<typename1, typename2>::iterator it;. 需要注意的是,map迭代器的使用方式和其他STL容器的迭代器使用方式不同,因為map的每一對對映都有兩個typename,這意味者必須能通過一個it來同時訪問鍵和值。事實上,map可以使用 it->first 來訪問鍵,通過it->second來訪問值
  • map常用函式解析

    1. find()
      find(key), 返回鍵為key的對映的迭代器,時間複雜度為 O ( l o g N ) ,N為map中對映的個數。
    2. erase()
      兩種用法: 刪除單個元素、刪除一個區間內所有元素。
      • 刪除單個元素:
        兩種方法:1. mp.erase(it),it為被刪除元素的迭代器。時間複雜度為 O ( 1 ) . 2. mp.erase(key),其中key為被刪除元素的鍵。時間複雜度為 O ( l o g N ) ,N 為map內元素的個數。
      • 刪除一個區間內所有的元素
        mp.erase(first, last),其中first為需要刪除的區間的起始迭代器,而last 則為需要刪除的區間的末尾迭代器的下一個
        地址。即刪除區間為[first,last). 時間複雜度為 O ( l a s t f i r s t )
    3. size()
      size(),用來獲得map中對映的對數,時間複雜度為