您的位置:首页 > 职场人生

面试题总结(三)、《STL源码剖析》相关面试题总结

2015-08-25 18:54 323 查看

声明:本文主要探讨与STL实现相关的面试题,主要参考侯捷的《STL源码剖析》,每一个知识点讨论力求简洁,便于记忆,但讨论深度有限,如要深入研究可点击参考链接,希望对正在找工作的同学有点帮助。

一、STL简介

STL提供六大组件,彼此可以组合套用:

  1. 容器
    容器就是各种数据结构,我就不多说,看看下面这张图回忆一下就好了,从实现角度看,STL容器是一种class template。
  2. 算法
    各种常见算法,如sort,search,copy,erase等,我觉得其中比较值得学习的就是sort,next_permutation,partition,merge sort,从实现角度看,STL算法是一种function template。
  3. 迭代器
    扮演容器与算法之间的胶合剂,是所谓的“泛型指针”。共有五种类型,从实现角度看,迭代器是一种将operator*,operator->,operator++,operator--等指针相关操作予以重载的class template。所有STL容器都附带有自己专属的迭代器,只有容器设计者才知道如何设计迭代器。原生指针也是一种迭代器。是设计模式的一种,所以被问到了解的设计模式可以用来凑数。
  4. 仿函数
    行为类函数,可作为算法的某种策略,从实现角度看,仿函数是一种重载了operator()的class或class template。一般函数指针可视为狭义的仿函数。
  5. 配接器
    一种用来修饰容器或者仿函数或迭代器接口的东西。比如queue和stack,看着像容器,其实就是deque包了一层皮。
  6. 配置器
    负责空间配置与管理。从实现角度看,配置器是一个实现了动态空间配置、空间管理、空间释放额class template。

二、关于容器的一些问题

2.1 当vector的内存用完了,它是如何动态扩展内存的?它是怎么释放内存的?用clear可以释放掉内存吗?是不是线程安全的?

  1. vector内存用完了,会以当前size大小重新申请2*size的内存,然后把原来的元素复制过去,把新元素插上,然后释放原来的内存。
  2. 一般我们释放vector里的元素使用clear,其实它不能释放内存,要想释放内存要使用swap,这样:
    [ol][li][code]vector<type> v;
  3. //.... 这里添加许多元素给v
    [/li]
  4. //.... 这里删除v中的许多元素
  5. vector<type>(v).swap(v);
  6. //此时v的容量已经尽可能的符合其当前包含的元素数量
  7. //对于string则可能像下面这样
  8. string(s).swap(s);
[/code]
  • 引用《effective stl》的第十二条:当涉及 STL容器和线程安全性时,你可以指望一个 STL库允许多个线程同时读一个容器,以及多个线程对不同的容器做写入操作。你不能指望 STL库会把你从手工同步控制中解脱出来,而且你不能依赖于任何线程支持。必须自己去写多线程安全措施。
  • [/ol]

    2.2 map是怎么实现的?查找的复杂度是多少?能不能边遍历边插入?

    1. 红黑树和散列

    2. O(logn)

    3. 不可以,map不像vector,它在对容器执行erase操作后不会返回后一个元素的迭代器,所以不能遍历地往后删除。

    2.3 写多读少应该用什么容器?

    私以为是链表,链表的插入操作时常数时间复杂度,访问操作是O(n),是最适合写多读少的容器。

    2.4 vector每次insert或erase之后,以前保存的iterator会不会失效?

    理论上会失效,理论上每次insert或者erase之后,所有的迭代器就重新计算的,所以都可以看作会失效,原则上是不能使用过期的内存
    但是vector一般底层是用数组实现的,我们仔细考虑数组的特性,不难得出另一个结论,

    1. insert时,假设insert位置在p,分两种情况:
      a) 容器还有空余空间,不重新分配内存,那么p之前的迭代器都有效,p之后的迭代器都失效
      b) 容器重新分配了内存,那么p之后的迭代器都无效咯

    2. erase时,假设erase位置在p,则p之前的迭代器都有效并且p指向下一个元素位置(如果之前p在尾巴上,则p指向无效尾end),p之后的迭代器都无效

    2.5 hash_map和map的区别在哪里?

    hash_map底层是散列的所以理论上操作的平均复杂度是常数时间,map底层是红黑树,理论上平均复杂度是O(logn),下面是借鉴的网上的总结:
    这里总结一下,选用map还是hash_map,关键是看关键字查询操作次数,以及你所需要保证的是查询总体时间还是单个查询的时间。如果是要很多次操作,要求其整体效率,那么使用hash_map,平均处理时间短。如果是少数次的操作,使用 hash_map可能造成不确定的O(N),那么使用平均处理时间相对较慢、单次处理时间恒定的map,考虑整体稳定性应该要高于整体效率,因为前提在操作次数较少。如果在一次流程中,使用hash_map的少数操作产生一个最坏情况O(N),那么hash_map的优势也因此丧尽了。

    2.6 为何map和set不能像vector一样有个reserve函数来预分配数据?

    [p]map和set内部存储的已经不是元素本身了,而是包含元素的节点。也就是说map内部使用的Alloc并不是map声明的时候从参数中传入的Alloc。例如:
    map
    内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
    标签: