c++面试题【转】

语言部分:

  • 虚函数,多态。这个概念几乎是必问。
  • STL的使用和背后数据结构,vector string map set 和hash_map,hash_set
  • 实现一个栈类,类似STL中的栈。这个题目初看非常简单,当时我还有点不屑,怎么出这么简单的题。但写过c++和没有写过c++的人写出的代码是一眼就能看出差别的。譬如三大函数有没有写,引用的使用,都非常的关键。如果这方面没有经验,建议阅读下http://book.douban.com/subject/1971825/ 这本书中简单数据结构的实现,细节很关键
  • c部分:函数指针的声明
  • c++中的多线程

算法部分:

  • 判断一个单链表是否存在循环(快慢指针的方法)
  • 如何判断一副麻将牌胡了(回朔法
  • 二叉搜索树,节点上已经标有数字,如何找两个节点的最小公共节点(查找第一个在两个节点值中间的节点,如两个节点分别是3,8,查找在3,8之间的值的节点,如没找到,则3,8其中一个是另一个父节点)
  • 二叉树,如何找两个节点的最小公共节点(比较笨的方法是,找到从root到两个节点的路径,然后两条路径的从root开始的最后一个相同节点就是所求节点。精妙点的参考lca算法)
  • 最短摘要生成,有一个数组,还有一个集合,在数组中寻找包含集合中全部元素的最短子串。详细可以参见编程之美3.5
  • 寻找最大的K个数,编程之美2.5
  • 如何判断两个网页的相关性(如果说面试搜索相关的只是,看下吴军的数学之美大有裨益,其中的向量空间模型非常优美,TF和IDF这些术语也必须知道)
  • Hash的平均查找长度和装填因子的关系(好好回味下数据结构书吧)哈希表(等概率情况下)查找成功与查找不成功的平均查找长度
    • 装填因子load factor λ:散列表中元素 与 散列表大小 的比值。
    • 常数时间 执行插入 删除 查找,;哈希函数;解决冲突:分离链接法,λ≈1,不使用分离连接,尽量让λ<0.5 这样效率最高
  • 一致性hash原理一致性哈希算法(consistent hashing)
  • rehash的时候可能导致插入的元素响应时间特别长,有无更好的方法?
    • 可以参考redis的rehash实现,当到了rehash的时候,不是一次性把所有的数据迁移到另一个更大的hash表中,而是每次迁移一个bucket,这样可以平摊时间
  • 如何对全是01的文件进行压缩,一直没有好的答案。高压缩文件是如何实现的

系统设计部分:

  • 当系统的处理请求时间一定的时候,如何可以加快响应速度。(cache的使用),当时很傻,竟然没想到这个,我给的答案是增加系统规模,这样只能减少请求等待时间。有时候面试人就会短路,google的题,想了半天才想出来
  • 两块虚拟网卡进行通讯,但是需要数据加密,如何实现数据加密。感觉跟ssl很像

编程部分:

  • 二分查找(频率超高)
  • 建堆,顺便说下建堆的时间复杂度是O(n),不是log,证明可以看算法导论构建二叉堆时间复杂度的证明
  • 写一个函数,将字符串反转,反转方式如下:“I am a student”反转成“student a am I”,不借助任何库函数 (频率超高的一题,先反转整个字符串,然后反转每个词

详细分析

2.标准关联容器set, multiset, map, multimap

内部采用的就是一种非常高效的平衡检索二叉树:红黑树,也成为RB树(Red-BlackTree)。RB树的统计性能要好于一般的平衡二叉树

3.STL map和set的使用中不易理解的地方

  • map: type pair<constKey, T>

很多不同的const Key对应的T对象的一个集合,所有的记录集中只要const Key 不一样就可以,T无关!

  • set: type const Key

只存单一的对const Key,没有map 的T对像!可以看成map的一个特例

(1)为何map和set的插入删除效率比用其他序列容器高?,树

答:因为对于关联容器来说,不需要做内存拷贝和内存移动。说对了,确实如此。map和set容器内所有元素都是以节点的方式来存储,其节点结构和链表差不多,指向父节点和子节点

(2)为何每次insert之后,以前保存的iterator不会失效?

答:iterator这里就相当于指向节点的指针,内存没有变,指向内存的指针怎么会失效呢(当然被删除的那个元素本身已经失效了)。相对于vector来说,每一次删除和插入,指针都有可能失效,调用push_back在尾部插入也是如此。因为为了保证内部数据的连续存放,iterator指向的那块内存在删除和插入过程中可能已经被其他内存覆盖或者内存已经被释放了。即使时push_back的时候,容器内部空间可能不够,需要一块新的更大的内存,只有把以前的内存释放,申请新的更大的内存,复制已有的数据元素到新的内存,最后把需要插入的元素放到最后,那么以前的内存指针自然就不可用了。特别时在和find等算法在一起使用的时候,牢记这个原则:不要使用过期的iterator。

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

答:我以前也这么问,究其原理来说时,引起它的原因在于在map和set内部存储的已经不是元素本身了,而是包含元素的节点。也就是说map内部使用的Alloc并不是map<Key, Data, Compare, Alloc>声明的时候从参数中传入的Alloc。

4.set, multiset

set和multiset会根据特定的排序准则自动将元素排序,set中元素不允许重复,multiset可以重复。

因为是排序的,所以set中的元素不能被修改,只能删除后再添加。

向set中添加的元素类型必须重载<操作符用来排序。排序满足以下准则:

    1. 非对称,若A<B为真,则B<A为假。
    2. 可传递,若A<B,B<C,则A<C。
    3. A<A永远为假。

set中判断元素是否相等:

if(!(A<B || B<A)),当A<B和B<A都为假时,它们相等。

5.map,multimap

map和multimap将key和value组成的pair作为元素,根据key的排序准则自动将元素排序,map中元素的key不允许重复,multimap可以重复。

    • map<key,value>

因为是排序的,所以map中元素的key不能被修改,只能删除后再添加。key对应的value可以修改。

向map中添加的元素的key类型必须重载<操作符用来排序。排序与set规则一致。

6. List的功能方法

  实际上有两种List: 一种是基本的ArrayList,其优点在于随机访问元素,另一种是更强大的LinkedList,它并不是为快速随机访问设计的,而是具有一套更通用的方法。

  List : 次序是List最重要的特点:它保证维护元素特定的顺序。List为Collection添加了许多方法,使得能够向List中间插入与移除元素(这只推荐LinkedList使用。)一个List可以生成ListIterator,使用它可以从两个方向遍历List,也可以从List中间插入和移除元素。

  ArrayList : 由数组实现的List。允许对元素进行快速随机访问,但是向List中间插入与移除元素的速度很慢。ListIterator只应该用来由后向前遍历ArrayList,而不是用来插入和移除元素。因为那比LinkedList开销要大很多。

  LinkedList : 对顺序访问进行了优化,向List中间插入与删除的开销并不大。随机访问则相对较慢。(使用ArrayList代替。)还具有下列方法:addFirst(), addLast(), getFirst(),getLast(), removeFirst() 和 removeLast(), 这些方法 (没有在任何接口或基类中定义过)使得LinkedList可以当作堆栈、队列和双向队列使用

7.1 hash_map和map的区别在哪里?

构造函数。hash_map需要hash函数,等于函数;map只需要比较函数(小于函数).

存储结构。hash_map采用hash表存储,map一般采用红黑树(RB Tree)实现。因此其memory数据结构是不一样的。

7.2 什么时候需要用hash_map,什么时候需要用map?

总体来说,hash_map 查找速度会比map快,而且查找速度基本和数据数据量大小,属于常数级别map的查找速度是log(n)级别。并不一定常数就比log(n)小,hash还有hash函数的耗时,明白了吧,如果你考虑效率,特别是在元素达到一定数量级时,考虑考虑hash_map。但若你对内存使用特别严格,希望程序尽可能少消耗内存,那么一定要小心,hash_map可能会让你陷入尴尬,特别是当你的hash_map对象特别多时,你就更无法控制了,而且hash_map的构造速度较慢。

现在知道如何选择了吗?权衡三个因素: 查找速度, 数据量, 内存使用。

8.一些使用上的建议:

Level 1 - 仅仅作为Map使用:采用静态数组

Level 2 - 保存定长数据,使用时也是全部遍历:采用动态数组(长度一开始就固定的话静态数组也行)

Level 3 - 保存不定长数组,需要动态增加的能力,侧重于寻找数据的速度:采用vector

Level 3 - 保存不定长数组,需要动态增加的能力,侧重于增加删除数据的速度:采用list

Level 4 - 对数据有复杂操作,即需要前后增删数据的能力,又要良好的数据访问速度:采用deque

Level 5 - 对数据中间的增删操作比较多:采用list,建议在排序的基础上,批量进行增删可以对运行效率提供最大的保证

Level 6 - 上述中找不到适合的:组合STL容器或者自己建立特殊的数据结构来实现

9.其他

(1).vector - 会自动增长的数组

vector<int>vec(10) //一个有10个int元素的容器

vector<float> vec(10, 0.5)//一个有10个float元素的容器,并且他们得值都是0.5

vector<int>::iterator iter;

for(iter = vec.begin(); iter != vec.end(); iter++)

{

    //. . . . . . .

}

vector由于数组的增长只能向前,所以也只提供了后端插入和后端删除,也就是push_back和pop_back。当然在前端和中间要操作数据也是可以的,用insert和erase,但是前端和中间对数据进行操作必然会引起数据块的移动,这对性能影响是非常大的。

最大的优势就是随机访问的能力。

vector<T1>::iterator相关的方法有:

begin():用来获得一个指向vector第一个元素的指针

end():用来获得一个指向vector最后一个元素之后的那个位置的指针,注意不是指向最后一个元素。

erase(vector<T1>::iterator):用来删除作为参数所传入的那个iterator所指向的那个元素。

(2).list - 擅长插入删除的链表

list<string>Milkshakes; list<int> Scores;

push_back, pop_backpush_front. pop_front

list是一个双向链表的实现。

为了提供双向遍历的能力,list要比一般的数据单元多出两个指向前后的指针

一个使用iterator来删除元素的例子

list<string> stringList;

list<string>::iterator iter;

advance(iter, 5); //将iterator指向stringList的第五个元素

stringList.erase(iterator);//删除

那么删除操作进行以后,传入erase()方法的iterator指向哪里了呢?它指向了删除操作前所指向的那个元素的后一个元素。

(3).deque - 拥有vector和list两者优点的双端队列

(4).这三个模板的总结 比较和一般使用准则

这三个模板都属于序列类模板,可以看到他们有一些通用方法

      • size():得到容器大小
      • begin():得到指向容器内第一个元素的指针(这个指针的类型依容器的不同而不同)
      • end():得到指向容器内最后一个元素之后一个位置的指针
      • erase():删除传入指针指向的那个元素
      • clear():清除所有的元素
      • ==运算符:判断两个容器是否相等
      • =运算符:用来给容器赋值
上面的三个模板有各自的特点

      • vector模板的数据在内存中连续的排列,所以随机存取元素(即通过[]运算符存取)的速度最快,这一点和数组是一致的。同样由于它的连续排列,所以它在除尾部以外的位置删除或添加元素的速度很慢,在使用vector时,要避免这种操作。
      • list模板的数据是链式存储,所以不能随机存取元素。它的优势在于任意位置添加 删除元素的速度。
      • deque模板是通过链接若干片连续的数据实现的,所以均衡了以上两个容器的特点
时间: 2024-08-07 21:17:42

c++面试题【转】的相关文章

【转】嵌入式软件工程师经典笔试题

嵌入式软件工程师经典笔试题 > 预处理器(Preprocessor) 1. 用预处理指令#define 声明一个常数,用以表明1年中有多少秒(忽略闰年问题) #define SECONDS_PER_YEAR (60 * 60 * 24 * 365)UL 我在这想看到几件事情: 1). #define 语法的基本知识(例如:不能以分号结束,括号的使用,等等) 2). 懂得预处理器将为你计算常数表达式的值,因此,直接写出你是如何计算一年中 有多少秒而不是计算出实际的值,是更清晰而没有代价的. 3).

10个常见的Node.js面试题

如果你希望找一份有关Node.js的工作,但又不知道从哪里入手评测自己对Node.js的掌握程度. 本文就为你罗列了10个常见的Node.js面试题,分别考察了Node.js编程相关的几个主要方面. 在进入正文之前,需要提前声明两点: 这些问题只是Node.js知识体系的一个局部,并不能完全考察被面试者的实际开发能力. 对现实世界开发中遇到的问题,需要的是随机应变与团队合作,所以你可以尝试结对编程. Node.js面试题列表 什么是错误优先的回调函数? 如何避免回调地狱? 如何用Node来监听8

.NET教程:.NET 面试题之IEnumerable

.NET教程,今天给大家介绍的是:.NET 面试题之IEnumerable ,这是在面试的时候可能会碰到的一道题目,这道题的注解分为了两个部分,这一篇是第一部分! 什么是IEnumerable? IEnumerable及IEnumerable的泛型版本IEnumerable是一个接口,它只含有一个方法GetEnumerator.Enumerable这个静态类型含有很多扩展方法,其扩展的目标是IEnumerable. 实现了这个接口的类可以使用Foreach关键字进行迭代(迭代的意思是对于一个集合

java 19 - 9 finally有关的面试题

1 /* 2 * 面试题: 3 * 1:final,finally和finalize的区别 4 * final:最终的意思,可以修饰类,成员变量,成员方法 5 * 修饰类,类不能被继承 6 * 修饰变量,变量是常量 7 * 修饰方法,方法不能被重写 8 * finally:是异常处理的一部分,用于释放资源. 9 * 一般来说,代码肯定会执行,特殊情况:在执行到finally之前jvm退出了 10 * finalize:是Object类的一个方法,用于垃圾回收 11 * 12 * 2:如果catc

java面试题大全

java面试笔试题大汇总     第一,谈谈final, finally, finalize的区别. 最常被问到. 第二,Anonymous Inner Class (匿名内部类) 是否可以extends(继承)其它类,是否可以implements(实现)interface(接口)? 第三,Static Nested Class 和 Inner Class的不同,说得越多越好(面试题有的很笼统). 第四,&和&&的区别. 这个问得很少. 第五,HashMap和Hashtable的区

【面试】iOS 开发面试题(二)

1. 我们说的oc是动态运行时语言是什么意思? 答案:多态. 主要是将数据类型的确定由编译时,推迟到了运行时. 这个问题其实浅涉及到两个概念,运行时和多态. 简单来说,运行时机制使我们直到运行时才去决定一个对象的类别,以及调用该类别对象指定方法. 多态:不同对象以自己的方式响应相同的消息的能力叫做多态.意思就是假设生物类(life)都用有一个相同的方法-eat; 那人类属于生物,猪也属于生物,都继承了life后,实现各自的eat,但是调用是我们只需调用各自的eat方法. 也就是不同的对象以自己的

JAVA常见面试题及解答-java开发

JAVA常见面试题及解答 Java的垃圾回收总结  浅谈Java中的内部类 1)transient和volatile是java关键字吗? 如果用transient声明一个实例变量,当对象存储时,它的值不需要维持.例如: class T { transient int a;  //不需要维持 int b;  //需要维持 } 这里,如果T类的一个对象写入一个持久的存储区域,a的内容不被保存,但b的将被保存. volatile修饰符告诉编译器被volatile修饰的变量可以被程序的其他部分改变.在多

链表面试题Java实现【重要】

本文包含以下内容: 1.单链表的创建和遍历 2.求单链表中节点的个数 3.查找单链表中的倒数第k个结点(剑指offer,题15) 4.查找单链表中的中间结点 5.合并两个有序的单链表,合并之后的链表依然有序[出现频率高](剑指offer,题17) 6.单链表的反转[出现频率最高](剑指offer,题16) 7.从尾到头打印单链表(剑指offer,题5) 8.判断单链表是否有环 9.取出有环链表中,环的长度 10.单链表中,取出环的起始点(剑指offer,题56) 11.判断两个单链表相交的第一个

Android-异步任务介绍及面试题

Android-异步任务 一 什么是AsyncTask Android为了减低异步操作的开发难度,结合Handle和线程池,提供了AsyncTask.AsyncTask就是一个封装过的后台任务类, 顾名思义就是异步任务,他具有可以在后台执行耗时操作,同时可以将 执行的进度与UI进行同步的优点 因为Handle实际上就是两个线程之间的桥梁,但是数据的传递是单向的 Handle机制如下图: 而AsyncTask机制如下: 二 如何使用AsyncTask AsyncTask定义三种泛型类型Params

Java面试题全集(中)

这部分主要是与Java Web和Web Service相关的面试题. 96.阐述Servlet和CGI的区别? 答:Servlet与CGI的区别在于Servlet处于服务器进程中,它通过多线程方式运行其service()方法,一个实例可以服务于多个请求,并且其实例一般不会销毁,而CGI对每个请求都产生新的进程,服务完成后就销毁,所以效率上低于Servlet. 补充:Sun Microsystems公司在1996年发布Servlet技术就是为了和CGI进行竞争,Servlet是一个特殊的Java程