1.在做应用时,如何选择vector和deque
(vector和deque有什么区别?在做一个应用的时候,如何选择)
解析:
deque称为双向队列容器,表面上与vector非常相似,甚至能在许多实现中直接替代vector。比较deque和vector两者的成员函数,可以发现下面两点。
1)deque比vector多了push_front和pop_front两个函数,而这两个函数都是对于首部进行操作的。于是得到第一个使用deque的情况,就是当程序需要从首尾两端进行插入和删除元素操作的时候。
2)deque中不存在capacity()和reserve()成员函数。在vector中,这两个函数分别用于获取和设置容器容量,例如下面的代码段:
int main()
{
vector<int> v;
v.push_back(1);
cout<<v.capacity()<<endl;
v.reserve(10);
cout<<v.capacity()<<endl;
return 0;
对于vector来说,如果有大量的数据需要push back,则应当使用reserve()函数先设定其容量大小,否则会出现许多次容量扩充操作,导致效率很低。
而deque使用一段一段的定量内存,在进行内存扩充时也只是加一段定量内存,因此不存在容量的概念,也就没有capacity()和reserve()成员函数。
最后,在插入(insert)操作上,deque和vector有很大的不同。由于vector是一块连续的内存,所以插入的位置决定执行效率,位置越偏向数组首部,效率越低。而deque中的内存是分段连续的,因此在不同段中的插入效率都相同。
总的来说:
vector和deque的不同点:内部数据管理不同。为了提高效率,vector在添加元素之前最好调用reserve()设置容量,而deque()则不需要。
选择的方法:
一般情况下选择vector;但当需要从首尾两端进行插入或者删除元素操作的时候,应该选择deque。
2.看代码找错误——适配器stack和queue的使用
(理解适配器和queue的使用)
#include<iostream>
#include<vector>
#include<queue>
#include<stack>
using namespace std;
int main()
{
stack<int,vector<int>> s;
queue<int,vector<int>> q;
for(int i=1;i<10;i++)
{
s.push(i);
q.push(i);
}
while(!s.empty)
{
cout<<s.top()<<endl;
s.pop();
}
24 while(!q.empty())
{
cout<<q.front()<<endl;
q.pop();
}
return 0;
使用vector分别定义了stack和queue,把1到9放入s和q中。代码第18-22行,循环打印s的栈顶元素,并且不断退栈,最终s变为空栈,打印顺序与如栈的顺序相反,即9 8 7 6 5 4 3 2 1。
代码第24到28行,与前面的操作方法一样,但是这里会出现编译错误。这是因为,queue是先进先出的,入队(调用push)是对队尾进行操作,由于q使用vector作为其序列容器,因此实际调用的是vector的push_back成员函数。而出队(调用pop)是对队首的操作。此时q实际需要调用vector的pop_front成员函数,而vector没有这个pop_front成员。因此出现编译错误。
stack是后进先出的,入栈和退栈都是对尾部进行操作。而vector有相应的push_back和pop_back成员函数,因此代码第18-22能够安全运行。
答案:由于vector没有pop_front函数,因此代码第27行出现编译错误。
3.举例说明set的用法
#include<iostream>
#include<set>
#include<string>
using namespace std;
int main()
{
set<string> strset;
set<string>::iterator si;
strset.insert("cantaloupes");//插入6个元素,其中有两个grapes
strset.insert("apple");
strset.insert("orange");
strset.insert("banana");
strset.insert("grapes");
strset.insert("banana");
strset.erase("banana");
for(si=strset.begin();si!=strset.end();si++)
{
cout<<*si<<" ";//打印set的所有元素
}
cout<<endl;
return 0;
}
4.举例说明map的用法
#include<iostream>
#include<map>
#include<string>
using namespace std;
int main()
{
map<int,string> mapstring;//键为int类型,值为string类型
mapstring.insert(pair<int,string>(1,"one"));
mapstring.insert(pair<int,string>(4,"four"));
mapstring.insert(pair<int,string>(3,"three"));
mapstring.insert(pair<int,string>(2,"two"));
13 mapstring.insert(pair<int,string>(4,"four four"));//4经存在,插入失败
15 mapstring[1]="one one";//1已经存在,修改键为1对应的值
mapstring[5]="five";//5不存在,添加键为5且值为“five”的元素
cout<<mapstring[1]<<endl;//打印键为1对应元素的值
mapstring.erase(2);//删除键为2的值
map<int,string>::iterator f=mapstring.find(2);//查找键为2的元素
if(f!=mapstring.end())//判断查找是否成功,如果成功,则不相等
{
mapstring.erase(f);
}
map<int,string>::iterator it=mapstring.begin();
while(it!=mapstring.end())//使用迭代器遍历map中所有元素
{
cout<<(*it).first<<" "<<(*it).second<<endl;//打印元素的键和值
it++;
}
return 0;
}
上面的程序中,mapstring是存放pair 《int,string>类型的元素。
插入操作,insert成员函数或使用[ ]操作符都可以进行插入。但它们有一点区别:当map中已经存在此键时,insert插入失败,例如代码第13行的插入没有作用;而[]操作符则修改此键所对应的元素,例如代码第15行则修改键为1所对应的值。
查找操作,代码第20行查找键为2的元素,如果查找成功,则迭代器指向键为2的元素,否则指向末尾,即mapstring.end( )。
删除操作,这里演示了两种删除,和别的容器一样,可以删除迭代器指向的元素位置(或者两个迭代器的区别删除)。由于map中元素的键是唯一的,因此map也提供了以键删除的操作(如代码第19行)。
遍历操作,与其他stl容器遍历操作类似,使用迭代器对begin( )和end()之间进行迭代。it指向的是都pair《int,string>元素。pair有两个成员:first和second,分别表示键(key)和值(value)。
可以看到,map遍历的结果是按照元素的键(key)的升序排列。这是默认情况,如果想让它们降序排列,只需要把map和iterator的声明都改为《int,string,greater《int>>就可以了,其中greater《int>表示按从大到小排序,并且键的类型是int。
5.STL中中map的内部是怎么实现的
标准的STL关联容器(包括set和map以及set的衍生体multiset和map的衍生体multimap)的内部结构是一个平衡二叉树。平衡二叉树有以下几种:AVL-tree,RB-tree,AA-tree。
STL的底层机制都是以RB-tree(红黑树)完成的。RB-tree也是一个独立容器,但并不给外界使用。红黑树这个名字的得来就是由于树的每个结点都被着上了红色或者黑色,结点所着的颜色被用来检测树的平衡性。在对节点插入或删除操作中,可能会被旋转来保持树的平衡性。平均和最坏情况下的插入,删除,查找时间都是O(lgn)。
一个红黑树是一棵二叉查找树,除了二叉查找树带有的一般要求外,它还具有下列的特性。
1)结点为红色或者黑色。2)所有叶子结点都是空结点,并且被着为黑色。3)如果父节点是红色的,那么两个子节点都是黑色的。4)结点到其子孙结点的每条简单路径上都包含相同数目的黑色结点。5)根结点是黑色的。
6.map和hashmap有什么区别
有以下几点区别:
1)底层的数据结构不同,map是红黑树,hashmap是哈希表。
2)map的优点在于元素可以自动按照键值排序,而hash map的优点在于它的各项操作的平均时间复杂度接近常数。
3)map属于标准的一部分,而hashmap则不是。
7.什么是STL算法
STL包含了大量的算法。它们巧妙的处理存储在容器中的数据。以reverse算法为例,只要简单使用reverse算法就能够逆置一个区间中的元素。
#include<iostream>
#include<vector>
#include<string>
#include<algorithm>
using namespace std;
int main()
{
int a[4]={1,2,3,4};
vector<string> v;
v.push_back("one");
v.push_back("two");
v.push_back("three");
reverse<int [4]>(a,a+4);//把数组a的所有元素逆置
reverse< vector<string>::iterator>(v.begin(),v.end());//逆置v中所有元素
for(vector<string>::iterator it=v.begin();it!=v.end();it++)
{
cout<<*it<<" ";//输出v的元素
}
cout<<endl;
for(int i=0;i<4;i++)
{
cout<<a[i]<<" ";//输出数组a中的元素
}
return 0;
}
注意:
reverse是个全局变量,而不是成员函数。
reverse有两个参数,第一个参数是指向要操作的范围的头的指针,第二个参数是指向尾的指针。
reverse操作一定范围的元素而不是仅仅操作容器,本题中也对数组进行了操作。
reverse和其他的STL算法一样,它们是通用的,也就是说,reverse不仅可以用来颠倒向量的元素,也可以用来颠倒链表中元素的顺序,甚至可以对数组操作,它们实际上都是函数模板。
8.分析代码功能——STL算法的使用
#include<iostream>
#include<deque>
#include<algorithm>
using namespace std;
void print(int elem)
{
cout<<elem<<" ";
}
int main()
{
deque<int> coll;
for(int i=1;i<=9;++i)
{
coll.push_back(i);
}
deque<int>::iterator pos1;
pos1=find(coll.begin(),coll.end(),2);
deque<int>::iterator pos2;
pos2=find(coll.begin(),coll.end(),7);
for_each(pos1,pos2,print);
cout<<endl;
deque<int>::reverse_iterator rpos1(pos1);
deque<int>::reverse_iterator rpos2(pos2);
for_each(rpos2,rpos1,print);
cout<<endl;
return 0;
}
涉及的内容:
1)deque容器的操作方法;2)find和for_each两种泛型算法;3)iterator和reverse_iterator的使用