C/C++程序设计07(STL容器和泛型算法)

前段时间内看过C++primer,在新浪博客做了一下笔记,笔记写的很简陋,今天又重温了一下,由于平时都在用,而且这段时间比较忙,就不打算系统的整理,只是将以前的笔记复制过来,可能以前的理解错误较多,以后有时间系统在总结一下。

 第九章  顺序容器

    容器类共享公共的接口,这样,只需要改变类型声明,用一种容器代替另一种容器类型,就可以优化程序的性能。

    1. 顺序容器的定义

    每一个容器都是一个类模板,都有自己的构造函数,最好使用默认构造函数,容器有几种构造函数

,在容器复制时,元素类型一定要一致。可以用迭代器来一个一个复制,只要他们兼容。如果元素类型没有默认构造函数,则必须显示的制定元素的初始化。访问容器时,不能越界。

    c++语言规定,元素类型必须具有复制赋值的才有容器,而IO对象和引用不能复制和赋值,故没有他们的容器。容器的容器,vector< vector >  **;注意“> >”要有空格,不然成“>>”易当做右移操作。

   2.容器的迭代器和迭代范围

     所有容器都具有相同的接口,注意迭代器的解引用符号“*”的用法;所有容器的迭代器都支持自增自减判别是否相等,而vectordeque还支持迭代器的加减,判别大小的操作。

    对容器内元素进行删除插入后,迭代器失效,故要重新计算end()来避免出问题.

    3.顺序容器的操作

     由容器定义的三种类型 size_type,itetator,和const_iterator;以及其他的reverse_iterator,等;例如:int类型的vector容器应该使用size_type的索引来访问。

    容器正序,c.begin()指向第一个元素,c.end()最后一个的下一个位置;反序c.rbegin()指向最后一个;

    在容器内添加元素,所有顺序容器都支持push_back();push_front();insert(*,*,);的操作。使用insert(迭代器位置)添加元素时,该函数返回指向新插入元素的迭代器。还可以插入一段元素insert(*,*,*);插入后回事后面的迭代器失效,所以此时要重新计算迭代器的大小end();

    迭代器的关系操作符有限制(知识点2),而对于容器,所有容器都支持关系操作来比较大小,比较方法从第一个开始比较。

    容器的大小操作,c.size(),c.max_size();c.empty(); c.resize(n); c.resize(n,t);

    访问元素,c.back()返回最后一个元素的引用,c.front()第一个的引用,c[n]和c.at[n]一样的是下标的访问。注意返回的是引用,最好这么接受例如:list::refence vai=ilist.begin();

     删除元素 c.erase(p),删除迭代器p指向的元素,c.erase(p,q),删除p,q之间的元素。c.clear()清空,删除最后一个元素pop_back(),pop_front();

    赋值和swap, 类型必须相同的赋值直接用等号,赋值后两个大小相同。c1.swap(c2),两个交换;重新设置元素c1.assign(iter1,iter2),iter1和iter2必须是指向别的容器的迭代器。c.assign(n,t); swap的要求是类型要兼容。

    vector容器的元素总数v.size()和容量总数v.capacity();容量肯定大于等于元素大小,一般情况是1.5:1.0的关系。有了容量,则在添加新的元素,原来的vector就会减少为增大其大小而不断来回复制的开销。v.reserve(n)是申请分配n的内存大小,即容量大小。

   

 5 容器的选用     

三种容器均支持resieze()操作,重新划定容器大小,且此函数有重载。
vector 
vector和built-in数组类似,是一个在堆上建立的一维数组,它拥有一段连续的内存空间,并且起始地址不变,因此 它能非常好的支持随即存取,即[]操作符。vector因为存储在堆上,所以支持erase( ), resieze()(重新划分容器容量)等操作; vector不用担心越界当空间不够用的时候,系统会自动按照一定的比例(对capacity( )大小)进行扩充。在vector序列末尾添加(push_back( ))或者删除(pop_back( ))对象效率高,在中间进行插入或删除效率很低,主要是要进行元素的移动和内存的拷贝,原因就在于当内存不够用的时候要执行重新分配内存,拷贝对象到新存储区,销毁old对象,释放内存等操 作,如果对象很多的话,这种操作代价是相当高的。为了减少这种代价,使用vector最理想的情况就是事先知道所要装入的对象数目,用成员函式 reserve( )预定下来;vector最大的优点莫过于是检索(用operator[ ])速度在这三个容器中是最快的,

list

list的本质是一个双向链表(根据sgi stl源代码),内存空间不连续,通过指针进行操作。说道链表,它的高效率首先表现是插入,删除元素,进行排序等等需要移动大量元素的操作。显然链表没有检索操作operator[ ], 也就是说不能对链表进行随机访问,而只能从头至尾地遍历,这是它的一个缺陷。list有不同于前两者的某些成员方法,如合并list的方法splice( ), 排序sort( ),交换list 的方法swap( )等等。

deque

deque是一个double-ended queue是由多个连续内存块构成,deque是list和vector的兼容,分为多个块,每一个块大小是512字节,块通过map块管理,map块里保存每个块得首地址。因此该容器也有索引操作operator[ ],效率没vector高。另外,deque比vector多了push_front( ) & pop_front( )操作。在两端进行此操作时与list的效率 差不多。

下面是选择顺序容器类型的一些准则

1.如果我们需要随机访问一个容器则vector要比list好得多 。
2.如果我们已知要存储元素的个数则vector 又是一个比list好的选择。 
3.如果我们需要的不只是在容器两端插入和删除元素则list显然要比vector好 
4.除非我们需要在容器首部插入和删除元素否则vector要比deque好。
5.如果只在容易的首部和尾部插入数据元素,则选择deque.
6.如果只需要在读取输入时在容器的中间位置插入元素,然后需要随机访问元素,则可考虑输入时将元素读入到一个List容器,接着对此容器重新排序,使其适合顺序访问,然后将排序后的list容器复制到一个vector容器中

6 再谈string

       string支持的操作:除了有关栈的操作back,front,pp_back,等操作,其他的操作有何vector几乎一样。而且对元素的查找等还有更多的操作。

      构造函数,基本一样,显示初始化,其他的string来选择性复制等,用子串来初始化等,修改也支持insert,erase,assign等的操作。string有自己独有的操作,返回自己的子串 string  str=st.substr(pos,n);等操作,还有在末尾添加的操作append(*),以及替代replace操作,替代时大小可以不同。string还有查找函数find开头的各种变形,正想反向,从某个位子等开始查找。还提供两个string对象的比较compare操作。

string   我把它理解成一个针对字符的vector,因为字符串的特殊,所以特地做出这样的一个东西来。

std::string是会在元素后面加上一个我们看不见的元素'\0'. 
std::string不支持push_back()
std::vector没有对cout重载

     7 容器适配器

      容器适配器是使一事物的行为类似于另一事物,也就是说容器适配器类似于容器。有三种适配器分别是:queue,stack,和priority_queue;前两种默认为deque容器实现,与他们有类似的操作(只有少部分成员函数的书写有一点点不同),后一种默认为vector实现,是队列优先级的适配器。这些都可以人为指定来修改,比如:stack> a;则a的stack关联的就不是默认的deque,而是vector了 等价于vector a;所有的适配器都根据其基础类型所支持的操作来定义自己的操作。


第十章 关联容器

关联容器和顺序容器的差别是:关联容器通过剑(key)来存储和读取元素,而顺序容器则是按容器中的顺序和位置来读取操作。关联容器的行为大多和顺序容器的行为相同,只是多了一个Key的支持。

本章的关联容器有map(关联数组),set(可变大小的集合),multiset和multimap,这两个的一个key可以对应多个set和map.

1.标准库pair类型

pair也是标准库的一个类模板,头文件utility在里面。pair P;有其对应的first,和second来访问第一个或第二个,make_pair(first,second)返回一个pair对象。这个类型理解就行了不多讲。

2. 关联容器

关联容器支持大部分的顺序容器的操作,除了front,back,及对应的pop_和push_操作。

关联容器不能通过容器大小来定义,因为这样就无法知道键对应的只是什么,它的排列顺序是按键的次序来排列的,可以按键的顺序来访问。

对比list(适合无序的中间随机插入),vector(适合随机访问),deque(适合队列的首尾删除的操作),map(适合键-值对应的操作,如字典),set(合适一个集合操作,如黑名单)。

3.map类型

    前面讲了pair ,而现在的map存放的都是pair对,即 键-值,相对应的。

    例如: map word_count; 则string是键的类型,int是值得类型,相对应的,而键是用作索引(下标)来使用。对键的要求是必须支持“<”操作,所以指针,地址,整数等都可以作为键。如list::iterator 类型的就不能作为键,因为它不支持小于操作。

    map也支持end,begin,iterator,insert,erase,等一系列容器的操作,注意:容器元素根据键的次序排列。其中迭代器解引用后是pair对(包括first,second的操作,其中first存放键,是const类型不能修改),这里说一下map定义的类型有三种:map::key_type(键的索引类型),map::mapped_type(值得索引类型),map::value_type(是pair对,包括first的键的类型和second的值得类型,是迭代器返回的类型)。

    map的下标访问,例如:

<span style="font-family:SimSun;">	map<string,int> word_count;
	word_count["A"] = 1;</span>

会发生将键为A对应的值赋值为1,但如果没有键A,则会插入这个键并赋值为1,同样查找时 int count=word_count["A"],也会在没有A时添加键,并把值初始化为零。为了避免这种情况,利用word_count.count("A")和word_count.find("A"),来查找元素A出现的次数。例如:

<span style="font-family:SimSun;">	int occurs = 0; 
	map<string, int> word_count;
	map<string,int>::iterator it = word_count.find("A");
	if (it != word_count.end()) //如果A不存在,则find函数返回end迭代器,所以这么判断。
		occurs = it->second;</span>

     注意:map的下标操作符返回mapped_type(即second的值),迭代器返回的是value_type,包括const key_type 和mapped_type.例如:记录每个单词出现的次数,

<span style="font-family:SimSun;">	string word;
	map<string, int> word_count;
	while (std::cin >> word)
	{
		++word_count[word];  //键word对应的值增加1.
	}</span>

      map的插入方法有多中,可以用总的来说都是用value_type的对来插入,包括insert一个make_pair类型,直接字面值或者map的迭代器。或则first,second单独来。map的遍历用迭代器,如上occurs的用法,然后迭代器自增。这些都和其他容器一样的,只是一定要注意是pair对。

4.set类型

      set只在乎这个集合里面是否有某个关键字,比如黑名单,只在乎是否在黑名单里面,不在乎次数。

     set类型与map类型相比,set只有键没有值,所以不是pair对,也没有mapped_type,键也是key_type类型,但是相同的键只能记一次。键也是const类型的。set与list的差别是,set的key不能修改。

     set的定义和使用和map差不多,只是注意没有与键对应的值。例如:

<span style="font-family:SimSun;">	vector<int> ivec(10);
	set<int> iset(ivec.begin(), ivec.end());</span>

插入操作和insert(key),删除erase(key)。 set没有下标操作,判断有无用成员函数 iset.find(22);和iset.count(22)。set的迭代器解引用返回的是

5 multimap和multiset

      这两个容器时一对多的容器,所以不支持下标操作。但是同一个key的是顺序排列的,添加还是insert,删除还是erase(删除键对应的多有的值), 在里面查找元素有三种:用成员函数count()计算对应多少个值和find()找到第一个值得迭代器,在联合同一个键key对应的值是顺序排列的来输出多个值。第二种用成员函数m.lower_bound(key)和m.upper_bound(key)直接返回首尾迭代器.第三种用m.equal_range(key)直接返回迭代器范围的pair对,first对应lower_bound,second对应upper_bound(指向末尾的下一个位置)。

6 容器的综合运动用(有时间一定要编写一下)

                本章小结

      关联容器的元素是按键排序的,支持高效的查找和读取,是根据键来访问的。

     map和multimap存储的是键-值对,他们在头文件utility中定义的标准pair类,对他两解引用得到pair类型的值,pair对象的first成员是一个const,而second成员是该键关联的值。set和multiset专门用来存储key.关联容器也可以用迭代器来访问。


第十二章 泛型算法

发现一篇博文写的很详细:http://www.cnblogs.com/ForFreeDom/archive/2012/05/08/2489689.html。





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值