代码改变世界

stl具体容器、算法 (摘)

2013-09-09 15:48  playerboy  阅读(316)  评论(0编辑  收藏  举报

1、所有容器都提供了一个默认的构造函数,一个拷贝构造函数。

例如:

list<int> l;

....

vector<int> ivector(l.begin(),l.end());

 

int array[]={1,2,3,4};

....

set<int> iset(array,array+sizeof(array)/sizeof(array[0]));

 

2、与大小相关的函数

size(),empty(),max_size()

3、返回迭代器的函数

begin(),end(),rbegin(),rend()

4、比较操作

==,!=,<,>,>=....

 

Vector详解:

capacity(),返回vector能够容纳的元素个数。

size(),返回vector内现有元素的个数。

赋值操作:

c1=c2; 把c2的全部元素指派给c1

c.assign(n,elem);复制n个elem,指派给c

c.assign(beg,end);将区间beg,end内的元素指派给c

c1.swap(c2);将c1,c2元素互换

swap(c1,c2);同上

元素存取

c.at(index);

c[index];

c.front();返回第一个元素

c.back();

 

插入和删除:

c.insert(pos.elem);

c.insert(pos,n.elem); 插入n个elem

c.insert(pos,beg,end); 在pos出插入beg,end区间内的所有元素。

c.push_back(elem);

c.pop_back();

c.erase(pos); 删除pos上的元素,返回下一个元素

c.erase(beg,end);

c.resize(num);将元素数量改为num,如果size变大了,多出来的新元素都要一default方式构建。

c.resize(num,elem);将元素数量改为num,如果size变大了,多出来的新元素是elem的副本。

c.clear();删除所有。

 

vector的reserve和resize

reserve只分配空间,而不创建对象,size()不变。而resize分配空间而且用空对象填充.

reserve是容器预留空间,但并不真正创建元素对象,在创建对象之前,不能引用容器内的元素,因此当加入新的元素时,需要用push_back()/insert()函数。

resize是改变容器的大小,并且创建对象,因此,调用这个函数之后,就可以引用容器内的对象了,因此当加入新的元素时,用operator[]操作符,或者用迭代器来引用元素对象。

再者,两个函数的形式是有区别的,reserve函数之后一个参数,即需要预留的容器的空间;resize函数可以有两个参数,第一个参数是容器新的大小,第二个参数是要加入容器中的新元素,如果这个参数被省略,那么就调用元素对象的默认构造函数。

vector有而deque无的:capacity(), reserve();

deque有而vector无的:push_front(elem), pop_front(); push_back(elem), pop_back();

STL提供的另两种容器queue、stack,其实都只不过是一种adaptor,它们简单地修饰deque的界面而成为另外的容器类型

 

List详解:

for_each  (.begin(), .end(), “函数”);

count (.begin(), .end(), 100, jishuqi);

返回对象等于100的个数jishuqi值。

count_if() 带一个函数对象的参数(上面“100”的这个参数)。函数对象是一个至少带有一个operator()方法的类。这个类可以更复杂。

find(*.begin().*end(),“要找的东西”);

如果没有找到指出的对象,就会返回*.end()的值,要是找到了就返回一个指着找到的对象的iterator

fine_if();与count_if()类似,是find的更强大版本。

STL通用算法search()用来搜索一个容器,但是是搜索一个元素串,不象find()和find_if() 只搜索单个的元素。

search算法在一个序列中找另一个序列的第一次出现的位置。

search(A.begin(), A.end(), B.begin(), B.end());

A中找B这个序列的第一次出现。

要排序一个list,我们要用list的成员函数sort(),而不是通用算法sort()。

list容器有它自己的sort算法,这是因为通用算法仅能为那些提供随机存取里面元素 的容器排序。

list的成员函数push_front()和push_back()分别把元素加入到list的前面和后面。你可以使用insert() 把对象插入到list中的任何地方。

insert()可以加入一个对象,一个对象的若干份拷贝,或者一个范围以内的对象。

list成员函数pop_front()删掉list中的第一个元素,pop_back()删掉最后一个元素。函数erase()删掉由一个iterator指出的元素。还有另一个erase()函数可以删掉一个范围的元素。

list的成员函数remove()用来从list中删除元素。

*.remove("要删除的对象");

通用算法remove()使用和list的成员函数不同的方式工作。一般情况下不改变容器的大小。

remove(*.begin(),*.end(),"要删除的对象");

使用STL通用算法stable_partition()和list成员函数splice()来划分一个list。

stable_partition()是一个有趣的函数。它重新排列元素,使得满足指定条件的元素排在不满足条件的元素前面。它维持着两组元素的顺序关系。

splice 把另一个list中的元素结合到一个list中。它从源list中删除元素。

 

Set Map详解:

STL map和set的使用虽不复杂,但也有一些不易理解的地方,如:

为何map和set的插入删除效率比用其他序列容器高?

为何每次insert之后,以前保存的iterator不会失效?

为何map和set不能像vector一样有个reserve函数来预分配数据?

当数据元素增多时(10000到20000个比较),map和set的插入和搜索速度变化如何?

C++ STL中标准关联容器set, multiset, map, multimap内部采用的就是一种非常高效的平衡检索二叉树:红黑树,也成为RB树(Red-Black Tree)。RB树的统计性能要好于一般的平衡二叉树(AVL-树).

为何map和set的插入删除效率比用其他序列容器高?

大部分人说,很简单,因为对于关联容器来说,不需要做内存拷贝和内存移动。说对了,确实如此。map和set容器内所有元素都是以节点的方式来存储,其节点结构和链表差不多,指向父节点和子节点。这里的一切操作就是指针换来换去,和内存移动没有关系。

为何每次insert之后,以前保存的iterator不会失效?(同解)

为何map和set不能像vector一样有个reserve函数来预分配数据?

究其原理来说时,引起它的原因在于在map和set内部存储的已经不是元素本身了,而是包含元素的节点。

其实你就记住一点,在map和set内面的分配器已经发生了变化,reserve方法你就不要奢望了。

当数据元素增多时(10000和20000个比较),map和set的插入和搜索速度变化如何?

如果你知道log2的关系你应该就彻底了解这个答案。在map和set中查找是使用二分查找,也就是说,如果有16个元素,最多需要比较4次就能找到结果,有32个元素,最多比较5次。那么有10000个呢?最多比较的次数为log10000,最多为14次,如果是20000个元素呢?最多不过15次。

 

泛型算法:

所有算法的前两个参数都是一对iterators:[first,last),用来指出容器内一个范围内的元素。

每个算法的声明中,都表现出它所需要的最低层次的iterator类型。

 

常用算法:

accumulate() 元素累加

adjacent_difference() 相邻元素的差额

adjacent_find() 搜寻相邻的重复元素

binary_search() 二元搜寻

copy() 复制

copy_backward() 逆向复制

count() 计数

count_if() 在特定条件下计数

equal() 判断相等与否

equal_range() 判断相等与否(传回一个上下限区间范围)

fill() 改填元素值

fill_n() 改填元素值,n 次

find() 搜寻

find_if() 在特定条件下搜寻

find_end() 搜寻某个子序列的最后一次出现地点

find_first_of() 搜寻某些元素的首次出现地点

for_each() 对范围内的每一个元素施行某动作

generate() 以指定动作的运算结果充填特定范围内的元素

generate_n() 以指定动作的运算结果充填 n 个元素内容

includes() 涵盖於

inner_product() 内积

inplace_merge() 合并并取代(覆写)

iter_swap() 元素互换

lexicographical_compare() 以字典排列方式做比较

lower_bound() 下限

max() 最大值

max_element() 最大值所在位置

min() 最小值

min_element() 最小值所在位置

merge() 合并两个序列

mismatch() 找出不吻合点

next_permutation() 获得下一个排列组合

泛型演算法(Generic Algorithms)与 Function Obje4 cts

nth_element() 重新安排序列中第n个元素的左右两端

partial_sort() 局部排序

partial_sort_copy() 局部排序并复制到它处

partial_sum() 局部总和

partition() 切割

prev_permutation() 获得前一个排列组合

random_shuffle() 随机重排

remove() 移除某种元素(但不删除)

remove_copy() 移除某种元素并将结果复制到另一个 container

remove_if() 有条件地移除某种元素

remove_copy_if() 有条件地移除某种元素并将结果复制到另一个 container

replace() 取代某种元素

replace_copy() 取代某种元素,并将结果复制到另一个 container

replace_if() 有条件地取代

replace_copy_if() 有条件地取代,并将结果复制到另一个 container

reverse() 颠倒元素次序

reverse_copy() 颠倒元素次序并将结果复制到另一个 container

rotate() 旋转

rotate_copy() 旋转,并将结果复制到另一个 container

search() 搜寻某个子序列

search_n() 搜寻「连续发生 n 次」的子序列

set_difference() 差集

set_intersection() 交集

set_symmetric_difference() 对称差集

set_union() 联集

sort() 排序

stable_partition() 切割并保持元素相对次序

stable_sort() 排序并保持等值元素的相对次序

swap() 置换(对调)

swap_range() 置换(指定范围)

transform() 以两个序列为基础,交互作用产生第三个序列

unique() 将重复的元素摺叠缩编,使成唯一

unique_copy() 将重复的元素摺叠缩编,使成唯一,并复制到他处

upper_bound() 上限

 

 

四、注意细节:

1auto_ptr 不能用new[]所生成的array作为初值,因为释放内存时用的是delete,而不是delete[]

2、就搜寻速度而言,hash table通常比二叉树还要快5~10倍。hash table不是C++标准程序库的一员。

3、迭代器使用过程中优先选用前置式递增操作符(++iter)而不是选择后置式递增操作符(iter++)。

3、迭代器三个辅助函数:advance(),distance(),iter_swap()

       advance()可令迭代器前进

       distance()可处理迭代器之间的距离。

       iter_swap()可交换两个迭代器所指内容。

4hasp函数 makeheap()push_heap()pop_heap()sort_heap()

5’/0’string之中并不具有特殊意义,但是在一般C形式的string中却用来标记字符串结束。在string中,字符 ‘/0’和其他字符的地位完全相同。string中有三个函数可以将字符串内容转换成字符数组或C形式的string

data()     以字符数组的形式返回字符串内容。但末未追加’/0’字符,返回类型并非有效的C形式string

c_str()    C形式返回字符串内容(在末尾端添加’/0’字符)。

copy()    将字符串内容复制到“调用者提供的字符数组”中,不添加’/0’字符。

6容器中用empty来代替检查size是否为0;当使用new得到指针的容器时,切记在容器销毁前delete那些指针;千万不要把auto_ptr放入容器中。

7、尽量使用vectorstring来代替动态申请的数组;避免使用vector<bool>vector<bool>有两个问题.第一,它不是一个真正STL容器,第二,它并不保存bool类型。

8、迭代器使用过程中,尽量使用iterator代替const_iteratorreverse_iteratorconst_reverse_iterator;使用distanceadvanceconst_iterators转化成iterators

typedef deque<int> IntDeque;  // 和以前一样

typedef IntDeque::iterator Iter;

typedef IntDeque::const_iterator ConstIter;

IntDeque  d;

ConstIter ci;

...     // 让ci指向d

Iter i(d.begin());    // 初始化i为d.begin()

advance(i, distance(i, ci));  // 调整i,指向ci位置

9、避免对setmultiset的键值进行修改。

10永远让比较函数对相同元素返回false。

11、排序选择:

1)如果你需要在vector、string、deque或数组上进行完全排序,你可以使用sort或stable_sort。

2)如果你有一个vector、string、deque或数组,你只需要排序前n个元素,应该用partial_sort。

3)如果你有一个vector、string、deque或数组,你需要鉴别出第n个元素或你需要鉴别出最前的n个元素,而不用知道它们的顺序,nth_element是你应该注意和调用的。

4)如果你需要把标准序列容器的元素或数组分隔为满足和不满足某个标准,你大概就要找partition或stable_partition。

5)如果你的数据是在list中,你可以直接使用partitionstable_partition,你可以使用listsort来代替sortstable_sort。如果你需要partial_sortnth_element提供的效果,你就必须间接完成这个任务。
12如果你真的想删除东西的话就在类似remove的算法后接上eraseremove从一个容器中remove元素不会改变容器中元素的个数,erase是真正删除东西。
13
提防在指针的容器上使用类似remove的算法,在调用类似remove的算法前手动删除和废弃指针。
14尽量用成员函数代替同名的算法,有些容器拥有和STL算法同名的成员函数。关联容器提供了countfindlower_boundupper_boundequal_range,而list提供了removeremove_ifuniquesortmergereverse。大多数情况下,你应该用成员函数代替算法。这样做有两个理由。首先,成员函数更快。其次,比起算法来,它们与容器结合得更好(尤其是关联容器)。那是因为同名的算法和成员函数通常并不是是一样的。
15
、容器中使用自定义的结构体时,如果用到拷贝与赋值,结构体需要重载operator=符号;比较容器分成相等与不等,相等时重载operator==符号,不等时重载operator<符号。比如setmapmultisetmultimappriority_queue等容器类要求重载operator<符号。
16
Map/Multimap,Sets/Multisets都不能用push_back,push_front,因为它是自动排序的。

Set内的相同数值的元素只能出现一次,Multisets内可包含多个数值相同的元素。

Map内的相同数值的元素只能出现一次,Multimap内可包含多个数值相同的元素。内部由二叉树实现,便于查找。

17、string 与 数字之间的转换,转换的方法有很多种,一般使用stringstream来实现转换。比如:

#include  <iostream>

#include  <sstream>  

#include  <string>  

using   namespace   std;  

int   main()  

{  

  int   i=0;  

  string   temp;    

  stringstream   s;  

  //string转换为数字

  temp = “1234”; 

  s<<temp;  

  s>>i;  

  cout<<i<<endl;  

 

 //数字转换为string

 i=256;

 s<<i;

 temp = s.str();

 cout<<temp<<end;

 

 system("pause");  

 return   0;

}

 

18、对于自定义的结构体,放入容器中,最好不要对容器进行内存初始化(不要调用memset,zeromemory函数),否则如果结构体中有指针类型的变量时,就会出现问题。

 

19Vector的函数泄漏问题

定义了一个

struct temp

{

     char name[256];

     int i;

}

Vector<temp> vect;

当对这个vect执行pushback一些temp的结构体后,执行clear这样是否会内存泄露?可以释放掉temp结构体中的name内存吗?

解决方法:

不行,clear只是把那些元素全部删除掉,并不是释放内存。再者,你这样的定义容器是不需要释放内存的,如果你这样定义,std::vector <temp> *pVec。就需要了。先pVec->clear() pVec->swap( (std::vector <temp>)(*pVec))。就能实现内存的释放。  

 

20、stl之map erase方法的正确使用
STL的map表里有一个erase方法用来从一个map中删除掉指令的一个节点,不存在任何问题。
如果删除多一个节点时,需要使用正确的调用方法。比如下面的方法是有问题:
for(ITER iter=mapTest.begin();iter!=mapTest.end();++iter)
{
cout<<iter->first<<":"<<iter->second<<endl;
mapTest.erase(iter);
}
这是一种错误的写法,会导致程序行为不可知.究其原因是map 是关联容器,对于关联容器来说,如果某一个元素已经被删除,那么其对应的迭代器就失效了,不应该再被使用;否则会导致程序无定义的行为。

正确的使用方法:
1).使用删除之前的迭代器定位下一个元素。STL建议的使用方式
for(ITER iter=mapTest.begin();iter!=mapTest.end();)
{
cout<<iter->first<<":"<<iter->second<<endl;
mapTest.erase(iter++);
}

或者
for(ITER iter=mapTest.begin();iter!=mapTest.end();)
{
ITER iterTmp = iter;
iter++;
cout<<iterTmp->first<<":"<<iterTmp->second<<endl;
mapTest.erase(iterTmp);
}

2). erase() 成员函数返回下一个元素的迭代器
for(ITER iter=mapTest.begin();iter!=mapTest.end();)
{
cout<<iter->first<<":"<<iter->second<<endl;
iter=mapTest.erase(iter);
}

 

摘自:http://blog.sina.com.cn/s/blog_861b2c32010160uk.html