Coursera課程筆記----C++程序設計----Week8

C++程序設計

標準模板庫 STL-1(week 8)

STL概述(Standard Template Library)

C++重用

  • C++語言的核心優點之一就是便於軟件的重用
  • C++中有兩個方面體現重用:
    1. 面向對象的思想:繼承和多態,標準類庫
    2. 泛型程序設計(generic programming)的思想:模版機制、標準模版庫STL

泛型程序設計

  • 簡單地說就是使用模版的程序設計法
  • 講一些經常使用的數據結構(鏈表,數組,二叉樹)(類模版)和算法(排序,查找)(函數模版)寫成模版,之後則不論數據結構裏放的是什麼對象,算法針對什麼樣的對象,則都沒必要從新實現數據結構,從新編寫算法
  • 標準模版庫(Standard Template Library)就是一些經常使用數據結構和算法的模版的集合
  • 有了STL,沒必要再寫大多的標準數據結構和算法,而且可得到很是高的性能

STL中的基本概念

  • 容器:可容納各類數據類型的通用數據結構,是類模版ios

  • 迭代器:可用於依次存取容器中元素,相似於指針算法

  • 算法:用來操做容器中的元素的函數模版數組

    • sort()來對一個vector中的數據進行排序
    • find()來搜索一個list中的對象

    算法自己與他們操做的數據類型無關,所以他們能夠在從簡單數組道高度複雜容器的任何數據結構上使用數據結構

int array[100];
//該數組就是容器,而int* 類型的指針變量就能夠做爲迭代器,sort算法能夠做用於該容器上,對其進行排序
sort(array,array+70);//將前70個元素排序,array和array+70就是兩個迭代器

容器概述

  • 能夠用於存放各類類型的數據(基本類型的變量、對象等)的數據結構,都是類模版,分爲三種:less

    1. 順序容器:vector,deque,list
    2. 關聯容器:set,multiset,map,multimap
    3. 容器適配器:stack,queue,priority_queue
  • 對象被插入容器中時,被插入的是對象的一個複製品。許多算法,好比排序、查找,要求對容器中的元素進行比較,有的容器自己就是排序的,因此,放入容器的對象所屬的類,每每還應該重載==和<運算符數據結構和算法

順序容器簡介

  • 容器並不是排序的,元素的插入位置同元素的值無關函數

  • 有vector,deque,list三種性能

    • vector 頭文件<vector>spa

      向量,動態數組,元素在內存連續存放,隨機存取任何元素都能在常數時間完成。在尾端增刪元素具備較佳的性能(大部分狀況下是常數時間,由於一般會預留一些存儲空間,因此時間複雜度是常數;偶爾會由於空間不夠而變成o(n) )。設計

    • duque 頭文件<deque>

      雙向隊列,元素在內存連續存放。隨機存取任何元素都能在常數時間完成(但次於vector,由於須要判斷是否越界,是否須要重置到頭部)。在兩端增刪元素具備較佳的性能(大部分狀況下是常數時間,只有在須要從新分配存儲空間的時候纔會變成o(n))

    • list 頭文件<list>

      雙向鏈表。元素在內存不連續存放。在任何位置增刪元素都能在常數時間完成。不支持隨機存取。

關聯容器簡介

  • 元素是排序的

  • 插入任何元素,都按相應的排序規則來肯定其位置

  • 在查找時具備很是好的性能(排序的目的

  • 一般以平衡二叉樹的方式實現,插入和檢索的時間都是o(log(N))

    • set/multiset 頭文件<set>

      set 即集合。set中不容許相同元素,multiset中容許相同的元素

    • map/multimap 頭文件<map>

      map與set的不一樣在於map中存放的元素有且僅有兩個成員變量,一個名爲first,一個名爲second,map根據first值對元素進行從小到大的排序,並可快速的根據first來檢索元素

      map同multimap的區別在因而否容許相同first值的元素

容器適配器簡介

  • stack:頭文件<stack>

    棧。是項的有限序列,並知足序列中被刪除、檢索和修改的項職能是最近插入序列的項(棧頂的項)。後進先出。

  • queue 頭文件<queue>

    隊列。插入只能夠在尾部進行,刪除、檢索和修改只容許從頭部進行。先進先出

  • priority_queue 頭文件<queue>

    優先級隊列,最高優先級元素老是第一個出列

順序容器和關聯容器中都有的成員函數

  • begin 返回指向容器中第一個元素的迭代器
  • end 返回指向容器中最後一個元素後面的位置的迭代器
  • rbegin 返回指向容器中最後一個元素的迭代器
  • rend 返回指向容器中第一個元素前面的位置的迭代器
  • erase 從容器中刪除一個或幾個元素
  • clear 從容器中刪除全部元素

順序容器的經常使用成員函數

  • front 返回容器中第一個元素的引用
  • back 返回容器中最後一個元素的引用
  • push_back 在容器末尾增長新元素
  • pop_back 刪除容器末尾的元素
  • erase 刪除迭代器指向的元素(可能會讓迭代器失效),或刪除一個區間,返回被刪除元素後面的那個元素的迭代器

迭代器

  • 用於指向順序容器和關聯容器中的元素

  • 迭代器用法和指針相似

  • 有const和非const兩種

  • 經過迭代器能夠讀取它指向的元素

  • 經過非const迭代器還能修改其指向的元素

  • 定義一個容器類的迭代器的方法:

    容器類名::iterator/const_iterator 變量名;

  • 訪問一個迭代器指向的元素

    * 迭代器變量名

  • 迭代器上能夠執行++操做,從而使其指向容器中的下一個元素。但若是迭代器到達了容器中的最後一個元素的後面,此時再使用它,就會出錯,相似於使用NULL或未初始化的指針同樣。

  • 迭代器類別

    • 隨機訪問:vector、deque
    • 雙向:list、set/multiset、map/multimap
    • 不支持迭代器:stack\queue\priority_queue
    • 有的算法,例如sort, binary_search須要經過隨機訪問迭代器來訪問容器中的元素,那麼list以及關聯容器就不支持該算法!
  • 遍歷vector(deque)

    vector<int> v(100);
    int i;
    for(i = 0; i < v.size(); i++)
      cout<< v[i]; //根據下標隨機訪問
    vector<int>::const_iterator ii;
    for(ii = v.begin(); ii != v.end(); ii++) // !=,<均可
       cout << *ii;
  • 遍歷list(所用的是雙向迭代器)

    list<int> v;
    list<int>::const_iterator ii;
    for(ii = v.begin();ii != v.end(); ++ii)
    cout << * ii;
    
    //錯誤的作法,雙向迭代器不支持<,list沒有[]成員函數
    for( ii = v.begin();ii<v.end();ii++)
      cout << * ii;
    for(int i = 0; i < v.size();i++)
      cout << v[i];

算法簡介

  • 算法就是一個個函數模版,大多數在<algorithm>中定義

  • STL中提供能在各個容器中通用的算法,好比查找,排序等

  • 算法經過迭代器來操縱容器中的元素。許多算法能夠對容器中的一個局部區間進行操做,所以須要兩個參數,一個是起始元素的迭代器,一個是終止元素的後面的一個元素的迭代器。好比,排序和查找。

  • 有的算法返回一個迭代器,好比find()算法,在容器中查找一個元素,並返回一個指向該元素的迭代器

  • 算法能夠處理容器,也能夠處理普通數組

  • 示例:find() 一般用於實現順序查找

    template<class Init, class T>
      Init find(Init first, Init last, const T& val);
    • first 和 last 這兩個參數都是容器的迭代器,它們給出了容器中的查找區間起點和終點[first,last)
    • 含義爲find在[first,last)查找等於val的元素
    • 用==運算符判斷相等
    • 函數返回值是一個迭代器。若是找到,則該迭代器指向被找到的元素。若是找不到,則該迭代器等於last
    • 時間複雜度o(n)

STL容器中「大」「小」的概念

  • 關聯容器內部的元素是從小到大排序的

  • 有些算法要求其操做的區間是從小到大排序的,稱爲「有序區間算法」(binary_search)

  • 有些算法會對區間進行從小到大排序,稱爲「排序算法」

  • 還有一些其餘算法會用到「大」,「小」的概念

使用STL時,在缺省的狀況下,如下三個說法等價

  1. x比y小
  2. 表達式「x<y」爲真
  3. y比x大

STL中「相等」的概念

  • 有時「x和y相等」等價於「x==y爲真」
  • 有時「x和y相等」等價於「x小於y和y小於x同時爲假」(binary search)

順序容器Vector

簡介

  • 可變長的動態數組(相較於靜態數組而言)

  • 必須包含頭文件 #include <vector>

  • 支持隨機訪問迭代器

    • 根據下標隨機訪問某個元素,所需時間爲常數
    • 尾部添加速度很
    • 中間插入
  • 全部STL算法 都能對vector操做

經常使用成員函數

  • 構造函數初始化

    成員函數 做用
    vector(); 無參構造函數,將容器初始化成空的
    vector(int n); 將容器初始化成有n個元素
    vector(int n, const T& val); 假定元素類型是T,將容器初始化成有n個元素,每一個元素的值都是val
    vector(iterator first, iterator last); 將容器初始化爲與別的容器上區間爲[first,last)一致的內容
  • 其餘經常使用函數

    成員函數 做用
    void pop_back(); 刪除容器末尾的元素
    void push_back(const T & val); 將val添加到容器末尾
    int size(); 返回容器中元素的個數
    T & front(); 返回容器中第一個元素的引用
    T & back(); 返回容器中最後一個元素的引用

使用範例

//例1
#include <iostream>
#include <vector>
using namespace std;
int main()
{
  int i;
  int a[5] = {1,2,3,4,5};
  vector<int> v(5);
  cout<<v.end()-v.begin()<<endl;//輸出5
  for(i = 0; i < v.size();i++) v[i] = i;//能夠用方括號直接訪問
  v.at(4) = 100;//將第5個元素賦值爲100
  for(i = 0; i < v.size();i++)
    cout<< v[i] << ","; // 0,1,2,3,100
  cout << endl;
  vector<int> v2(a,a+5);//構造函數
  v2.insert(v2.begin()+2,13);//在begin()+2位置插入13
  for(i = 0; i < v2.size();i++)//1,2,13,3,4,5
    cout<<v2.at(i)<<",";
}

二維動態數組

vector< vector<int>> v(3);
//v有3個元素
//沒個元素都是vector<int>容器
#include <iostream>
#include <vector>
using namespace std;
int main()
{
  vector<vector<int>> v(3);
  for(int i=0; i < v.size();++i)
    for(int j=0; j < 4; ++j)
      v[i].push_back(j);
  for(int i = 0; i < v.size();++i)
  {
    for(int j = 0; j < v[i].size(); ++j)
      cout << v[i][j] << " ";
    cout << endl;
  }
  return 0;
}

List容器

簡介

  • 本質是雙向鏈表 #include<list>
  • 所以在任何位置插入or刪除都是常數時間
  • 不支持根據下標隨機存取元素
  • 具備全部順序容器都有的成員函數

額外支持的成員函數

成員函數 做用
push_front 在鏈表最前面插入
pop_front 刪除鏈表最前面的元素
sort 排序(list不支持STL的算法sort
remove 刪除和指定值相等的全部元素
unique 刪除全部和前一個元素相同的元素
merge 合併兩個鏈表並清空被合併的鏈表
reverse 顛倒鏈表
splice 在指定位置前面插入另外一鏈表中的一個或多個元素,並在另外一鏈表中刪除被插入的元素

sort函數

  • list容器的迭代器不支持徹底隨機訪問,所以不能用標準庫中sort函數對它進行排序
  • list本身的sort成員函數
list<T> classname;
classname.sort(compare); //compare函數能夠本身定義
classname.sort(); //無參數版本,按<排序
  • list容器只能使用雙向迭代器,所以不支持>/</[]/隨機移動(+2等操做不行,+1/++能夠)
#include<list>
#include<iostream>
#include<algorithm>
using namespace std;
class A { //定義類A,並以友元重載<,==,<<
  private:
  int n;
  public:
  A( int n_) {n = n_;}
  friend bool operator< (const A & a1, const A & a2);
  friend bool operator== (const A & a1, const A & a2);
  friend ostream & operator << (ostream & o, const A & a);
};

bool operator < (const A & a1, const A & a2)
{
  return a1.n < a2.n
}

bool operator== (const A & a1, const A & a2)
{
  return a1.n == a2.n
}

ostream & operator << (ostream & o, const A & a)
{
  o << a.n;
  return o;
}

//定義函數模版PrintList,打印列表中的對象
template <class T>
  void PrintList(const list<T> & lst)
{
  int tmp = lst.size();
  if( tmp > 0){
    typename list<T>::const_iterator i;
    i = lst.begin();
    for(i = lst.begin();i!=lst.end();i++)
      cout << *i << ",";
  }
}
//與其餘順序容器不一樣,list容器只能使用雙向迭代器
//所以不支持大於/小於比較運算符,[]運算符和隨機移動
//typename用來講明 list<T>::const_iterator 是個類型
//在VS中不寫也能夠

deque容器

簡介

  • 雙向隊列
  • 必須包含頭文件 #include <deque>
  • 全部適用於vector的操做,都適用於deque
  • deque還有push_front(將元素插入到容器頭部)和pop_front(刪除頭部的元素)操做

函數對象

簡介

  • 若一個類重載了運算符"()",則該類的對象就成爲函數對象
class CMyAverage{//函數對象類
  public:
  double operator()(int a1, int a2, int a3)
  {
    return (double)(a1+a2+a3)/3;
  }
};

CMyAverage average;//函數對象
cout << average(3,2,3); //average.operator()(3,2,3)
//輸出2.66667

函數對象的應用

//Dev C++中的Accumulate源代碼1
template <typename _InputIterator, typename _Tp>
_Tp accumulate(_InputIterator __first, _InputIterator __last, _Tp __init)
{
  for(;__first != __last; ++__first)
    __init = __init + *__first;
  return __init;
}
//typename 等價於 class

//Dev C++中的Accumulate源代碼2
template <typename _InputIterator, typename _Tp,typename _BinaryOperation>
_Tp accumulate(_InputIterator __first, _InputIterator __last, _Tp __init, _BinaryOperation __binary_op)
{
  for(;__first != __last; ++__first)
    __init = __binary_op(__init, *__first);
  return __init;
}
//調用accumulate時,和__binary_op對應的實參能夠是一個函數或函數對象

函數對象的應用示例

#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>
#include <functional>
using namespace std;
int sumSquares(int total, int value)
{
  return total + value * value;
}

template <class T>
  void PrintInterval(T first, T last)
{//輸出區間[first,last)中的元素
  for(;first!=last;++first)
    cout<<*first<<" ";
  cout<<endl;
}

template<class T>
class SumPowers
{
  private:
  int power;
  public:
  SumPowers(int p):power(p){}
  const T operator()(const T & total, const T & value)
  {//計算value的power次方,加到total上
    T v = value;
    for(int i = 0; i < power - 1 ; ++i)
      v = v * value;
    return total + v;
  }
};

int main()
{
  const int SIZE = 10;
  int a1[] = {1,2,3,4,5,6,7,8,9,10};
  vector<int> v(a1,a1+SIZE);
  cout << "1.";
  PrintInterval(v.begin(),v.end());
  int result = accumulate(v.begin(),v.end(),0,SumSquares);//🌟
  cout << "2.平方和" << result << endl;
  result = accumulate(v.begin(),v.end(),0,SumPowers<int>(3));//🌛
  cout << "3.立方和" << result << endl;
  result = accumulate(v.begin(),v.end(),0,SumPowers<int>(4));
  cout << "4.四次方和" << result << endl;
  return 0;
}
//🌟句實例化出
int accumulate(vector<int>::iterator first, vector<int>::iterator last, int init, int(*op)(int,int))
{
  for(;first != last; ++first)
    init = op(init, *first);
  return init;
}
//🌛句實例化出
int accumulate(vector<int>::iterator first, vector<int>::iterator last, int init, SumPowers<int> op)
{
  for(;first != last; ++first)
    init = op(init, *first);
  return init;
}

STL中的函數對象類模版

如下模版能夠用來生成函數對象

  • equal_to
  • greater
  • less

頭文件:<functional>

greater函數對象類模版

template <class T>
  struct greater: public binary function <T, T, bool>{
    bool operator()(const T& x, const T& y) const{
      return x > y;
    }
  };

greater的應用

list有兩個sort成員函數

  • void sort();

    將list中的元素按"<"規定的比較方法升序排列

  • template <class Compare>

    void sort(Compare op);

    將list中的元素按op規定的比較方法升序排列,即要比較x,y大小時,看op(x,y)的返回值,爲true則認爲x小於y

#include <list>
#include <iostream>
using namespace std;
class MyLess{
  public:
  bool operator()(const int & c1, const int & c2)
  {
    return (c1 % 10) < (c2 % 10);
  }
};

template <class T>
  void Print(T first, T last)
{
  for(;first != last;++first)
    cout << * first << ",";
}

int main()
{
  const int SIZE = 5;
  int a[SIZE] = {5,21,14,2,3};
  list<int> lst(a,a+SIZE);
  lst.sort(MyLess());
  Print(lst,begin(),lst.end());//21,2,3,14,5
  cout << endl;
  lst.sort(greater<lint>());//greater<int>()是個對象
  Print(lst,begin(),lst.end());//21,14,5,3,2
  cout << endl;
}

在STL中使用自定義的「大」「小」關係

關聯容器盒STL中許多算法,都是能夠用函數或函數對象自定義比較器的。在自定義了比較器op的狀況下,如下三種說法等價:

  1. x小於y
  2. op(x,y)返回的值爲true
  3. y大於x

例題

  • 寫出MyMax模版
#include <iostream>
#include <iterator>
using namespace std;
class MyLess{
  public:
  bool operator()(int a1, int a2) {
    if((a1 % 10) < (a2 % 10))
      return true;
    else
      return false;
  }
};

bool MyCompare(int a1, int a2)
{
  if((a1 % 10) < (a2 % 10))
    return false;
  else
    return true;
}

int main()
{
  int a[] = {35,7,13,19,12};
  cout << *MyMax(a,a+5,MyLess()) << endl; //19
  cout << *MyMax(a,a+5,MyCompare << endl; //12
  return 0;
}

template <class T, class Pred>
T MyMax(T first, T last, Pred myless)
{
T tmpMax = first;
for(;first !=last;++first)
  if(myless(*tmpMax,*first))
    tmpMax = first;
return tmpMax;
};

習題

練習1

#include <iostream>
#include <iterator>
#include <set>
using namespace std;
int main() {
    int a[] = {8,7,8,9,6,2,1};
    set<int> v(a,a+7);
// 在此處補充你的代碼
    ostream_iterator<int> o(cout," ");
    copy( v.begin(),v.end(),o);
    return 0;
}

練習2 List

#include <iostream>
#include <iterator>
#include <list>//雙向鏈表
#include <vector>
#include <string>
using namespace std;

list<int>& FindList(vector< list<int> >& l, int id)
{
    int tmp = l.size();
    vector< list<int> >::iterator i;
    i = l.begin();
    return *(i + id - 1);
}

int main()
{
    int n;
    cin >> n;
    vector< list<int> > a;
    for (int i = 0; i < n; i++)
    {
        string s;
        cin >> s;
        if(s == "new")
        {
            int id;
            cin >> id ;
            a.push_back(list<int>());//這個答案是默認id不會跳着設定……
        }
        else if(s == "add")
        {
            int id, num;
            cin >> id >> num;
            list<int>& temp = FindList(a, id);
            temp.push_back(num);
            temp.sort();//將list中的元素按"<"規定的比較方法升序排列
        }
        else if(s == "merge")
        {
            int id1, id2;
            cin >> id1 >> id2;
            list<int>& temp1 = FindList(a, id1);
            list<int>& temp2 = FindList(a, id2);
            temp1.sort();
            temp2.sort();
            temp1.merge(temp2);
        }
        else if(s == "unique")
        {
            int id;
            cin >> id;
            list<int>& temp = FindList(a, id);
            temp.sort();
            temp.unique();
        }
        else if(s == "out")
        {
            int id;
            cin >> id;
            list<int>& temp =FindList(a, id);
            temp.sort();
            if (temp.size() > 0)
            {
                list<int>::iterator i;
                for (i = temp.begin(); i != temp.end(); i++)
                {
                    cout << *i << " ";
                }
            }
            cout << endl;
        }
    }
    return 0;
}
相關文章
相關標籤/搜索