(1) 为何map和set的插入删除效率比用其他序列容器高?
因为map和set的内部数据结构是红黑树,它的插入和删除不需做内存的拷贝和移动。(红黑树的插入和删除是log(n)的)。
(2) 为何每次insert之后,以前保存的iterator不会失效?
iterator这里就相当于指向节点的指针,内存没有变,指向内存的指针怎么会失效呢(当然被删除的那个元素本身已经失效了)。相对于vector来说,每一次删除和插入,指针都有可能失效,调用push_back在尾部插入也是如此。因为为了保证内部数据的连续存放,iterator指向的那块内存在删除和插入过程中可能已经被其他内存覆盖或者内存已经被释放了。即使时push_back的时候,容器内部空间可能不够,需要一块新的更大的内存,只有把以前的内存释放,申请新的更大的内存,复制已有的数据元素到新的内存,最后把需要插入的元素放到最后,那么以前的内存指针自然就不可用了。
(3) 向一个vector中添加N个元素,平均的添加的性能是?
看看下面代码的输出:
vector<int> v;
for(int i=0;i<100;i++}
{
cout << v.capacity() << endl;
v.push_back(i);
}
可以看到空间的变化是0,1,2,4,8,16... (GCC下是如此,VC下不是..)
这个需要考虑到当空间不够时,需要开辟新的内存并且发生元素的copy. 一般情况下开辟的空间是原来的2倍。那么平均的添加时间是:(1+2+4+...+(N-logN))/N,也就是有logN个元素添加时需要整体挪动n次,其它的操作代价是1,平均下来依旧是O(N)。
(4) 如何理解容器适配器,其与顺序容器有什么区别?
adapter原意是插座、适配器、接合器的意思。现在我需要一个栈结构,我们可以用deque来模拟,只在一端进行元素插入和弹出,另一端不动。但deuqe毕竟不能直接作为一个stack,它并不能直接地严格地满足你的要求,因为你不能防止别人在另一端乱动你的东西。你需要对它进行一些包装,作一些限制,使之只能在一端进行插入和删除。也就是说你必须提供一个“插座”,这个“插座”一端插在deque上,另一端插在你的程序中,你就可以使用栈结构了。而stack就是这样的“插座”,它连接了deque和你的程序。表面上看你使用的是stack,实际上你是通过stack这个“插座”来使用deque(因为stack完全是用deque来实现的,它并没有任何其他的东西,它只是在deque上面作了一层包装,相当于一个“插座”的功能)。因此,stack、queue、priority_queue这样的类一般称为容器适配器,它们只是基本容器类型(vector,dequeue,list)的适配。
实际上,这也适配器模式的基本思想:将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。也就是说,在一个类的接口上提供一个“插座”类,使它变成你希望使用的接口。