1. 程式人生 > >【C/C++】STL學習筆記

【C/C++】STL學習筆記

STL

什麼是STL

STL(Standard Template Library)是C++標準庫的一部分(80%),是用C++ Template機制來表達泛型的庫。

面向過程——基於物件——面向物件——泛型

STL其實就是一個模板庫,這個模板庫主要由以下幾個元件組成:
Iterator(迭代器):正向迭代器、反向迭代器、檔案流迭代器。
Container(容器):陣列、連結串列、棧、佇列、set、map等。
Algorithm(演算法):對容器進行查詢、排序、遍歷等操作。
Adaptors(介面卡):介面卡是標準庫中通用的概念,包括容器介面卡、迭代器介面卡和函式介面卡。

本質上,介面卡是使一事物的行為類似於另一類事物的行為的一種機制


容器介面卡讓一種已存在的容器型別採用另一種不同的抽象型別的工作方式實現。例如,stack介面卡可使任何一種順序容器以棧的方式工作。
http://blog.sina.com.cn/s/blog_9946f55601016qwk.html

迭代器

迭代器實際是也是一個指標。是經過封裝的指標類。

迭代器物件名稱就是迭代器的地址。
iterator._Ptr是迭代器物件指向資料的地址。當使用++、–、或*操作符時實際上就是對_Ptr進行操作。

迭代器主要有以下型別:
正向迭代器
反向迭代器
檔案流迭代器

環境說明:windows7(64bit)、QT5.4.1

// 迭代器
#include<stdio.h>
void iteratorTest(){ vector<int> arr = {1,2,3,4,5,6,7,8}; vector<int>::const_iterator ib = arr.begin(); vector<int>::const_iterator ie = arr.end(); //printf("%p %p \n\n", ib._Ptr, ib); for(;ib != ie; ++ib){ //printf("%p %p \n", ib, ib._Ptr); printf
("%p %p \n", ib._Ptr, ib); } }

列印結果如下:
這裡寫圖片描述
在64位系統地址是16位的,可以每次列印ib看到的都是同一個地址,但ib._Ptr在++ib後每次都有變化。

容器

array(靜態陣列)

//靜態陣列:可隨機訪問,記憶體分配在棧上,長度不可變
#include<array>
void main(){

    // 定義一個大小為5的陣列
    std::array<int,5> arr = {1,2,3,4,5};
    arr[0] = 1;
    arr[1] = 3;
    arr[2] = 2;

    cout<<"size="<<arr.size()<<endl;

    // 列印每個元素
    for_each(arr.begin(), arr.end(), [](int i){ //lambda表示式
        cout<<i<<endl;
    });
}

vector(動態陣列)

// 動態陣列:可隨機訪問,插入和刪除效率較低,記憶體分配在堆上,長度可動態變化
#include<vector>
void vectorTest()
{
    vector<int> arr = {1,2,3,4,5};

    // 新增元素
    arr.push_back(6);
    // 隨機訪問
    arr[5] = 7;
    //刪除索引為0—1的元素
    arr.erase(arr.begin(),arr.begin()+2);

    // 迭代,列印
    for_each(arr.begin(), arr.end(), [](int val){
        cout<<val<<endl;
    });
}

list(連結串列)

// 連結串列:插入和刪除時間複雜度為O(1),相對效率較高,隨機訪問效率為O(n),相對效率較低。
#include<list>
void listTest(){
    list<int> l;

    //在結尾新增元素
    l.push_back(1);
    l.push_back(2);
    //在開頭新增元素
    l.push_front(3);

    list<int>::iterator ib = l.begin();
    ++ib;//鏈式結構容器的迭代器不能使用+操作符...只能這樣了...
    ++ib;
    // 第3個位置插入一個元素
    l.insert(ib, 100);

    // 迭代列印
    for_each(l.begin(), l.end(), [](int val){
        cout<<val<<endl;
    });
}

注意:merge和unique函式依賴於sort,也就是合併list和去除重複元素這兩個操作是在list有序的前提下才可執行。

stack(棧)


#include <stack>
// 棧(stack):先進後出,後進先出。
void main()
{
    // 把一個數字轉換為二進位制
    int a = 100;
    stack<int> s;

    // 一個數字不斷除以2的餘數,由下往上的數字序列就是其二進位制數
    while(a){
        s.push(a%2);
        a/=2;
    }

    // 列印該數字的二進位制數
    while(!s.empty()){
        cout<< s.top();
        s.pop();
    }
    cout<<endl;
}

queue(佇列)


#include<queue>
//佇列(queue):先進先出。
void main(){

    queue<int> q;
    // 入隊
    q.push(1);
    q.push(1);
    q.push(0);
    q.push(1);
    q.push(0);

    // 列印佇列元素個數
    cout<<"queue size : "<<q.size()<<endl;

    while(!q.empty()){
        cout<<q.front();
        // 出隊
        q.pop();
    }
    cout<<endl;
}

deque(雙向佇列)

// 列印函式
void print(int n){
    cout<<n;
}

#include<deque>
//雙端佇列(deque):兩端都可進可出,可隨意位置插入和刪除。  可反向迭代。可整體交換值(deque.swap(deque))。
void main(){

    deque<int> q;
    q.push_back(1);
    q.push_back(2);
    q.push_back(3);
    q.push_back(4);
    q.push_back(5);
    q.push_back(6);
    q.push_front(0);

    deque<int> q2;
    q2.push_back(11);
    q2.push_back(12);
    q2.push_back(13);

    //使用swap函式實現整體交換
    q.swap(q2);

    // 正向迭代
    /*
    deque<int>::const_iterator cib =  q.begin();
    while( cib != q.end())
    {
        cout<<*cib;
        cib++;
    }
    */
    for_each(q.begin(),q.end(),print);
    cout<<endl;

    for_each(q2.begin(),q2.end(),print);
    cout<<endl;

    // 反向迭代
    /*
    deque<int>::reverse_iterator rib = q.rbegin();
    while( rib != q.rend())
    {
        cout<<*rib;
        rib++;
    }
    */
    for_each(q.rbegin(),q.rend(),print);
    cout<<endl;

    // 隨機訪問
    q[1] = 2;

    for_each(q.begin(),q.end(),print);
    cout<<endl;
}

priority_queue(優先順序佇列)

#include<vector>
#include<queue>
//優先順序佇列(priority_queue)是一個可自動排序的佇列。
void main(){

    priority_queue<int> q;
    q.push(1);
    q.push(2);
    q.push(3);
    q.push(4);
    q.push(5);

    // 自動排序,預設按從大到小的順序
    while(!q.empty()){
        cout<<q.top();
        q.pop();
    }
    cout<<endl;

    //////////////////////// 自定義類的大小比較 ///////////////////////////
    // 類
    class student{
    public:
        int age;
        std::string name;

    };
    // 比較器
    class studentComparator{
    public:
        // age值較大的排在前面
        bool operator()(const student& s1,const student& s2)
        {
            return s2.age > s1.age;
        }
    };

    //priority_queue<型別,容器,比較器>
    priority_queue<student,vector<student>,studentComparator> students;
    student s1;
    s1.age = 10;
    s1.name = "xiaoming1";

    student s2;
    s2.age = 15;
    s2.name = "xiaoming2";

    student s3;
    s3.age = 9;
    s3.name = "xiaoming3";

    students.push(s1);
    students.push(s2);
    students.push(s3);

    while(!students.empty()){
        student s = students.top();
        cout << "name = " << s.name.c_str() << " , age = " << s.age << endl;
        students.pop();
    }
}

紅黑樹

紅黑樹實際上是可自平衡的二叉樹。如果二叉樹不平衡的話,結構就可能類似連結串列,而二叉樹最大的特點就是可以實現二分查詢,不平衡的二叉樹查詢效率就不高了,為了解決這個問題就出現了可自平衡的二叉樹,即紅黑樹。紅黑樹可以在O(log n)時間內做查詢,插入和刪除,這裡的n是樹中元素的數目。>>維基百科
紅黑樹的綜合效率是陣列與連結串列的折中。
這裡寫圖片描述

set(集合)

set

#include<set>
// set:紅黑樹,不可有重複元素,自動排序。
void setTest(){

    set<int> s;
    // 插入是亂序的
    s.insert(5);
    s.insert(3);
    s.insert(1);
    s.insert(4);
    s.insert(0);
    s.insert(2);

    // 紅黑樹會自動對元素進行排序
    // 列印結果為: 0 1 2 3 4 5
    for_each(s.begin(), s.end(), [](int val){
        cout<<val<<" ";
    });
    cout<<endl;


    /////////////////////////// 自定義排序方式 /////////////////////////////
    // 類
    struct student{
        std::string name;
        int age;
    };
    // 比較器
    struct studentComparator{
        bool operator()(const student &s1, const student &s2){
            return s1.name.compare(s2.name) < 0; //比較兩個字串,字母較小的排前
        }
    };

    // 使用自定義比較方式,預設是使用less函式,小的排在前面
    set<student,studentComparator> students;

    student s1 = {"ab",11};
    student s2 = {"b",12};
    student s3 = {"a",13};

    // 插入元素
    cout<<endl;
    auto p1 = students.insert(s1);
    cout<<"insert: "<<(*p1.first).name.c_str()<<" "<<p1.second<<endl;

    // insert函式返回一個pair,pair包含插入元素的迭代器和插入結果
    pair<set<student,studentComparator>::iterator, bool> p2 = students.insert(s2);
    cout<<"insert: "<<(*p2.first).name.c_str()<<" "<<p2.second<<endl;

    auto p3 = students.insert(s3);
    cout<<"insert: "<<(*p3.first).name.c_str()<<" "<<p3.second<<endl;
    cout<<endl;

    // 列印所有元素
    for_each(students.begin(), students.end(), [](const student &stu){
        cout<<stu.name.c_str()<<" "<<stu.age<<endl;
    });
}

multiset

// multiset:紅黑樹,每個節點都是一個連結串列,可有重複元素,自動排序
void multiSetTest(){

  multiset<int> ms;
  ms.insert(2);
  ms.insert(1); //插入了兩個1
  ms.insert(1);
  ms.insert(3);

  // 可插入重複元素
  // 列印結果為:1 1 2 3
  for_each(ms.begin(), ms.end(), [](int val){
    cout<<val<<" ";
  });
  cout<<endl;

  // find與equal_range的區別
  // 1.find:查詢目標元素出現的第一個位置,返回該位置的迭代器
  auto it1 = ms.find(1);
  cout<<"find : "<<*it1<<endl;

  // 2.equal_range:查詢所有目標元素(multiset可包含重複元素),返回連結串列(multiset每個節點就是一個連結串列)的迭代器
  auto it2 = ms.equal_range(1);
  //first是連結串列的開始結點,second是連結串列的結束結點
  for_each(it2.first, it2.second, [](int i){
      cout<<"find : "<<i<<endl;
  });
  cout<<endl;


  struct student{
      std::string name;
      int age;
  };

  struct studentComparator{
      bool operator()(const student& s1, const student& s2){
        return s1.name.compare(s2.name) > 0; //字母大的靠前
      }
  };

  student s1 = {"c",11};
  student s2 = {"bd",12};
  student s3 = {"abc",13};

  multiset<student,studentComparator> students;
  students.insert(s2);
  students.insert(s1);
  students.insert(s3); //這裡插入了兩次s3,都能成功插入
  students.insert(s3);

  // 列印所有元素
  for_each(students.begin(), students.end(), [](const student &stu){
        cout<<stu.name.c_str()<<" "<<stu.age<<endl;
  });
}

map(鍵值對集合)

map

#include<map>
// map:實際上也是紅黑樹,每個節點,都是一個鍵值對,key不允許重複。
void mapTest(){

    map<const char*, int> m;

    // 插入元素
    pair<const char*, int> p1 ("aaaa",2);
    m.insert(p1);
    m["a"] = 1;
    m["bb"] = 3;
    //insert,重複插入的元素將會被忽略
    m.insert(p1);

    // 遍歷所有元素
    for_each(m.begin(), m.end(), [](pair<const char*, int> p){
        cout<<p.first<<" - "<<p.second<<endl;
    });

    ////////////////////////// 自定義類自定義排序方式 ///////////////////////////////
    struct student{
        string name;
        int age;
    };
    struct studentComparator{
        bool operator()(int key1, int key2){
            return key1 > key2; //讓大數排在前面
        }
    };

    student s1 = {"c",11};
    student s2 = {"bd",12};
    student s3 = {"abc",13};

    map<int,student,studentComparator> stum;
    stum[1] = s1;
    stum[2] = s2;
    stum[3] = s3;

    // 遍歷所有元素
    for_each(stum.begin(), stum.end(), [](pair<int, const student&> p){
        cout<<p.first<<" - [ "<<p.second.name.c_str()<<" , "<<p.second.age<<" ]"<<endl;
    });
}

multimap

// multimap:紅黑樹,每個節點都是一個鍵值對連結串列,所以key允許重複,自動對元素進行排序。
void multiMapTest(){

    multimap<int,int> m;
    //m[1] = 1; //multimap不可以使用這種方式
    pair<int,int> p (3,3);
    // 這裡重複插入了p,但都能插入成功
    m.insert(p);
    m.insert(p);
    m.insert(p);
    m.insert(pair<int,int>(1,1));
    m.insert(pair<int,int>(2,2));

    //遍歷每個元素
    for_each(m.begin(), m.end(), [](pair<int,int> p){
        cout<<p.first<<" - "<<p.second<<endl;
    });
}

hash(雜湊)

散列表(Hash table,也叫雜湊表),是根據關鍵字(Key value)而直接訪問在記憶體儲存位置的資料結構。也就是說,它通過計算一個關於鍵值的函式,將所需查詢的資料對映到表中一個位置來訪問記錄,這加快了查詢速度。這個對映函式稱做雜湊函式,存放記錄的陣列稱做散列表。>>維基百科

hash_set

// hash:精確查詢,快速,一次找到,比二分查詢快,無序,不可重複。
#include<hash_set>
void hashSetTest(){

    hash_set<const char*> hs;
    hs.insert("aa");
    hs.insert("aaa");
    hs.insert("a");
    const char* str = "aaaa";
    hs.insert(str);
    hs.insert(str); // 重複插入的元素將被忽略
    hs.insert(str);

    // 這裡的兩個字元將被重複插入,因為他們的hashCode不一樣
    hs.insert("aaaa");
    hs.insert("aaaa");

    // 無序
    for_each(hs.begin(), hs.end(), [](const char* str){
        cout<<str<<endl;
    });

    //hash_set<const char*,hash<const char*>,comparator> hs;
}

hash_map

#include<hash_map>
void hashMapTest(){

    hash_map<const char*, const char*> m;
    m.insert(pair<const char*, const char*>("1","a"));
    m.insert(pair<const char*, const char*>("2","b"));
    m.insert(pair<const char*, const char*>("3","c"));
    m.insert(pair<const char*, const char*>("4","d"));
    m.insert(pair<const char*, const char*>("4","d"));
    m["5"] =  "e";

    for_each(m.begin(), m.end(), [](pair<const char*, const char*> p){
        cout<<p.first<<" - " <<p.second<<endl;
    });
}

bitset


// 位集合
#include<bitset>
// 使用bitset可以很方便得將一個數字轉換為其二進位制數
void bitsetTest(){
    //    位數      根據一個數字構造一個二進位集合
    bitset<8> bits (1);
    //bitset<8> bits (256);

    // 是否所以位都沒有被設定為1
    bool isNone = bits.none();
    cout<<"isNone : "<< isNone <<endl;

    // 是否有位已經被設定為1
    bool isAny = bits.any();
    cout<<"isAny : "<< isAny <<endl;

    // 測試指定索引位是否為1
    bool testResult = bits.test(1);
    cout<<"testResult : "<< testResult <<endl;

    // 列印所有位:注意儲存的順序是從高位到低位的,並沒有做高低位轉換,所以順序是反的
    for(int i=0; i<bits.size(); ++i){
        cout<<bits[i];
    }
    cout<<endl;

    // 轉換為字串,順序是正的
    cout<<bits.to_string()<<endl;

    // 所有位重置為0
    bits.reset();

    cout<<bits.to_ulong()<<endl;
}

演算法

包含演算法相關的標頭檔案:#include

for_each

#include<vector>
#include<iostream>
#include<algorithm> //演算法相關的標頭檔案

using namespace std;

// 列印函式
void print2(int val){
    cout<<val<<endl;
}

// 模板
template<class T>
class Printor{
public:
    // 過載()操作符
    void operator()(T val){
        cout<<val<<endl;
    }
};

void forEachTest(){

    // 定義一個動態陣列
    std::vector<int> arr;
    // 新增元素
    arr.push_back(1);
    arr.push_back(2);
    arr.push_back(3);

    //迭代列印容器
    //1.使用lambda表示式
    for_each(arr.begin(), arr.end(), [](int val){
        cout<<val<<endl;
    });


    //2.使用函式指標
    for_each(arr.begin(), arr.end(), print2);

    //3.使用模板
    Printor<int> printInt;
    for_each(arr.begin(), arr.end(), printInt);//當類的()操作符被過載時,可使用類似呼叫函式的方式去呼叫該類

    // 例項化模板
    Printor<const char*> printStr;
    printStr("abc"); //呼叫()操作符
}

remove_if


#include<list>
#include<functional>
void listTest(){
    list<int> l;

    //在結尾新增元素
    l.push_back(1);
    l.push_back(2);
    //在開頭新增元素
    l.push_front(3);

    list<int>::iterator ib = l.begin();
    ++ib;//居然沒有過載+操作符...只能這樣了...
    ++ib;
    // 第3個位置插入一個元素
    l.insert(ib, 100);

    // 3 1 100 2

    //bind1st:用於繫結一個函式
    //greater:是一個struct模板,過載了()操作符,實現了比較功能,設定值大於目標值則返回true
    //greater_equal:設定值大於等於目標值則返回true
    //equal_to:設定值等於目標值則返回true
    //less:設定值小於目標值則返回true
    //less_equal:設定值小於等於目標值則返回true
    //需要#include<functional>

    //移除小於3的值
    //remove_if(l.begin(), l.end(), bind1st(greater<int>(),3)); //列印結果為3 100 100 2,不對,此處有bug...

    // 保留小於等於3的值
    l.remove_if(bind1st(less_equal<int>(),3));

    // 迭代列印
    for_each(l.begin(), l.end(), [](int val){
        cout<<val<<endl;
    });
}

sort

    vector<int> arr;
    arr.push_back(3);
    arr.push_back(1);
    arr.push_back(4);
    arr.push_back(2);
    arr.push_back(5);

    // 排序(預設從小到大排序)
    //sort(arr.begin(),arr.end());
    // 指定比較使用的函式,greater表示從大到小排序,less表示從小到大排序,如果是自定義類也可以使用自定義的比較方式
    sort(arr.begin(),arr.end(),greater<int>());

    for_each(arr.begin(),arr.end(),[](int val){
        cout<<val<<endl;
    });

find

    vector<int> arr;
    arr.push_back(3);
    arr.push_back(1);
    arr.push_back(4);
    arr.push_back(2);
    arr.push_back(5);

    vector<int>::iterator it = find(arr.begin(),arr.end(),5);
    if(it != arr.end()){
        cout<<"found! "<<*it<<endl;
    }else{
        cout<<"not found!"<<endl;
    }

find_if

#include<vector>
#include<functional>
void findIfTest(){

    vector<int> arr;
    arr.push_back(1);
    arr.push_back(2);
    arr.push_back(3);

    //查詢等於3的值,如果查詢成功返回該值所在位置的迭代器,否則返回結束位置迭代器
    vector<int>::iterator it = find_if(arr.begin(),arr.end(),bind1st(equal_to<int>(),3));
    if(it != arr.end()){
        cout<<"found! "<<*it<<endl;
    }else{
        cout<<"not found!"<<endl;
    }

    // 查詢第一個不大於3的元素,返回該元素的迭代器
    vector<int>::iterator it2 = find_if_not(arr.begin(),arr.end(),[](int i)->bool{ //lambda表示式,返回一個bool值
        return i>3;
    });
    if(it2 != arr.end()){
        cout<<"found! val="<<*it2<<endl;
    }else{
        cout<<"not found! it2"<<endl;
    }

}

隨機排序

// 列印函式
template<class Type>
struct show{
    void operator()(Type t){
        cout<<t<<"\t";
    }
};

// 對容器中元素進行隨機排序
void AlgorithmTest::randomTest(){
    vector<int> arr;
    arr.push_back(1);
    arr.push_back(2);
    arr.push_back(3);
    arr.push_back(4);
    arr.push_back(5);

    for_each(arr.begin(),arr.end(),show<const int>());
    cout<<endl;

    // 對指定位置元素進行隨機排序
    random_shuffle(arr.begin(),arr.end());

    for_each(arr.begin(),arr.end(),show<const int>());
}

adjacent_find

//adjacent_find,找出一組重複的元素,multiset、multimap每一個節點都是一個連結串列,可有重複元素。
#include<set>
void AlgorithmTest::adjacent_findTest(){
    multiset<int> s;
    s.insert(1);
    s.insert(2);
    s.insert(2);
    s.insert(2);
    s.insert(2);
    s.insert(3);
    s.insert(3);

    // 返回該節點的迭代器
    auto it = adjacent_find(s.begin(),s.end());
    for_each(it,s.end(),show<int>());
    //cout<<*it<<endl;
}

rotate(begin, end, target); //把begin到end的元素移動到target的後面,後面的元素往前移
fill(begin, end, val); //填充指定元素
count(begin, end, val); //統計指定元素個數