STL浅析

#include <iostream>
#include <vector>
#include <functional>
#include <numeric>
#include <algorithm>
using namespace std;
int main()
{
    int ia[]={1,2,3,4,5};
    vector<int> iv(ia,ia+5);
    //cout<<accumulate(iv.begin(),iv.end(),1,multiplies<int>())<<endl;//120
    multiplies<int> mulObj;
    //cout<<mulObj(3,5)<<endl;//15
    sort(iv.begin(),iv.end(),greater<int>());//大数放在前
    modulus<int> modObj;//取余数(仿函数)
    cout<<modObj(3,5)<<endl;//3

    system("pause");

        return 0;

}

一.STL六大组件:

1.容器:vector list deque map set multiset 

2.算法(algorithm) sort search erase 

3.迭代器 (iterator) 泛型指针

4.配置器  (allocator) 负责空间配置和管理

5.仿函数(functional) 重载

6.配接器 修饰容器或仿函数或迭代器接口的东西

二. 常用迭代器

序列式容器:

1.vector

vector与数组类似,维护一个连续线性空间,支持随机存取,不同点是其是动态空间,随着元素的加入,它的内部机制会自行扩充空间以容纳新元素。其迭代器是普通指针,它以两个迭代器start和finish分别指向配置得来的连续空间中目前已被使用的范围,并以迭代器end_of_storage指向整块连续空间(含备用空间)的尾端 
为了降低空间配置时的速度成本,vector实际配置的大小可能比客户端需求量更大一些——capacity,这里缺省采用前述的alloc空间配置器,同时据此另外定义了一个data_allocator,以方便以元素大小为配置单位:typedef simple_alloc<value_type,Alloc> data_allocator

vector内存分配实现: 
当push_back(x)将新元素插入vector尾端时,该函数首先检查是否还有备用空间,如果有就直接在备用空间上构造元素,并调整迭代器finish;如果没有,就扩充空间(重新配置、移动数据、释放原空间): 
调用data_allocator::allocator重新配置一块大小为原空间两倍大小(若原空间为0,则新空间配置为1)的新内存空间 
——>调用uninitialized_copy将原vector的内容拷贝到新vector中,并对新元素设定初值x 
——>析构并释放原vector(destroy,deallocate) 
——>调整迭代器指向新的vector 
由上述过程可知,针对vector的任何操作,一旦引起空间重新配置,指向原vector的所有迭代器就都失效了

 

2、map: 
以红黑树为底层机制。 
map所有元素都会根据元素的键值自动被排序。map的所有元素都是pair,同时拥有实值value和键值key。可表示为pair<key,value>。map同样不允许两个元素有相同的键值(它的插入操作采用的是底层机制RB-tree的insert_unique()),相同键值的元素插入不会被覆盖,若想键值不唯一,可使用multimap。

 

不能通过map的迭代器改变map元素的键值,因为map元素的键值,关系到map元素的排列规则。如果任意改变map元素键值,会严重破坏map组织。但可以修正元素的实值。因此,map源码中迭代器既不是一种constant iterator,也不是一种mutable iterators。

 

与list相同。当客户端对它进行元素新增操作或删除操作时,操作之前的所有迭代器,在操作完成之后都依然有效(除了被删除元素的迭代器)。

 

定义形式:

 

    map<string,int>mymap;
    mymap[string("jihou")]=1;
    mymap.insert(pair<string,int>("jimmy",3));
    mymap["marry"]=6;
    mymap["marry"]=5;//将前值覆盖
    mymap["harry"]=5;//
    map<string ,int>::iterator it;
    it=mymap.begin();
    for (;it!=mymap.end();it++)
    {
        cout<<(*it).first<<"=>"<<(*it).second<<endl;
    }

[]操作符如simap[string(“jihou”)]既可以做左值,又可以做右值。如 
int number = simap[string(“jihou”)]; 
因此operator[]的返回值为引用,具体代码如下:

 T& operator[](const key_type& k) {

    return (*((insert(value_type(k,T()))).first)).second;
}

 附:

set/map底层为红黑树:

红黑树: 树根始终为黑色 外部结点均为黑色  其余节点若为红色,则其孩子节点必为黑色     从任意外部结点待根节点的沿途,黑色节点数目相同

为啥不用AVL树: 

  1. 单从查找、删除、插入等的时间复杂度来看,两者旗鼓相当,但因为AVL树对平衡条件要求更为严格,因此当进行操作时,AVL树进行调整的频率与次数要比红黑树高,也因此对于数据量较大插入或删除时或者数据复杂的情况,红黑树需要通过旋转变色操作来重新达到平衡的频度要小于AVL,因此红黑树比AVL(平衡二叉搜索树)具有更高的插入效率,虽然查找效率会平衡二叉树稍微低一点点,但是这种查找效率的损失是非常值得的。它的操作有着良好的最坏情况运行时间,并且在实践中是高效的: 它可以在O(log n)时间内做查找,插入和删除。

  2. 此外,需要平衡处理时。红黑树比AVL树多一种变色操作,而且变色的时间复杂度在对数量级上,但因为操作相对简单,所以在实际应用中,这种变色仍然十分快速,对效率影响较小

  3. 当插入一个节点引起树的不平衡时,AVL和红黑树都最多需要2次旋转操作。但删除一个节点引起不平衡之后,AVL最多需要logN次旋转操作,而红黑树最多只需要3次。任何不平衡都会在3次旋转之内解决。因此两者插入一个结点的代价差不多,但删除一个结点的代价红黑树要低一些。

  4. AVL和红黑树的插入删除代价主要还是消耗在查找待操作的结点上。因此时间复杂度基本上都是与O(logN) 成正比的。

posted on 2017-09-12 10:41  zhaodun  阅读(217)  评论(0编辑  收藏  举报

导航