Part10 泛型程序设计与C++标准模板库10.4顺序容器

顺序容器
  向量(vector)
  双端队列(deque)
  列表(list)
  单向链表(forward_list) (以上四种在逻辑上可看作是一个长度可扩展的数组)
  数组(array)
    元素线性排列,可以随时在指定位置插入元素和删除元素。
    必须符合Assignable这一概念(即具有公有的拷贝构造函数并可以用“=”赋值)。
    array对象的大小固定,forward_list有特殊的添加和删除操作。

顺序容器的接口(不包含单向链表(forward_list)和数组(array))
  构造函数
  赋值函数
    assign
  插入函数
    insert, pushfront(只对list和deque), pushback,emplace,emplace_front
  删除函数
    erase,clear,popfront(只对list和deque) ,popback,emplace_back
  首尾元素的直接访问
    front,back
  改变大小
    resize

//10-4顺序容器的基本操作
#include<iostream>
#include<list>
#include<deque>
#include<iterator>
using namespace std;

template <class T> //输出指定的顺序容器的元素
void printContainer(const char* msg, const T& s) {
    cout << msg << ": ";
    copy(s.begin(), s.end(), ostream_iterator<int>(cout, " "));
    cout << endl;
}

int main() {
    //从标准输入读入10个整数,将它们分别从s的头部加入
    deque<int> s;
    for (int i = 0; i < 10; i++) {
        int x;
        cin >> x;
        s.push_front(x);
    }
    printContainer("deque at first", s);
    //用s容器的内容的逆序构造列表容器l
    list<int> l(s.rbegin(), s.rend());
    printContainer("list at first", l);

    //将列表容器l的每相邻两个元素顺序颠倒
    list<int>::iterator iter = l.begin();
    while (iter != l.end()) {
        int v = *iter;  
        iter = l.erase(iter);
        l.insert(++iter, v);
    }
    printContainer("list at last", l);
    //用列表容器l的内容给s赋值,将s输出
    s.assign(l.begin(), l.end());
    printContainer("deque at last", s);
    return 0;
}

 

 

 


2顺序容器的特征
顺序容器:向量、双端队列、列表、单向链表、数组

向量(Vector)
  特点
    一个可以扩展的动态数组
    随机访问、在尾部插入或删除元素快
    在中间或头部插入或删除元素慢
  向量的容量
    容量(capacity):实际分配空间的大小
    s.capacity() :返回当前容量
    s.reserve(n):若容量小于n,则对s进行扩展,使其容量至少为n
双端队列(deque)
  特点
    在两端插入或删除元素快
    在中间插入或删除元素慢
    随机访问较快,但比向量容器慢

//10-5奇偶排序,先按照从大到小顺序输出奇数,再按照从小到大顺序输出偶数。
#include<iostream>
#include<iterator>
#include<vector>
#include<deque>
#include<algorithm>
using namespace std;
int main(){
    istream_iterator<int> i1(cin), i2;        //建立一对输入流迭代器
    vector<int> s1(i1, i2);        //通过输入流迭代器从标准输入流中输入数据
    sort(s1.begin(), s1.end());
    deque<int> s2;
    for(vector<int>::iterator iter = s1.begin(); iter != s1.end(); ++iter){
        if(*iter % 2 == 0)        //偶数放到尾部
            s2.push_back(*iter);
        else        //奇数放到头部
            s2.push_front(*iter);
    }
    //将s2的结果输出
    copy(s2.begin(), s2.end(), ostream_iterator<int>(cout, " "));
    cout << endl;
    return 0;
}

 

列表(list)
  特点
    在任意位置插入和删除元素都很快
    不支持随机访问
  接合(splice)操作
    s1.splice(p, s2, q1, q2):将s2中[q1, q2)移动到s1中p所指向元素之前

//list举例
#include<iostream>
#include<iterator>
#include<list>
#include<string>
#include<algorithm>
using namespace std;

int main(){
    string names1[] = {"Alice", "Helen", "Lucy", "Susan"};
    string names2[] = {"Bob", "David", "Levin", "Mike"};
    list<string> s1(names1,names1 + 4);        //用names1数组的内容构造列表s1
    list<string> s2(names2,names2 + 4);        //用names2数组的内容构造列表s2
    
    s2.splice(s2.end(), s1, s1.begin());        //将s1的第一个元素放到s2的最后
    list<string>::iterator iter1 = s1.begin();     //iter1指向s1首
    advance(iter1,2);            //    iter1前进2个元素,指向s1第3个元素
    list<string>::iterator iter2 = s2.begin();    //iter2指向s2首
    ++iter2;        //指向s2第二个元素
    list<string>::iterator iter3 = iter2;
    advance(iter3,2);        //指向s2的第四个元素
    s1.splice(iter1, s2, iter2, iter3);        //将[iter2, iter3)范围内的结点接到s1中iter1指向的结点前
    
    copy(s1.begin(), s1.end(), ostream_iterator<string>(cout, " "));
    cout << endl;
    copy(s2.begin(), s2.end(), ostream_iterator<string>(cout, " "));
    cout << endl;
    return 0;
}

 

单向链表(forward_list)
    单向链表每个结点只有指向下个结点的指针,没有简单的方法来获取一个结点的前驱;
    未定义insert、emplace和erase操作,而定义了insertafter、emplaceafter和erase_after操作,其参数与list的insert、emplace和erase相同,
    但并不是插入或删除迭代器p1所指的元素,而是对p1所指元素之后的结点进行操作;
    不支持size操作。

数组(array)
    array是对内置数组的封装,提供了更安全,更方便的使用数组的方式
    array的对象的大小是固定的,定义时除了需要指定元素类型,还需要指定容器大小。
    不能动态地改变容器大小

顺序容器的比较
    如果需要执行大量的随机访问操作,而且当扩展容器时只需要向容器尾部加入新的元素,就应当选择向量容器vector;
    如果需要少量的随机访问操作,需要在容器两端插入或删除元素,则应当选择双端队列容器deque;
    如果不需要对容器进行随机访问,但是需要在中间位置插入或者删除元素,就应当选择列表容器list或forward_list;
    如果需要数组,array相对于内置数组类型而言,是一种更安全、更容易使用的数组类型。

 

 


3顺序容器的插入迭代器与适配器
顺序容器的插入迭代器
    用于向容器头部、尾部或中间指定位置插入元素的迭代器
    包括前插迭代器(frontinserter)、后插迭代器(backinsrter)和任意位置插入迭代器(inserter)
    例:
      list<int> s;
      back_inserter iter(s);
      *(iter++) = 5; //通过iter把5插入s末尾

顺序容器的适配器
  以顺序容器为基础构建一些常用数据结构,是对顺序容器的封装
    栈(stack):最先压入的元素最后被弹出
    队列(queue):最先压入的元素最先被弹出
    优先级队列(priority_queue):最“大”的元素最先被弹出

 

栈和队列模板
  栈模板
    template <class T, class Sequence = deque<T> > class stack;
  队列模板
    template <class T, class FrontInsertionSequence = deque<T> > class queue;
  栈可以用任何一种顺序容器作为基础容器,而队列只允许用前插顺序容器(双端队列或列表)

栈和队列共同支持的操作
  s1 op s2 op可以是==、!=、<、<=、>、>=之一,它会对两个容器适配器之间的元素按字典序进行比较
  s.size() 返回s的元素个数
  s.empty() 返回s是否为空
  s.push(t) 将元素t压入到s中
  s.pop() 将一个元素从s中弹出,对于栈来说,每次弹出的是最后被压入的元素,而对于队列,每次被弹出的是最先被压入的元素
  不支持迭代器,因为它们不允许对任意元素进行访问

栈和队列不同的操作
  栈的操作
    s.top() 返回栈顶元素的引用
  队列操作
    s.front() 获得队头元素的引用
    s.back() 获得队尾元素的引用

//例10-7 利用栈反向输出单词
#include<iostream>
#include<stack>
#include<string>
using namespace std;
int main(){
    stack<char> s;
    string str;
    cin >> str;
    for(string::iterator iter = str.begin(); iter != str.end(); ++iter)
        s.push(*iter);        //将字符串的每个元素顺序压入栈中
    while(!s.empty()){
        cout << s.top();
        s.pop();
    }
    cout << endl;
    return 0;
}

 

 

优先级队列
    优先级队列也像栈和队列一样支持元素的压入和弹出,但元素弹出的顺序与元素的大小有关,每次弹出的总是容器中最“大”的一个元素。
    template <class T, class Sequence = vector<T> > class priority_queue;

    优先级队列的基础容器必须是支持随机访问的顺序容器。
    支持栈和队列的size、empty、push、pop几个成员函数,用法与栈和队列相同。
    优先级队列并不支持比较操作。
    与栈类似,优先级队列提供一个top函数,可以获得下一个即将被弹出元素(即最“大”的元素)的引用。

posted @ 2017-12-29 13:44  LeoSirius  阅读(184)  评论(0编辑  收藏  举报