C++学习:STL-容器

一、容器分类
二、序列式容器的创建
三、序列式容器的访问
四、序列式容器的修改
五、序列式容器元素的容量
六、set/multiset    
七、map/multimap  

容器是STL的基础,容器分2种:序列式容器(sequential container)、关联式容器(associative container)    
序列式容器会强调元素的次序,依次维护第一个元素、第二个元素……,直到最后一个元素,面向序列式容器的操作主要是迭代操作。

一、容器分类:
序列式容器(sequential container):
  vector       //向量
  deque       //双向队列
  list           //线性表
关联式容器(associative container):
  Set           //集合       采用红黑树来做的。存放关键字。  关键字不重复。
  MultiSet   //多集合   采用红黑树来做的。存放关键字。 关键字可重复。
  Map         //映射       采用红黑树来做的。存放键值对。  关键字不重复。
  MultiMap //多映射   采用红黑树来做的。 存放键值对。  关键字可重复。

       
 
所有的容器都支持的方法:
ob.begin()           //返回一个迭代器,该迭代器指向容器的第一个元素
ob.end()              //返回一个迭代器,该迭代器指向容器的最后一个元素的下一个元素
empty          
ob.size()             //返回元素个数
max_size
clear
insert
erase
ob.swap(ob2)     //交换ob和ob2的内容
ob1==ob2          //如果ob1和ob2长度相同,且每个对应元素都相等,返回true
ob1 !=ob2          //!(ob1==ob2)

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
二、序列式容器的创建:6种方式 
(1)创建空的容器,此时容器中的元素个数为0。 
vector<int> obV;                         //直接创建空的。
list<float> obL; 
deque<double> obD; 

(2) 产生特定大小的容器。
vector<double> obV(10);             //产生特定大小的容器。此时容器中的元素被创建,编译器使用默认值为元素隐式初始化,像int、float                                                    和double等内置的数据类型会被初始化为0,对于类对象元素,将调用其无参构造函数(用户定义的或编                                                    译器缺省提供的)或每个参数都有默认值的构造函数。此例中,obV中含10个double型元素, 初始化为0.
list<int> obL(20);                        //创建list对象,含20个int型元素, 初始化为0  
deque<float> obD(30);                 //创建deque对象,含30个float型元素, 初始化为0  

(3) 创建特定大小的容器,并且为其中的每个元素指定初始值 
vector<int> obV(10,8);                //10个int型元素, 每个都初始化为8 
list<double> obL(20,3.1);            //20个double型元素, 每个都初始化为3.1 
deque<string> obD(30,"Hello");   //30个string型元素, 每个都初始化为"Hello" 

(4)根据已有同类型的容器创建新容器,并将其中的元素完全复制过来 
vector<int> obV2(obV1);             //或vector<int> obV2=obV1;  
list<int> obL2(obL1);                  //或list<int> obL2=obL1;  
deque<int> obD2(obD1);             //或deque<int> obD2=obD1;  

(5)通过数组 
int sz[5]={11,2,3,4,45}; 
vector<int> obV(sz, sz+5); 
list<int> obL(sz, sz+5); 
deque<int> obD(sz, sz+5); 

(6)C++11新特性 
vector<int> vec1={1,2,3,4,5};
list<int> vec2={1,2,3,4,5};
deque<int> vec3={1,2,3,4,5};

三、序列式容器的访问 
vector和deque类的容器创建后就可以通过容器名[下标]或容器名.at(序号)的形式对元素进行随机访问(这是因为这2种类模板对下标运算符[]进行了重载); 也支持迭代器访问。   
list类的容器不支持下标运算符[],无法使用[]对元素进行随机访问。但支持双向迭代器访问。
              
                     vector   deque   list
at                     1         1         0
operator[]         1         1         0               //下标访问运算符
迭代器访问     1          1        1  
front                1          1        1               //获取容器头部元素。
back                1          1        1               //获取容器尾部元素。


例1:
#include <iostream>
#include <vector>
#include <list>
#include <deque>
using std::cout;
using std::endl;
using std::vector;
using std::list;
using std::deque;

int test Deque(void){                                                            //实验vector和deque
        double arr1[5] = {1, 2, 3, 4, 5};
        deque<double> dequeDbl(arr1, arr1 + 5);                      //左闭右开。写[arr1[0],arr1[5]) ,则只有arr1[0],arr1[1],arr1[2],arr1[3],arr1[4]入队

        for(int idx = 0; idx != dequeDbl.size(); ++idx){             //访问方式一:利用下标运算符随机访问 
                cout <<  dequeDbl[idx] << " ";
        }
        cout << endl;

        deque<double>::iterator dit = dequeDbl.begin();            //访问方式二:迭代器 
        for(; dit != dequeDbl.end(); ++dit){
                cout << *dit << " ";
        }
        cout << endl;

        dit = dequeDbl.end();                                                  
        --dit;                                                                           //因为.end()指向最后一个元素的下一个元素。所以--
        for(; dit != dequeDbl.begin() -1; --dit){                         //因为.begin()执行第一个元素,判断到第一个时,还是要打印,所以-1
                cout << *dit << " ";
        }
        cout << endl;

        for(auto & elem : dequeDbl){                                       //访问方式三:c++11 新特性,auto关键字 
                cout << elem << " ";        
  }
        cout << endl;
        return 0;
}

int testList(){
        list<float> listFlt(3, 5);
        listFlt[0] = 10;                                                             //错误。list容器没有重载下标访问运算符,不支持随机访问。

        list<float>::iterator lit;
        for(lit = listFlt.begin(); lit != listFlt.end(); ++lit){           //通过迭代器访问。
                (*lit) += 2;
                cout << *lit << " ";
        }
        cout << endl;

        list<float> listFlt2(4, 9);                                               //4个元素,初始为9
        listFlt.swap(listFlt2);                                                    //交换元素。进行交换,交换并没有移动数据,只是交换了指针而已
        for(lit = listFlt.begin();lit != listFlt.end(); ++lit){
                (*lit) += 2;
                cout << *lit << " ";
        }
        cout << endl;

        return 0;
}

int main(void){
        testDeque();
        testList();

        return 0;
}

例2:
int main(){
  vector<int> vecInt;
  vec.reserve(10);                                                             //预先开辟10个空间
  vecInt[5]=3;                                                                  //错误。因为vetInt中没有数据。
}

四、序列式容器元素的修改  
        vector   deque   list    
push_bak       1           1       1                   //在容器尾部进行插入
pop_bak        1           1       1                   //在容器尾部进行删除
push_front     0           1       1                   //在容器头部进行插入。 vector是数组,不能在头部插入。
pop_front      0           1       1                   //在容器头部进行删除。 vector是数组,不能再头部删除。
insert            1           1       1                   //在容器中间插入元素
erase            1            1       1                   //删除容器中间的元素
clear             1           1       1                   //将容器中的对象清空。不是将元素设为0或什么,而是删掉。清空之后就不能访问这个元素了
swap            1            1       1                   

insert函数的使用
iterator insert(iterator p, elemType t);                     //将元素t插到p之前,返回的迭代器指向被插入的元素。
void insert(iterator p, int n, elemType t);                 //在p之前插入n个t,无返回值。
void insert(iterator p, iterator first, iterator last);      //在p之前插入[first, last)之间的所有元素。 
 对于vector而言,使用insert的时候元素要后移,时间复杂度是O(n), 尽量避免使用。

例1:
#include <iostream>
#include <vector>
using std::cout;
using std::endl;
using std::vector;

template <typename T>                                        //函数模板。不管是哪种容器,都可以调用display方法。
void display(T & c){
        typename T::iterator cit;                               //typename关键字告诉编译器iterator是一个类类型,而不是其它的。 
        for(cit = c.begin(); cit != c.end(); ++cit){
                cout << *cit << " ";
        }
        cout << endl;
}

int main(void){
        vector<int> vecInt(5, 0);                               //5个元素,初始为0 
        display(vecInt);                                            //0 0 0 0 0 

        vector<int>::iterator it = vecInt.end();            //获取迭代器的位置。指向最后一个元素的下一个元素。
        it = vecInt. insert(it, 1);                                 //在it之前插入元素1,也就是在最后一个元素之后插入1。然后迭代器指向被插入的元素
        display(vecInt);                                            //0 0 0 0 0 1 
        cout << "*it = " << *it << endl;                     //因为迭代器指向插入的元素,所以为1 

        vecInt.insert(it, 2, 3);                                    //在it的位置之前插入2个3。
        display(vecInt);                                            //0 0 0 0 0 3 3 1 

        it = vecInt.begin();                                        //it移动到头部位置
        int arr[3] = {7, 8, 9};               
        vecInt.insert(it, arr, arr + 3);                          //在it之前插入arr[0]到arr[3]之间的元素(前闭后开),即插入arr[0],arr[1],arr[2] 
        display(vecInt);                                            //7 8 9 0 0 0 0 0 3 3 1 
        cout << endl;

        return 0;
}


erase函数的使用 
iterator erase(iterator p);                                        //删除迭代器p所指向的元素,返回p指向的下一个迭代器
iterator erase(iterator first, iterator last);                  //删除[frist,last)之间的所有元素,返回last指向的下一个迭代器

五、序列式容器元素的容量  
        vector   deque   list   
empty             1          1        1
size                1          1        1                  //当前vec拥有多少个元素 
max_size        1          1        1 
resize(count)   1          1        1                  //调整容器大小,使其能够容纳count个元素。若当前大小<count,则追加额外元素,并初始化。
                                                               若当前大小>count,则容器会缩小到仅包含前count个元素。
cpapcity         1          0        0                  //容量。当前vec最多可以存放多少元素 
reserve(10);    1          0        0                  //预先开辟一定的空间 
shrink_to_fit   1          1        0                  //删除未使用的容量。C++11的用法。

resize函数的使用
例1:
#include <iostream>
#include <vector>

int main (){
    std::vector<int> foo ( 100);                                //产生100个元素的vector,初始化为0
    std::cout << foo.capacity() << std::endl;            //100

     foo.resize(10);                                                  //调整容器大小为10。容器会缩小到仅包含前10个元素。但是capacity的值仍然为100 
 std::cout << foo.size() << endl;                         //10。只有10个元素(都为0)
    std::cout << foo.capacity() << std::endl;            //100。capacity值为100

    return 0;
}

例2:
#include <iostream>
#include <vector>
using std::cout;
using std::endl;
using std::vector;

int main(void){
        vector<int> vecInt = {1, 2, 3};                                    
        cout << vecInt.capacity() << endl;                    //3
         vecInt.resize(10);                                             //调整容器大小为10。会追加额外元素,并初始化。
        for(auto & elem : vecInt)
                cout << elem << " ";                                //1 2 3 0 0 0 0 0 0 0   
        cout << vecInt.capacity() << endl;                    //10。元素个数长了,容量也会跟着涨。
   return 0;
}

shrink_to_fit函数的使用  
例1:
#include <iostream>
#include <vector>

int main (){
    std::vector<int> foo (100);                                  //产生100个元素的vector,初始化为0.
    std::cout << foo.capacity() << std::endl;              //100。

    foo.resize(10);                                                   //调整容器大小为10.
    std::cout << foo.capacity() << std::endl;              //100。capacity值为100,但是只有10个元素(都为0)。

    foo.shrink_to_fit() ;       
    std::cout << foo.capacity() << std::endl;               //10。删除多余的容量(后面90个)。

    return 0;
}

例2:
#include <iostream>
#include <vector>
using std::cout;
using std::endl;
using std::vector;

int main(void){
        vector<int> vecInt = {1, 2, 3};
        cout << vecInt.capacity() << endl;                   //3。容量为3
        vecInt.resize(100);                                          //100。调整容器大小为100
        cout << vecInt.capacity() << endl;                   //100。
   vecInt.shrinke_to_fit();                                    //因为当前有100个元素(最后一堆0也是数据),所以没有未使用的容量要删 
        cout << vecInt.capacity() << endl;                   //100。打印出还是100。

   vecInt.clear();                                                 //把数据清空,此时capacity还是100。相当于vecInt.resize(0);
   cout << vecInt.capacity() << endl;                  //100。
   vecInt.shrink_to_fit();                                     //容量为100,当前无元素。所以可以删除未使用的容量。
   cout << vecInt.capacity() << endl;                   //0 。删除未使用的容量后,容量变为了0 

        return 0;
}

例3:不使用C++11的shrink_to_fit方法,而是使用swap函数实现未使用容量的删除。
#include <iostream>
#include <vector>
int main(){
    vec.reserve(100);                                            //预先开辟100空间的大小
        vecInt.push_back(1);
        vecInt.push_back(2);
        vecInt.push_back(3);
        cout << vecInt.size() << endl;                         //3
        cout << vecInt.capacity() << endl;                  //100

        vector<int>(vecInt).swap(vecInt);                   //或 vecInt.swap(vector<int>(vecInt));  等价于调用C++11的vecInt.shrink_to_fit();函数。                                                                                vector<int>(vecInt)表示产生一个临时的vector,并把vecInt中的元素拷贝过来。所以临时                                                                               的vector就是一个含有3个元素,并且容量为3的vector。然后这个临时的对象调用swap                                                                              函数,与vecInt进行了交换。这样vecInt就成了含有3个元素,容量为3的vector,而临时                                                                            的对象就成了含有3个元素,容量为100的vector。执行完这句话之后,临时的vector释                                                                              放掉了。
        for(auto & elem : vecInt) 
    cout << elem << " ";                               //1 2 3
        cout << endl;
        cout << vecInt.capacity() << endl;                  //3。执行完之后,容量变成了3

   return 0;
}

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
关联式容器
set              采用红黑树来做的。存放关键字。  关键字不重复。
multiset       采用红黑树来做的。存放关键字。 关键字可重复。
map            采用红黑树来做的。存放键值对。  关键字不重复。
multimap     采用红黑树来做的。存放键值对。  关键字可重复。


六、set/multiset    
例1:set的创建   
#include <iostream>
#include <set>
using std::cout;
using std::endl;
using std::set;

int main(void){
        int arr[9] = {2, 1, 3, 5, 4, 6, 3, 5, 6};
   set<int> setInt(arr,arr+9);                                  //创建一个set。默认情况下采用"<"排序,即set<int, std::less<int> > setInt(arr, arr + 9);
        cout << setInt.size() << endl;                             //set和map并没有capacity方法.因为是用红黑树实现的,可以一直往里添加。
   set<int>::iterator sit;
        for(sit = setInt.begin(); sit != setInt.end(); ++sit) //set不支持[]下标式的随机访问,必须通过迭代器访问元素。
                cout << *sit << " ";                                   //1 2 3 4 5 6 。放了9个元素只存放了6个.这说明set会过滤掉已经存放在其中的元素。                                                                               同时会对元素进行排序。默认情况下采用"<"进行排序。
        cout << endl;
  
   return 0;
}

例2:采用">"的排序方式进行元素的创建  
#include <iostream>
#include <set>
using std::cout;
using std::endl;
using std::set;

int main(void){
        int arr[9] = {2, 1, 3, 5, 4, 6, 3, 5, 6};
   set<int, std::greater<int>> setInt(arr,arr+9);          //创建一个set。不采用默认的方式,而是采用">"的方式。
        cout << setInt.size() << endl;                           
   set<int>::iterator sit;
        for(sit = setInt.begin(); sit != setInt.end(); ++sit)
                cout << *sit << " ";                                   //6 5 4 3 2 1  
        cout << endl;
  
   return 0;
}

例3:自定义类型的排序,需要自定义">","<"运算符。 
#include <iostream>
#include <set>
using std::cout;
using std::endl;
using std::set;

class Point{
        friend std::ostream & operator << (std::ostream & os, const Point &rhs);
public:
        Point(int ix, int iy)
        : _ix(ix)
        , _iy(iy)
        {
   }

        int product() const{                                                              //坐标的乘积
      return _ix * _iy;        
   }
private:
        int _ix;
        int _iy;
};

std::ostream & operator << (std::ostream & os, const Point &rhs){   //重载输出运算符。
        os << "(" << rhs._ix<< "," << rhs._iy<< ")";
}

bool operator >(const Point & lhs, const Point & rhs){                     //重载大于符号。
        return lhs.product() > rhs.product();                                       //如果lhs坐标的乘积>rhs坐标的乘积,返回true 
}

int main(void){
        Point arr[4] = {
                Point(1, 2),
                Point(3, 4),
                Point(-1, 2),
                Point(4, 5)
        };

        //自定义排序函数
        set<Point,  std::greater<Point> > setPoint(arr, arr + 4);             //创建一个集合,存放Point类型。 不采用默认的方式,采用">"的方式。
        set<Point,  std::greater<Point> >::iterator sit;                           //迭代器 
        for(sit = setPoint.begin();sit != setPoint.end();++sit){
                cout << * sit << endl;                                                   //*sit是一个Point,所以要重载输出运算符。结果:(4,5) (3,4) (1,2) (-1,2) 
        }

        return 0;

}

例4:multiset与set不同之处在于其允许出现相同的关键字。  
#include <iostream>
#include <set>
using std::cout;
using std::endl;
using std::multiset;

int main(void){
        int arr[9] = {2, 1, 3, 5, 4, 6, 3, 5, 6};
         multiset<int, std::greater<int> > msetInt(arr, arr + 9);
        cout << "msetInt's size: " << msetInt.size() << endl;

        multiset<int>::iterator sit;
        for(sit = msetInt.begin(); sit != msetInt.end(); ++sit)
                cout << *sit << " ";                                               
        cout << endl;
        return 0;
}

七、map/multimap  
map中存放的是pair.
multimap与map的区别是关键字可以相同。

例1:pair
#include <iostream>
#includde <utility>                                                   //pair单独使用时用到的头文件 
using std::cout;
using std::endl;
using std::pair;
using std::string;

int main(void){
        pair<string, string> t("600036", "招商银行");      //键值对。"600036"是键,"招商银行"是值。
        cout << t.first << "-->" << t.second << endl;        //600036-->招商银行。pair是用结构体定义的。first和second的访问权限是public。 
  
   return 0;
}

例2:map的创建和使用
#include <iostream>
#include <string>
#include <map>
using std::cout;
using std::endl;
using std::pair;
using std::string;
using std::map;

int main(void){
        pair<int, string> pairArr[4] ={                                               //创建pair的数组 
                pair<int, string>(1, "北京"),
                pair<int, string>(2, "深圳"),
                pair<int, string>(3, "广州"),
                pair<int, string>(2, "苏州"),
        };

        map<int, string> mapi2str(pairArr, pairArr + 4);                     //创建map。map里面不允许有相同关键字的pair元素。关键字相同时会                                                                                                    自动过滤掉。因为这里有两个关键字2,所以第二个键值对会过滤掉。同                                                                                                   时会根据关键字进行排序。

        map<int, string>::iterator mit;                                                //通过迭代器方式访问。
        for(mit = mapi2str.begin();mit != mapi2str.end();++mit){
                cout << mit->first << "-->"<< mit->second << endl;        //输出:1-->北京 2-->深圳 3-->苏州 。会根据关键字进行排序。
        }

        return 0;
}


例子3:map支持下标式的访问运算符。
#include <iostream>
#include <string>
#include <map>
using std::cout;
using std::endl;
using std::pair;
using std::string;
using std::map;

int main(void){
        pair<string, string> pairArr[4] ={
                pair<string, string>("1", "北京"),
                pair<string, string>("2", "深圳"),
                pair<string, string>("3", "广州"),
                pair<string, string>("2", "苏州"),
        };

        map<string, string> mapi2str(pairArr, pairArr + 4);
        cout << "2-->" << mapi2str["2"] << endl;                                 // 输出2-->深圳。 map支持[]下标式的随机访问.set/multiset/multimap都                                                                                                         支持。由此k看 出: ["2"]的"2" 代表的是关键字,不是数字。                
        return 0;
}

例4:可以通过下标访问和修改map的值 
#include <iostream>
#include <string>
#include <map>
using std::cout;
using std::endl;
using std::pair;
using std::string;
using std::map;

int main(void){
        map<string, string> mapss;                                                    //创建map
        mapss["11"] = "aa";                                                                //没有相应关键字时,直接添加 
        mapss["22"] = "bb";
        mapss["33"] = "cc";
        mapss["33"] = "dd";                                                               //有相应关键字时,直接修改value的内容 

        for(auto & elem : mapss){                                                      //使用auto关键字的方式访问。输出:11-->aa 22-->bb 33-->dd 
                cout << elem.first << "-->"<< elem.second << endl;
        }
  
   return 0;
}

例5:multimap允许关键字重复
#include <iostream>
#include <map>
#include <string>
using std::cout;
using std::endl;
using std::multimap;
using std::pair;
using std::string;

int main(void){
        pair<int, string> pairArr[4] ={
                pair<int, string>(9, "Asia"),
                pair<int, string>(4, "Africa"),
                pair<int, string>(1, "Euro"),
                pair<int, string>(4, "America")
        };
        multimap<int, string> mmapi2str(pairArr, pairArr + 4);
        cout << mmapi2str.size() << endl;                                          //4 
        for(auto & elem : mmapi2str){
                cout << elem.first << "-->" << elem.second << endl;       //1-->Euro 4-->Africa 4-->America 9-->Asia。关键字重复很可能采用链表                                                                                                    的方式往上挂。 
        }

        return 0;
}





stl萃取技术
迭代器模式
适配器模式



unordermap:
时间复杂度o(a)


补充:unordered_map 









猜你喜欢

转载自blog.csdn.net/pengchengliu/article/details/80525123