Bootstrap

C++ STL: Container

  • reference - http://www.cplusplus.com/reference/stl/

  • 在数据存储上,有一种对象类型,它可以持有其它对象或指向其它对像的指针,这种对象类型就叫做容器。简单来说,就是保存其它对象的对象.

  • 容器类自动申请和释放内存,因此无需new和delete操作。

一、序列式容器 Sequence containers

  • 各元素之间有顺序关系的线性表,是一种线性结构的可序群集。

  • 顺序性容器中的每个元素均有固定的位置,除非用删除或插入的操作改变这个位置。

  • 顺序容器的元素排列次序与元素值无关,而是由元素添加到容器里的次序决定。

1.1 array

定义
// generic template
template < class T, size_t N > class array; 
//初始化
array<int, 4> = {1, 2, 3, 4};
//访问
operator[]/at()/front()/back()/data()
//操作
fill()/swap()
特性
  • 严格线性,连续存储空间,大小固定,位于栈上,不需要allocator对空间进行管理。

  • 独特功能:重载了get()函数,可以被视为tuple使用。

比较
  • 与数组比较,仅仅增加了成员变量和全局函数,访问跟数组一样高效。

  • 与其他容器类比较,array对于swap的操作需要遍历所有元素,代价比较高。

1.2 vector

定义
template < class T, class Alloc = allocator<T> > class vector;
特性
  • 严格线性,连续存储,动态数组,allocator管理。

  • 猜测:栈如果不是用new,存在栈上,然后栈保存所存数据的地址,数据保存在堆中。

  • vector的swap()交换的是存储数据的指针(数据地址),属于浅拷贝。

但是在插入/删除元素时,时间复杂度与**num(操作元素)+num(操作位置后所有元素)**线性相关。

当元素数量超过空间,需要重新分配空间,时间复杂度与新空间大小线性相关。

一般来说,vector容器实际的容量会大于包含其元素所需的空间,来适应可能的增长,并保证reallocation以对数增长的大小间隔发生。

比较
  • 与array相比,vector空间大小灵活,但需要消耗更多内存换取管理存储和有效的动态增长。

  • 与deques,forward_lists,list相比,vector可以像数组一样高效地访问其元素并且相对有效地从其末尾添加或删除元素。但是,对于涉及除末尾以外的位置插入或删除元素的操作,它的性能比其他位置差。

操作
//初始化
vector<int> vec1;         //默认初始化,vec1为空
vector<int> vec2(vec1);  //使用vec1初始化vec2
vector<int> vec3(vec1.begin(),vec1.end());//使用vec1初始化vec2
vector<int> vec4(10);    //10个值为0的元素
vector<string> vec5(10,"null");    //10个值为null的元素

//操作
vec1.size();                    //元素个数
vec1.empty();                   //判断是否为空
vec1.clear();                   //清空元素
vec1.push_back(100);            //添加元素
vec1.pop_back();                //删除末尾元素
vec1.insert(vec1.end(),5,3);    //在vec1.end()前面插入5个元素
vec1.erase(vec1.begin(),vec1.end());//删除后元素前移,不包括end()
vector<int>::iterator iter = vec1.begin();    //迭代器首地址
operator[]、at、front、back、data //access data

1.3 deque

定义
// generic template
template < class T, class Alloc = allocator<T> > class deque;
特性
  • 严格线性,动态数组,使用allocator管理

  • 双端队列,可以实现在begin和end的快速添加、删除操作

  • 不是连续存储(支持迭代器的++itr,但是不支持指针偏移),需要多个连续内存块,并在一个映射结构中保存对这些块和顺序的跟踪。

  • 如果在序列的开始或结尾进行插入操作,所有迭代器失效,但是指针和引用不变;如果在deque其他位置,所有迭代器、指针、引用失效。

  • 如果在序列的开始或结尾进行删除操作,删除的迭代器、指针、引用失效(若最后一个元素也删了,end itrator也失效);如果在其它位置,所有迭代器、指针、引用失效。

比较
  • 与vector相比,deque仍支持直接访问,但是随机访问和遍历性能都比vector差(多了映射结构)。不过因为映射结构和本身的不连续存储,它不需要reallocations。

  • 与forward_list和list相比,deque在非两端位置的频繁插入、删除操作的效率要低很多(没有连续的迭代器和引用)。

操作
//初始化
deque<int> first;  // empty deque of ints
deque<int> second (4,100); // 4 ints with value 100
deque<int> third (second.begin(),second.end()); 
deque<int> fourth (third); // a copy of third

int myints[] = {16,2,77,29};
deque<int> fifth (myints, myints + sizeof(myints) / sizeof(int) );//copy arrays

//赋值
deque<int> first;
first.assign (7,100);// 7 ints with a value of 100

deque<int> second;
deque<int>::iterator it;
it=first.begin()+1;
second.assign (it,first.end()-1); //first的5个赋值

deque<int> third;
int myints[] = {1776,7,4};
third.assign (myints,myints+3);   //数组赋值

//操作
//时间复杂度: O(1)
push_back()/push_front()
pop_back()/pop_front()
swap() //所有迭代器、指针、引用依旧有效

//时间复杂度:O(操作元素数量)
insert()/erase()
clear()

1.4 forward_list

定义
template < class T, class Alloc = allocator<T> > class forward_list;
特性
  • 严格线性,链表实现,allocator动态维护

  • 支持任意位置常数时间的插入、删除操作

  • 为了保证高效,forward_list特意去掉size变量,避免额外的空间,也避免在插入、删除是对size的维护。

比较
  • 与list相比,forward_list只有一个next指针,list有双向指针。在单向的插入、删除时,forward_list更高效。

  • 主要的缺点:无法直接访问元素,要额外空间保存链接信息

操作
//初始化
forward_list<int> first;    //default: empty
forward_list<int> second (3,77);  //fill: 3 seventy-sevens
forward_list<int> third (second.begin(), second.end()); //range initialization
forward_list<int> fourth (third); //copy constructor
forward_list<int> fifth (std::move(fourth));  //move ctor. (fourth wasted)
forward_list<int> sixth = {3, 52, 25, 90};   // initializer_list constructor

//front & after
mylist.push_front (100); //在第一个元素前插入
mylist.pop_front();//删除第一个元素
mylist.emplace_front(10, 'a');//构造(10,a),插入第一个元素前
it = mylist.emplace_after (pos, args...);//返回新元素第一个迭代器
it = mylist.insert_after (pos, val);//返回最后插入元素的迭代器
it = mylist.erase_after(it); //删除操作,返回it后面的迭代器
list1.splice_after(pos, list2); //将list2迁移到list1的pos位置后
list1.splice_after (pos, list2, first, last);//将(first,last)节点插到list1
                                             //不包括first和last,list2也被修改

//删除操作
remove(20);//删除list中所有值为20的元素,O(n)

//条件删除操作
// a binary predicate implemented as a function:
bool single_digit (const int& value) { return (value<10); }

// a binary predicate implemented as a class:
class is_odd_class{
public:
  bool operator() (const int& value) {return (value%2)==1; }
} 

forward_list<int> mylist = {7, 80, 7, 15, 85, 52, 6};
mylist.remove_if (single_digit);      // 80 15 85 52
mylist.remove_if (is_odd_object);     // 80 52

//删除重复操作(适用于sorted list)
// a binary predicate implemented as a function:
bool same_integral_part (double first, double second)
{ return ( int(first)==int(second) ); }

// a binary predicate implemented as a class:
class is_near_class{
public:
  bool operator() (double first, double second)
  { return (fabs(first-second)< 1.0); }
} is_near_object;

forward_list<double> mylist = { 2.5, 4.5, 3.0, 2.1, 4.5, 6.2};
mylist.sort();  //2.1 2.5 3.0 4.5 4.5 6.2
mylist.unique(); //2.1 2.5 3.0 4.5 6.2
mylist.unique (same_integral_part);  //2.1 3.0 4.5 6.2
mylist.unique (is_near_object);      //2.1 4.5 

//排序 O(NlogN)
mylist.sort();
mylist.sort(greater<int>());

//逆转 O(N)
mylist.reverse();

//合并链表,O(M + N - 1)注意first和second合并前已排序
//合并后second为空,合并期间没有拷贝、构造操作
first.merge (second); //merge(second,greater<double>())

1.5 list

定义
template < class T, class Alloc = allocator<T> > class list;
特性
  • 双向链表,可以任意在特定元素前面或者后面插入、删除。

  • 保留了size变量

比较

参考forward_list

操作
emplace_front/push_front/pop_front
emplace_back/push_back/pop_back
emplace(pos, args...);//构造元素,在pos插入
insert(pos, val); //在pos前插入val的元素
erase(itr1, itr2); //删除[itr1, itr2)的元素
swap();//利用浅拷贝进行交换
splice //同forward_list

二、容器适配器 Container Adaptors

  • 容器适配器是容器的接口,让一种已存在的容器类型采用另一种不同的抽象类型的工作方式实现。

  • C++提供了三种容器适配器:stack和queue基于deque实现,priority_queue基于vector实现。

虽然我们可以直接在deque上按照stack的方式工作,但它的功能远超过stack操作所需的各种接口,不免有些大材小用,而且容易用错函数,比如把push_back()写成push_front()。所以,我们需要适配器来做一下转换,只保留基础类型提供的接口中的一部分。

适配器本身不能直接保存元素,而是通过调用顺序容器去实现,即理解为“它保存一个容器,这个容器再保存所有元素”。

2.1 stack

定义
template <class T, class Container = deque<T> > class stack;
特性
  • 保证container的成员函数有empty、size、back、push_back、pop_back,这些vector、deque(默认)、list都可以满足。
操作
stack<int> s;
stack< int, vector<int> > stk(myvec);  //使用vector实现stk
s.empty();  //判断stack是否为空,为空返回true,否则返回false
s.size();   //返回stack中元素的个数
s.top();    //返回栈顶元素的值,但不删除此元素
s.push(item);   //在栈顶压入新元素item
s.pop();    //删除栈顶元素,但不返回其值
swap(); //交换,o(1)

2.2 queue

特性
  • 保证container的成员函数有empty、size、front、back、push_back、pop_front,这些deque(默认)、list都可以满足。
queue<int> q; 
queue<int, list<int> > fourth (mylist);

push(item);   //在队尾压入一个新元素
pop(); //删除队首元素
emplace();//构造,并在队尾压入新元素
swap();//交换,o(1)
//queue only:
front();  //返回队首元素的值,但不删除该元素
back();   //返回队尾元素的值,但不删除该元素

2.3 priority_queue

定义
template <class T, class Container = vector<T>,
  class Compare = less<typename Container::value_type> > class priority_queue;
特性
  • 优先队列具有队列的所有特性,包括基本操作,只是在这基础上添加了内部的一个排序,它本质是一个堆实现的。

自定义类型的优先队列
#include<queue>
#include<vector>
#include<iostream>
using namespace std;
 
struct node
{
    int x, y;
    node(int x,int y):x(x),y(y){}
};
 
struct cmp
{
    bool operator()(node a,node b)
    {
        if(a.x == b.x)  return a.y >= b.y;
        else return a.x > b.x;
    }
};
 
int main()
{
    priority_queue<node, vector<node>, cmp> pq;
    for(int i = 1; i <= 5; i++)
        for(int j = 1; j <= 5; j++)
            pq.push(node(i,j));
    while(!pq.empty())
    {
        cout << pq.top().x << " " << pq.top().y << endl;
        pq.pop();
    }
    return 0;
}

三、关联容器 Associative containers

  • 非线性的树结构(binary search tree),元素在容器中并没有保存元素置入容器时的逻辑顺序。

  • 迭代器能根据元素的特点“顺序”获取元素。元素是有序的集合,默认在插入的时候按升序排列。

  • 有序但是访问速度比无序的慢

3.1 set

定义
template < class T, //key_type/value_type
           class Compare = less<T>,  //key/value_compare
           class Alloc = allocator<T> //allocator_type
           > class set;
特性
  • 按compare逻辑存储unique元素
  • 可以插入或者删除元素,但是不能修改元素
  • 比unordered_set性能差,但允许direct iteration

3.2 multiset

定义
template < class T, //key_type/value_type
           class Compare = less<T>, //key/value_compare
           class Alloc = allocator<T> >//allocator_type
           > class multiset;
特性
  • 按compare逻辑存储元素,元素值可重复
  • 可以插入或删除元素,但是不能修改元素
  • 比unordered_multiset性能差,但允许direct iteration

3.3 map

定义
template < class Key,   //key_type
           class T,     //mapped_type
           class Compare = less<Key>,//func pointer or object
           class Alloc = allocator<pair<const Key,T> >//allocator_type
           > class map;
特性
  • 通过compare逻辑对key进行排序,没有重复的key
  • 可以插入或删除元素,也可以修改元素

3.4 multimap

定义
template < class Key,   //key_type
           class T,     //mapped_type
           class Compare = less<Key>, //key_compare
           class Alloc = allocator<pair<const Key,T> > //allocator_type
           > class multimap;
特性
  • 通过compare逻辑对key进行排序,有重复的key
  • 可以插入或删除元素,也可以修改元素

四、无序关联容器 Unordered Associative containers

  • 通过hash进行存储,无序但是访问速度比有序快

4.1 unordered_set

定义
template < class Key, //key_type/value_type
           class Hash = hash<Key>,  //hasher
           class Pred = equal_to<Key>, //key_equal
           class Alloc = allocator<Key> //allocator_type
           > class unordered_set;
特性
  • 无序存储unique元素,支持快速检索
  • 可以插入或删除元素,但是不能修改元素

4.2 unordered_multiset

定义
template < class Key, //key_type/value_type
           class Hash = hash<Key>,  //hasher
           class Pred = equal_to<Key>,  //key_equal
           class Alloc = allocator<Key> //allocator_type
           > class unordered_multiset;
特性
  • 元素既是key又是value,不可以被修改
  • 允许不同元素可以包含相同的值,相同的值被hasher分到一个bucket

4.3 unordered_map

定义
template < class Key, //key_type
           class T, //mapped_type
           class Hash = hash<Key>,  //hasher
           class Pred = equal_to<Key>,  //key_equal
           class Alloc = allocator< pair<const Key,T> >           > class unordered_map;
特性
  • 无序,没有重复的key
  • unordered_map比map的访问速度快,但是range iteration要慢

4.4 unordered_multimap

定义
template < class Key, //key_type
           class T, //mapped_type
           class Hash = hash<Key>,  //hasher
           class Pred = equal_to<Key>,  //key_equal
           class Alloc = allocator< pair<const Key,T> >           > class unordered_multimap;
特性
  • 无序,允许重复的key(被分到一个bucket)
;