容器和算法
C++提供了使用抽象进行高效率编程的方式。标准库就是一个很好的例子:标准库定义了许多容器类以及一系列泛型算法,使程序员更加简洁高效的编写程序。算法库提供了各种各样经典算法的有效实现,像查找、排序及其他常见的算法任务。泛型算法中,所谓“泛型”指的是两方面:这些算法可作用于各种不同的容器类型,而这些容器又可以容纳多种不同类型的元素。
1. 顺序容器类型
顺序容器 |
|
vector | 支持快速随机访问 |
list | 支持快速插入删除 |
deque | 双端队列 |
顺序容器适配器 |
|
stack | 后进先出(LIFO)栈 |
queue | 先进先出(FIFO)队列 |
priority_queue | 有优先级管理的队列 |
|
|
2. 顺序容器的定义
包含下列头文件:
#include <vector>
#include <list>
#include<queue>
定义
Vector<string> svec; //empty vector that can hold strings
List<int > ilist; //empty list that hold ints
Deque<sales_item> items ; //empty deque that holds sales_item
容器的构造函数 | |
C<T> c; | 创建名为c的空容器,C是容器类型名,如vector,T是元素类型,如int,string。适用于所有容器 |
C c(c2); | 创建容器c2的副本c;c和c2必须具有相同的容器类型,并存放相同类型的元素,适用于所有容器 |
C c(b,e); | 创建c,其元素是迭代器b和e标示的范围内元素的副本。适用于所有容器 |
C c(n,t); | 用n个值为t的元素创建容器c,其中值t必须是容器类型C的元素类型值,或者是可以转化为该类型的值。只适用于顺序容器 |
C c(n); | 创建有n个值初始化元素的容器c ,只适用于顺序容器 |
A,将一个容器初始化为另一个容器的副本
Vector <int> ivec;
Vector<int> ivec2(ivec); //ok, ivec isvector<int>类型必须匹配,容器类型和元素类型
B, 初始化为一段元素的副本
尽管不能直接将容器内的元素复制给另一种容器,但系统允许通过传递一对迭代器间接实现该功能。使用迭代器时,不要求容器类型相同。容器内的元素类型也可以不相同,只要它们相互兼容。采用这种初始化形式可复制不能直接复制的容器。更重要的是,可以实现复制其他容器的一个子序列。
list<string> slist( svec.begin(), svec.end() ); | 用svec的每个元素初始化slist | ||
vector<string>::iterator mid=sevc.begin()+svec.size()/2; | 找到vector的中间点 | ||
deque<string> front (svec.begin(), mid) | 用svec前部分初始化front,不包括 *(mid) | ||
deque<string> back (mid ,svec.end() ) | 用svec后半部分初始化back,包括*(mid) |
指针就是迭代器
char *words[]={“stately”,”plump”,”buck”,”mulligan”};
size_t word_size=sizeof(words)/sizeof(char*);
list<string> words(words,words+words_size);
将数组长度加到指向第一个元素的指针上就可以得到指向超出数组末端的下一位置的指针。第二个指针提供终止复制的条件,其所指向的位置上存放的元素并没有复制。
C, 分配和初始化指定数目的元素
Const list<int>::size_type list_size=64;
List<string> slist(list_size, “he”); //slist含64个元素,每个元素都被初始化为“he”字符串
List<int> ilist(list_size);//64个元素,每个初始化为0
3. 容器内元素的类型约束
C++语言中,大多数类型都可用作容器的元素类型。容器元素类型必须满足一下两个约束:
l 元素类型必须支持赋值运算(引用不支持一般意义的赋值运算,因此没有元素是引用 类型的容器)
l 元素类型的对象必须可以复制
容器的容器
Vector < vector<string>> lines;//定义vector类型的容器lines,其元素为string类型的vector
Ø > >之间必须有空格
4. 顺序容器的操作
在顺序容器中添加元素的操作 | |
c.push_back(t) | 在容器c的尾部添加值为t的元素,返回void类型 |
c.push_front(t) | 在容器c 的前端添加值为t 的元素,返回void类型,只适用于list和queue |
c.insert(p,t) | 在迭代器p所指向的元素前面插入值为t的新元素,返回指向新添加元素的迭代器 |
c.insert(p,n,t) | 在迭代器p所指向的元素前面插入n个值为t的新元素,返回void类型 |
c.insert(p,b,e) | 在迭代器p所指向的元素前面插入由迭代器b和e标记的范围内的元素,返回void类型 |
要避免存储end操作返回的迭代器
在vector或deque容器中添加元素时,可能会导致某些或全部迭代器失效。特别适用于由end操作返回的迭代器。在容器的任何位置插入任何元素都会使该迭代器失效。
vector<int> :: iterator first=v.begin(),last=v.end();
while(first!=last)
{
first=v.insert(++first,42);
++first;
}//这段代码会导致死循环。添加元素会使得存储在last中的迭代器失效。不要存储end操作返回的迭代器。添加或删除deque或vector容器内的元素都会导致存储的迭代器失效。
为了避免存储end迭代器,可以在每次做完插入运算后重新计算end迭代器值:
while(first!=v.end())
{
first=v.insert(++first,42);
++first;
}//
顺序容器的大小操作 | |
c.size() | 返回容器c中的元素个数,返回类型为c::size_type |
c.max_size() | 返回容器c中可容纳最多元素个数,返回类型为c::size_type |
c.empty() | 返回标记容器大小是否为0的布尔值 |
c.resize(n) | 调整容器c的长度大小,使其能容纳n个元素,如果n<c.size(),则删除多出来的元素;否则,添加采用值初始化的新元素 |
c.resize(n,t) | 调整容器c的大小,使其能容纳n个元素。所有添加的元素值都为t |
访问顺序容器内元素的操作 | |
c.back() | 返回容器c的最后一个元素的引用。如果c为空,则该操作未定义 |
c.front() | 返回容器c的第一个元素的引用。如果c为空,则该操作未定义 |
c[n] | 返回下标为n的元素的引用,如果n<0或n>=c.size(),则该操作未定义,只适用于vector和deque容器 |
c.at(n) | 返回下标为n的元素的引用,如果下标越界,则该操作未定义,只适用于vector和deque容器 |
If(!ilist.empty())
{
List<int>::referenceval=*ilist.begin();
List<int>::referenceval2=ilist.front();
List<int>::reference last=*--ilist.end();
List<int>::referencelast2=ilist.back();
}
删除顺序容器内元素的操作 | |
c.erase(p) | 删除迭代器p所指向的元素,返回一个迭代器,它指向被删除元素后面的元素。如果p指向容器内的最后一个元素,则返回的迭代器指向容器的超出末端的下一个位置。如果p本身就是指向超出末端的下一位置的迭代器,则该函数未定义 |
c.erase(b,e) | 删除迭代器b和e所标记的范围内所有的元素,返回一个迭代器,它指向被删除元素后面的元素。如果e本身就是指向超出末端的下一位置的迭代器,则返回的迭代器也指向容器超出末端的下一位置的迭代器 |
c.clear() | 删除容器c内的所有元素,返回void |
c.pop_back() | 删除容器c的最后一个元素,返回void,如果c为空容器,则该函数未定义 |
c.pop_front() | 删除容器c的第一个元素,返回void,如果c为空容器,则该函数未定义 只适用于list和deque容器 |
Pop_back()和pop_front()函数的返回值并不是删除的元素值,二是void,要获取删除的元素值,则必须在删除元素之前调用front或back函数。
Eg:
StringsearchValue(“hehe”);
List<string>::iterator iter=find(slist.begin(),slist.end(), searchValue);
If(iter!=slist.end())//删除之前必须确保迭代器不是end迭代器
Slist.erase(iter);
顺序容器赋值操作 | |
c1=c2 | 删除容器c1的所有元素,然后将c2的元素复制给c1.c1和c2的类型(包括容器类型和元素类型)必须相同 |
c1.swap(c2) | 交换内容:c1和c2的类型必须相同。 |
c.assign(b,e) | 重新设置c的元素:将迭代器b和e标记的范围内所有元素复制到c中,b和e必须不是指向c中元素的迭代器 |
c.assign(n,t) | 将容器c重新设置为存储n个值为t的元素 |
5. vector容器的自增长
为了使vector容器实现快速的内存分配,其实际分配的容量要比当前所需的空间多一些。Vector容器预留了这些额外的存储区,用于存放新添加的元素。于是,不必为每一个新元素重新分配容器。每当vector容器不得不分配新的存储空间时,以加倍当前容量的分配策略实现重新分配。
6.
String操作 | |
String s; | 定义一个新的空string对象,命名为s |
String s(cp); | 定义一个新的string对象,用cp所指向的(以空字符null结束的)c风格字符串初始化该对象 |
String s(s2); | 定义一个新的string对象,并将他初始化为s2的副本 |
is >>s; | 从输入流is中读取一个以空白字符分隔的字符串,写入s |
os<<s; | 将s写到输出流os中 |
getline(is,s) | 从输入流is中读取一行字符,写入s |
s1+s2 | 把s1和s2串接起来产生一个新的string对象, |
S1+=s2; | 将s2拼接在s1后面 |
关系操作符 | 相等运算(==和!=)以及关系运算符(<, <=, >=) |
String s(“hiya!”);
String::iterator iter=s.begin();
While(iter!=s.end())
{
Cout<<*iter++<<endl;
}
7.容器适配器
除了顺序容器,标准库还提供了三种顺序容器适配器:queue、priority_queue和stack。适配器(adaptor)是标准库中通用的概念,包括容器适配器,迭代适配器和函数适配器。
容器适配器让一种已存在的容器类型采用另一种不同的抽象类型的工作方式实现。例如,stack(栈)适配器可使任何一种顺序容器以栈的方式工作。
A, 适配器初始化
stack<int> stk(deq);//假设deq是deque<int>类型的容器,则可用deq初始化一个新的栈
栈容器适配器支持的操作 | |
s.empty() |
|
s.size() |
|
s.pop() | 删除栈顶元素,但不返回其值 |
s.top() | 返回栈顶元素的值,但不删除该元素 |
s.push(item) | 在栈顶压入新元素 |