STL

C++ STL 之所以得到广泛的赞誉,也被很多人使用,不只是提供了像vector, string, list等方便的容器,更重要的是STL封装了许多复杂的数据结构算法和大量常用数据结构操作。vector封装数组,list封装了链表,map和set封装了二叉树等,在封装这些数据结构的时候,STL按照程序员的使用习惯,以成员函数方式提供的常用操作,如:插入、排序、删除、查找等。让用户在STL使用过程中,并不会感到陌生。

STL可分为:

1)  容器(continers)、

2)  迭代器(iterator)、

3)  空间配置器

4)  配接器

5)  算法

6)  仿函数

容器:向量(vector)、双向链表(list)、集合(set)、映射(map)

一  vector

  1.概念:

 

  (1)vector是表示可变大小数组的序列容器。

  (2)就像数组一样,vector也采用的连续存储空间来存储元素。也就是意味着可以采用下标对vector的元素进行访问,和数组一样高效。但是又不像数组,它的大小是可以动态改变的,而且它的大小会被容器自动处理。

  (3)本质讲,vector使用动态分配数组来存储它的元素。当新元素插入时候,这个数组需要被重新分配大小为了增加存储空间。其做法是,分配一个新的数组,然后将全部元素移到这个数组。就时间而言,这是一个相对代价高的任务,因为每当一个新的元素加入到容器的时候,vector并不会每次都重新分配大小。

  (4)vector分配空间策略:vector会分配一些额外的空间以适应可能的增长,因为存储空间比实际需要的存储空间更大。不同的库采用不同的策略权衡空间的使用和重新分配。但是无论如何,重新分配都应该是对数增长的间隔大小,以至于在末尾插入一个元素的时候是在常数时间的复杂度完成的。

  (5)因此,vector占用了更多的存储空间,为了获得管理存储空间的能力,并且以一种有效的方式动态增长。

  (6)与其它动态序列容器相比(deques, lists and forward_lists), vector在访问元素的时候更加高效,在末尾添加和删除元素相对高效。对于其它不在末尾的删除和插入操作,效率更低。比起lists和forward_lists统一的迭代器和引用更好。

  (7)  Vector的大小(size)和容量(capacity)通常是不同的,catacity返回vector实际能容纳的元素数量,如果超过这个数量需要重新配置内部存储器。

  2.vector基本操作

向量大小: vec.size();
向量最大容量: vec.max_size();
更改向量大小: vec.resize();
向量真实大小: vec.capacity();
向量判空: vec.empty();
减少向量大小到满足元素所占存储空间的大小:
多个元素赋值: vec.assign(); //类似于初始化时用数组进行赋值
末尾添加元素: vec.push_back();
末尾删除元素: vec.pop_back();
任意位置插入元素: vec.insert();
任意位置删除元素: vec.erase();
交换两个向量的元素: vec.swap();
清空向量元素: vec.clear();
开始指针:vec.begin();
末尾指针:vec.end();
下标访问: vec[1]; //并不会检查是否越界
at方法访问: vec.at(1); //以上两者的区别就是at会检查是否越界,是则抛出out of range异常
访问第一个元素: vec.front();
访问最后一个元素: vec.back();
返回一个指针: int* p = vec.data(); //可行的原因在于vector在内存中就是一个连续存储的数组,所以可以返回一个指针指向这个数组。这是是C++11的特性。

 

二  deque(双端队列容器),和vector相似,vector一边操作,deque可以2边操作

               

 

 

三  string容器

  基本操作:

    

    (1)capacity():返回当前容量
    (2)max_size():返回string对象中可存放的最大字符串的长度
    (3)size():返回当前字符串的大小
    (4)length():返回当前字符串的长度
    (5)empty():当前字符串是否为空
    (6)resize(int len,char c):把字符串当前大小置为len,多去少补,多出的字符c填充不足的部分
    (7)find():查找函数
    (8)replace():替换函数
    (9)compare()比较函数
    (10)substr():字符串截取
    (11)insert():插入

 

 四 stack(栈容器

  基本操作:

empty:判断堆栈元素是否为空,true表示栈元素为空;
pop:移除栈顶元素;
push:栈顶添加元素;
top:返回栈顶元素;
size:返回栈中元素数目;

 

 

#include<iostream>
#include<stack>
using namespace std;
int main()
{   
        stack<int> stackTmp;

    for (int i = 0; i<10; i++)
    {
        stackTmp.push(i);
    }

    while (stackTmp.size()>1){

        cout << stackTmp.top() << " ";

        stackTmp.pop();

    }
    cout << stackTmp.top() << endl;

    system("pause");
    
    return 0;
}    
  

 五 queue(队列)

  基本操作:

    

    push(x); 将x 接到队列的末端。
    pop(); 弹出队列的第一个元素,注意,并不会返回被弹出元素的值。
    front(),即最早被压入队列的元素。
    back(),即最后被压入队列的元素。
    empty(),当队列空时,返回true。

  

  

#include<iostream>
#include<stack>
using namespace std;
int main()
{   
    queue <int> queueTmp;

    for (int i = 0; i<10; i++)
    {
        queueTmp.push(i);
    }

    while (queueTmp.size()>1){

        cout << queueTmp.front() << " ";

        queueTmp.pop();

    }
    cout << queueTmp.front() << endl;

    system("pause");
    
    return 0;
}    

  注意:就stack和queue没有对应的迭代器,不支持遍历

  

六  list(链表)

  list是一种序列式容器。list容器完成的功能实际上和数据结构中的双向链表是极其相似的,list中的数据元素是通过链表指针串连成逻辑意义上的线性表,list不仅是一个双向链表,而其还是一个环状双向链表。所以它只需要一个指针,便可以完整实现整个链表。list有一个重要性质:插入操作(insert)和合并操作(splice)都不会 造成原有的list迭代器失效。甚至 list的元素删除操作(erase)也只有“指向被删除元素”的那个迭代器失效,其他迭代器不受任何影响。

基本操作:

  

   listTmp.push_back(elem);//容器尾部添加一个元素
    listTmp.back(); //删除容器中的最后一个元素
    listTmp.push_front(elem); //在容器的开头插入一个元素
    listTmp.pop_front();//从容器的开头移除一个元素
    listTmp.insert(pos, elem);//在pos位置插入elem元素的拷贝,返回新数据的位置
    listTmp.insert(pos, n, elem); //在pos位置插入n个elem数据,无返回值
    listTmp.insert(pos, begin, end);//在pos位置插入[begin,end]区间的数据,无返回值
    listTmp.clear();//移除pos位置的数据,返回下一个数据的位置
    listTmp.erase(pos);//删除pos位置的数据,返回下一个数据的位置
    listTmp.remove();//删除容器中所有与elem值匹配的元素

七  set/multiset

关于set,必须说明的是set关联式容器。set作为一个容器也是用来存储同一数据类型的数据类型,并且能从一个数据集合中取出数据,在set中每个元素的值都唯一,而且系统能根据元素的值自动进行排序。应该注意的是set中数元素的值不能直接被改变。C++ STL中标准关联容器set, multiset, map, multimap内部采用的就是一种非常高效的平衡检索二叉树:红黑树,也成为RB树(Red-BlackTree)。RB树的统计性能要好于一般平衡二叉树,所以被STL选择作为了关联容器的内部结构

关于set有下面几个问题:
(1)为何map和set的插入删除效率比用其他序列容器高?
大部分人说,很简单,因为对于关联容器来说,不需要做内存拷贝和内存移动。说对了,确实如此。set容器内所有元素都是以节点的方式来存储,其节点结构和链表差不多,指向父节点和子节点。结构图可能如下:
 
  A
   / \
  B C
 / \ / \
  D E F G
因此插入的时候只需要稍做变换,把节点的指针指向新的节点就可以了。删除的时候类似,稍做变换后把指向删除节点的指针指向其他节点也OK了。这里的一切操作就是指针换来换去,和内存移动没有关系。
(2)为何每次insert之后,以前保存的iterator不会失效?
iterator这里就相当于指向节点的指针,内存没有变,指向内存的指针怎么会失效呢(当然被删除的那个元素本身已经失效了)。相对于vector来说,每一次删除和插入,指针都有可能失效,调用push_back在尾部插入也是如此。因为为了保证内部数据的连续存放,iterator指向的那块内存在删除和插入过程中可能已经被其他内存覆盖或者内存已经被释放了。即使时push_back的时候,容器内部空间可能不够,需要一块新的更大的内存,只有把以前的内存释放,申请新的更大的内存,复制已有的数据元素到新的内存,最后把需要插入的元素放到最后,那么以前的内存指针自然就不可用了。特别时在和find等算法在一起使用的时候,牢记这个原则:不要使用过期的iterator。
(3)当数据元素增多时,set的插入和搜索速度变化如何?
如果你知道log2的关系你应该就彻底了解这个答案。在set中查找是使用二分查找,也就是说,如果有16个元素,最多需要比较4次就能找到结果,有32个元素,最多比较5次。那么有10000个呢?最多比较的次数为log10000,最多为14次,如果是20000个元素呢?最多不过15次。看见了吧,当数据量增大一倍的时候,搜索次数只不过多了1次,多了1/14的搜索时间而已。你明白这个道理后,就可以安心往里面放入元素了。
set使用方法:
 
begin()        ,返回set容器的第一个迭代器
end()      ,返回set容器的最后一个迭代器
clear()          ,删除set容器中的所有的元素
empty()    ,判断set容器是否为空
max_size()   ,返回set容器可能包含的元素最大个数
size()      ,返回当前set容器中的元素个数
rbegin     ,返回的值和end()相同
rend()     ,返回的值和rbegin()相同
#include <iostream>
#include <set>    
using namespace std;    
int main()  
{      
    set<int> s;      
    s.insert(1);      
    s.insert(2);      
    s.insert(3);      
    s.insert(1);      
    cout<<"set 的 size 值为 :"<<s.size()<<endl;      
    cout<<"set 的 maxsize的值为 :"<<s.max_size()<<endl;      
    cout<<"set 中的第一个元素是 :"<<*s.begin()<<endl;      
    cout<<"set 中的最后一个元素是:"<<*s.end()<<endl;      
    s.clear();      
    if(s.empty())      
    {          
        cout<<"set 为空 !!!"<<endl;      
    }      
    cout<<"set 的 size 值为 :"<<s.size()<<endl;      
    cout<<"set 的 maxsize的值为 :"<<s.max_size()<<endl;      
    return 0;  
}  

小结:插入3之后虽然插入了一个1,但是我们发现set中最后一个值仍然是3哈,这就是set 。还要注意begin() 和 end()函数是不检查set是否为空的,使用前最好使用empty()检验一下set是否为空.

count() 用来查找set中某个某个键值出现的次数。这个函数在set并不是很实用,因为一个键值在set只可能出现0或1次,这样就变成了判断某一键值是否在set出现过了

#include <iostream> 
#include <set>    
using namespace std;    
int main()  
{      
    set<int> s;      
    s.insert(1);      
    s.insert(2);      
    s.insert(3);      
    s.insert(1);      
    cout<<"set 中 1 出现的次数是 :"<<s.count(1)<<endl;      
    cout<<"set 中 4 出现的次数是 :"<<s.count(4)<<endl;      
    return 0;  
}  

equal_range() ,返回一对定位器,分别表示第一个大于或等于给定关键值的元素和 第一个大于给定关键值的元素,这个返回值是一个pair类型,如果这一对定位器中哪个返回失败,就会等于end()的值。

#include <iostream>  
#include <set>    
using namespace std;    
int main()  
{      
    set<int> s;      
    set<int>::iterator iter;      
    for(int i = 1 ; i <= 5; ++i)      
    {          
        s.insert(i);      
    }      
    for(iter = s.begin() ; iter != s.end() ; ++iter)      
    {          
        cout<<*iter<<" ";      
    }      
    cout<<endl;      
    pair<set<int>::const_iterator,set<int>::const_iterator> pr;      
    pr = s.equal_range(3);      
    cout<<"第一个大于等于 3 的数是 :"<<*pr.first<<endl;      
    cout<<"第一个大于 3的数是 : "<<*pr.second<<endl;      
    return 0;  
}  

erase(iterator)  ,删除定位器iterator指向的值

erase(first,second),删除定位器first和second之间的值

erase(key_value),删除键值key_value的值

#include <iostream>  
#include <set>    
using namespace std;    
int main()  
{      
    set<int> s;      
    set<int>::const_iterator iter;      
    set<int>::iterator first;      
    set<int>::iterator second;      
    for(int i = 1 ; i <= 10 ; ++i)      
    {          
        s.insert(i);      
    }      
    //第一种删除      
    s.erase(s.begin());      
    //第二种删除      
    first = s.begin();      
    second = s.begin();      
    second++;      
    second++;      
    s.erase(first,second);      
    //第三种删除      
    s.erase(8);      
    cout<<"删除后 set 中元素是 :";      
    for(iter = s.begin() ; iter != s.end() ; ++iter)      
    {         
        cout<<*iter<<" ";      
    }      
    cout<<endl;      
    return 0;  
}  

find()  ,返回给定值值得定位器,如果没找到则返回end()。

#include <iostream>  
#include <set>    
using namespace std;    
int main()  
{      
    set<int> st;
    st.insert(4);
    st.insert(5);
    st.insert(3);
    st.insert(1);
    st.insert(2);

    set<int>::iterator it = st.find(4);
    //find()返回要查找值的迭代器,如果没有,则返回end()
    if (it != st.end())
    {
        cout << *it << endl;
    }
    else
    {
        cout << "没有找到" << endl;
    }
    return 0;  
}  

lower_bound(key_value) ,返回第一个大于等于key_value的定位器

upper_bound(key_value),返回最后一个大于等于key_value的定位器

#include <iostream>  
#include <set>    
using namespace std;    
int main()  
{      
    set<int> s;      
    s.insert(1);      
    s.insert(3);      
    s.insert(4);      
    cout<<*s.lower_bound(2)<<endl;      
    cout<<*s.lower_bound(3)<<endl;      
    cout<<*s.upper_bound(3)<<endl;      
    return 0;  
}  

 

 

  迭代器:是面向对象版本的指针。

1)  指针可以指向内存中的一个地址,

2)  迭代器可以指向容器中的一个位置,用来便利STL容器的全部或者部分元素。

3)  STL的每一个容器类模板中,都定义了一组对应的迭代器类

a)*返回当前位置上的元素值,如果给元素有成员,可以通过迭代器以operator->取用。

b)   ++   将迭代器前进至下一个元素。

c)  ==&!=   判断两个迭代器是否指向同一位置。

d)=  为迭代器赋值(将所指元素的位置赋值过去) 

1)begin():返回一个迭代器,指向第一个元素。

2)end():返回一个迭代器,指向序列结束。

3)sort(array.begin(),array.end());    排序

 

posted @ 2017-09-13 08:21  zwj鹿港小镇  阅读(150)  评论(0编辑  收藏  举报