C++ STL入门

  1、STL的三个基本组件:

  1)容器:以模板类的形式提供。

  (1)顺序容器:如vector。通过元素在容器中的位置顺序存储和访问元素。

  (2)关联容器:如map、set、multimap(同一个“键”可多次出现的map)和multiset(同一个“键”可多次出现的set)。通过“键”存储和读取元素。在迭代遍历关联容器时,可确保按“键”的“升序”访问元素。

  关联容器不提供front和back系列操作:包括front()、back()、push_front()、pop_front()、back()、push_back()和pop_back()等。

  关联容器不支持以下关于“容器大小”的操作,因为有了键之后,我们才能安排它的内部结构:无法通过容器大小来定义(如set<int> s(10);是错误的);不支持resize()。

  2)迭代器:提供了访问容器中元素的方法。

  3)算法:用来操作容器中元素的模板函数。

  注意,使用通用算法(如find()、max_element())时需要加上#include <algorithm>。

 

  2、容器

  1)vector:“动态数组”。

  (1)特性:

  I、容量(capacity)随着元素的增加而自动增长:0、1、2、4、8、16... ... 使用复制构造一个新vector时,如vector<int> vec2(vec1),vec2的size和capacity等于vec1的size,且其capactiy从复制后的初始值开始double。如复制后vec2.capacity()为3,则之后其容量按6、12、24... ...增长。

  每次double容量时,都会把原vector的元素复制到新vector的存储空间。通过平摊分析可知,插入n个元素的所有操作的总时间复杂度还是O(n)。

  double容量时,重新分配内存会导致原迭代器失效。

  II、使用pop_back()、erase()和clear()等可以改变vector的size,但不能减少其capacity

  若想在vector作用域结束前回收其内存,可以使用swap():vector<int>().swap(vec)。

  (2)常用操作:

  I、push_back():在vector末尾插入元素。注意vec[900] = 900;这样的形式不仅是不安全的,也不会改变size。

  II、erase(iter):删除iter指向的元素,并将其后的元素前移。如13、15、91、30、19,iter指向91,则erase()之后变成13、15、30、19、19,size减1。

  III、remove_if(v.begin(), v.end(), Odd());:将不删除的元素复制到v开头。如v的元素有1~7,删除奇数后,v的元素变成2、4、6、4、5、6、7。remove_if()返回指向“v的最后一个有效元素之后的元素”(在这里是第二个4)的迭代器。

  IV、reserve():预留指定大小的容量。对于需要执行大量push_back()的vector来说,提前使用reserve()可以减少double容量时的复制操作。reserve()改变的是capacity,而size并没有改变。

  V、front():返回第一个元素。

  VI、back():返回最后一个元素(v1.at(v1.size() - 1))。

 

  2)map:通常可理解为(键值)关联数组(使用“键”作为下标来获取“值”)。使用红黑树实现。

  (1)应用:如字典,单词本身是“键”,而它的解释说明则是“值”。

  (2)特性:

  I、“键”是唯一的,且不可修改。将自定义类型作为“键”时,需要重载"<"运算符,以定义“键”的排序规则

class key
{
public: key(int i) : i(i) {} bool operator<(const key &k) const { return i < k.i; } private: int i; };

  II、将自定义类型作为“值”时,该类型可能需要有默认构造函数。详见下文关于“下标运算符”的内容。

  (3)常用操作:

  I、插入:

  在说明我们的例子之前,先来熟悉一个标准库类型——pair类型。它也是一种模板类型,包含两个“成员”。可以通过以下方法创建和访问pair对象:

pair<int, string> p1(1, string("hello"));

// make_pair(v1, v2):以v1和v2值创建pair对象,其元素类型分别是v1和v2的类型

pair<int, string> p2 = make_pair(2, string("world"));
// first和second是pair的(公有)数据成员,可直接读写 cout << p1.first << "\t" << p1.second << endl;

  再来了解一下map定义的类型:

  map<K, V>::key_type:用作索引的“键”的类型(此处为K)。

  map<K, V>::mapped_type:“键”所关联的“值”的类型(此处为V)。

  map<K, V>::value_type(map元素的类型):实际上是pair类型,其first元素是const map<K, V>::key_type类型,second元素是map<K, V>::mapped_type类型。value_type是pair<const K, V>类型的同义词。

  关于“插入”操作的例子:

map<int, string> m;

// 该版本的insert()的实参可以是:value_type对象、pair对象或make_pair()的返回值
// 返回值:pair类型,包含一个迭代器和一个bool值(表示是否成功插入了元素)。在这个例子中是pair<map<int, string>::iterator, bool>
m.insert(map<int, string>::value_type(2, "world")); m.insert(pair<const int, string>(1, "hello")); m.insert(make_pair(3, "hello world"));

   II、查找:

// 下标操作符危险的副作用:查找的“键”(key)对应的元素不存在时,会插入一个新元素
// 新元素的“键”为key,“值”使用0或默认构造函数来初始化
// 另一个副作用:不必要的初始化(如m[123] = 4;会先初始化,再赋值) cout << m[123] << endl; // 对于map来说,只能返回0或1 int count = m.count(456); map<int, string>::iterator iter = m.find(456); if (iter != m.end()) { cout << iter -> first << ": " << iter -> second << endl; }

  III、删除:erase()

  IV、遍历:

// map迭代器进行解引用将得到value_type类型的对象

map<int, double>::value_type vt = *iter;

 

  3)set:map容器是键值对的集合,而set容器只是单纯的“键”的集合(没有定义mapped_type类型;value_type是与key_type相同的类型)。

  (1)特性:

  I、set不支持下标操作符。

  II、与map一样,set容器存储的键必须唯一,而且不能修改。

  (2)常用操作:

  I、插入

// 与map类似,只带一个参数的insert()返回pair类型对象,包含一个迭代器和一个bool值,迭代器指向拥有该键的元素,而bool值表明是否添加了元素

pair<set<int>::iterator, bool> p = iset.insert(111);

cout << *(p.first) << "\t" << p.second << endl;



// 使用迭代器对作为参数的insert()返回void类型

iset.insert(ivec.begin(), ivec.end());

  II、查找:

// find()返回迭代器

set<int>::iterator iter = iset.find(7);



// count()返回0或1

cout << iset.count(8) << endl;

  III、集合操作(差集、并集、交集等)

// sa的元素:2, 4, 7, 9, 10

// sb的元素:3, 4, 8, 10, 12

set<int> sa, sb, sc, sd, se;

// 差集。输出:2,7,9,

set_difference(sa.begin(), sa.end(), sb.begin(), sb.end(), inserter(sc, sc.begin()));

copy(sc.begin(), sc.end(), ostream_iterator<int>(cout, ","));

cout << endl;



// 交集。输出:4,10,

set_intersection(sa.begin(), sa.end(), sb.begin(), sb.end(), inserter(sd, sd.begin()));

copy(sd.begin(), sd.end(), ostream_iterator<int>(cout, ","));

cout << endl;



// 并集。输出:2,3,4,7,8,9,10,12,

set_union(sa.begin(), sa.end(), sb.begin(), sb.end(), inserter(se, se.begin()));

copy(se.begin(), se.end(), ostream_iterator<int>(cout, ","));

cout << endl;

 

  4)list:双向循环链表。

  (1)特性:

  I、大致结构(括号中的数字是元素的值):

  

  以下是验证代码:

// 输出:2, 6, 4, 3, -1074611216, 2, 6, 4, 

list<int>::iterator iter = l.begin();

for (int i = 0; i < 8; i++)

{

    cout << *(iter++) << "\t";

}

cout << endl;



// 输出:3, 4, 6, 2, -1074611216, 3, 4, 6, 

list<int>::reverse_iterator riter = l.rbegin(); for (int i = 0; i < 8; i++)

{

    cout << *(riter++) << "\t";

}

  (2)常用操作:

  I、插入:push_back()、push_front()、pop_back()、pop_front()

  II、查看:front()、back()

  III、排序:sort()

  IV、去重(在表有序的前提下):unique()。通过Binary Predicate(二元谓词)可以自定义“两个元素在什么情况下相等”:

// 二元谓词。可以用函数或类(匿名对象)实现

bool is_equal(double first, double second)

{

        return (int(first) == int(second));

}



class Equality

{

public:

        bool operator() (double first, double second)

        {

                return (fabs(first - second) < 5.0);

        }

};

int main () { double darray[]={12.15, 2.72, 73.0, 12.77, 3.14, 12.77, 73.35, 72.25, 15.3, 72.25}; list<double> my_list(darray, darray + 10); // 2.72, 3.14, 12.15, 12.77, 12.77, 15.3, 72.25, 72.25, 73.0, 73.35 my_list.sort(); // 2.72, 3.14, 12.15, 12.77, 15.3, 72.25, 73.0, 73.35 my_list.unique(); // 2.72, 3.14, 12.15, 15.3, 72.25, 73.0 my_list.unique(is_equal); // 2.72, 12.15, 72.25 my_list.unique(Equality()); return 0; }

  V、逆转链表:reverse()

  VI、合并:merge()。如l1.merge(l2);,合并有序的l1和l2,并使合并结果按指定方式排序。合并结果存放到l1,而l2变为空。

  VII、删除满足条件的元素:l1.remove_if(Odd());

// 定义了什么是奇数

class Odd

{

public:

    bool operator()(int i) { return i % 2 == 1; }

};

 

  3、算法

  1)查找最大值:iter = max_element(l1.begin(), l1.end());

  2)统计等于特定值的元素的个数:count(l1.begin(), l1.end(), 12)

  3)统计满足特定条件的元素的个数:count_if(l1.begin(), l1.end(), Odd())

  4)查找满足特定条件的元素:iter = find_if(l2.begin(), l2.end(), Odd());

  5)排序:sort(vec.begin(), vec.end(), compare);是不稳定排序,stable_sort(vec.begin(), vec.end(), compare);是稳定排序

 

  参考资料:

  《C++ Primer》

  http://www.cplusplus.com/reference/list/list/unique/

 

 

不断学习中。。。

你可能感兴趣的:(C++)