今天又到又爱又恨的百度面试了(已跪过两次T T),这一次是由学长内推过去的。具体招聘要求如下:
百度xx部xx组急招实习生,要求:
1.熟悉算法与数据结构
2.熟悉python,shell等
3.熟练掌握java或者c++
4.能近期尽快入职
5.计算机相关专业
一面是一名年轻的技术面试官,他居然把我的简历已经打印好了,让我好感倍增啊!首先他让我简单介绍一下自己,这一点由于最近面试太疲乏了,没有好好准备,只简单根据简历描述了一下自己,应该好好整理思路(经历、性格、优势等)。然后就之前在企业实习所做的内容做一个介绍,并且必须边画流程图边解释,这个方式在以后描述自己的项目时值得借鉴。随后问了我之前在课程上做过的项目,偏偏问到了我没有参与很多的项目,然后具体做什么印象都不太深了,项目中用到了几种方法,他问我用的哪几种,我说了原理最有把握的两个,结果被他问到是怎么实现的,函数的输入是什么,输出是什么,表示已忘光。。。因此,一定要check一下自己的简历中自己不熟悉的地方,不能瞬间被问倒。另外,他挑出我所做的Logistics项目中的Logistics回归算法的具体流程,我只记得大致原理了,忘了推导流程。然后,他又问不同的模型输出结果怎样进行融合的问题,我提到投票制方法,之前学的boosting方法没有提出来。那么他又问了不同的机器学习算法的应用场景问题,这个之前大致了解过SVM不太适合大规模数据集的训练等,但是他追问原因时回答不全。项目就问到这儿了,接着,他给我出了一道题目:“每次抛硬币出现正反两面的可能性都是1/2,那么连续出现两次正面的期望是什么”,期望是什么我还记得,但是怎么求都忘了,他提醒了多次但是实在概念都不记得了,这一题花了很长时间,在他一步一步指导下做出来的,但是中间由于有点紧张加上面试官说话太快没有表现出较快的应变能力。正确解法是:设期望为E,那么E等于每个可以掷出连续两次正面的次数乘以其概率的和。考虑第一次和第二次掷出的结果,若第一次结果为“正”,第二次结果为“正”,那么结果是1/4*2;
第一次结果为“正”,第二次结果为“反”,那么前两次都白掷了,那么结果是1/4*(2+E);
第一次结果为“反”,那么第一次白掷了,那么结果是1/2*(1+E);
因此求总和:E=1/4*2+1/4*(2+E)+1/2*(1+E),最后结果E=6。
第二个问题:有一个random()函数可以随机返回0或者1,写一个函数用于随机返回0到5之间的任意一个数。
这个问题起先我纠结在怎样把1/2的概率转换为1/6的概率,想到了多次返回random来解决。但是通过统计多次random所得的和只能解决以2^n为分母的问题,然后再想到二进制,但是用二进制的话两位又少了,然后面试官提醒我两位少了,三位怎么样?随后,我就想到了答案,三位可以用于表示8个数,那么每次掷三次表示一个三位的二进制数,如果这个数小于等于5,那么就返回答案,否则继续掷三次,代码如下:
int random5(){
int sum = 0;
while(true){
for(int i=0;i<3;i++){
if(random()==1) sum+=2^i;
}
if(sum<=5) return sum;
}
}
程序写得应该没有bug,然后他就说等下二面了。
二面面试官是一个中年技术官,也是让我自我介绍一遍(T^T),首先问了我做的机器学习相关的项目,让我挑一个向其介绍,因此我选了和机器学习相关性最强的歧义消解和命名实体识别的项目,实质上是关于将基于规则的聚类和分层聚类相结合对网页进行聚类的方法。详细介绍完了之后,他问了规则是如何得出的,为什么用这样的规则,每条规则具体是怎样?为何不把规则直接转为特征值向量,我回答是因为我的文本特征值向量是统计词频,不适用于规则,然后他问如果将规则转化为比重更大的特征值呢?或者在一篇文章中怎样结合不同部分的信息呢?我给出的答案是将网页中不同的区域给予不同的权重,最后累加起来。然后他就问权重怎么给?我说人工赋值。。。然后他又抛出问题来,给一些特定商品的评论,如何在里面提取有用的关键词,比如化妆品,提取出类似于效果好坏、价格高低等等的关键词。我的回答是首先进行分词,然后根据tf-idf和词频提取出排在前面的词,再进行停用词表或关键词表的删选。面试官直接说我们不接受任何的人工方式,给跪了。。。然后,实在想不出来,面试官非常nice地安慰了我一下,说机器学习懂得的东西还不错了,知识迁移能力挺好的,知道最近我对“挺好”两字多心碎吗。。。接着,他又问会啥编程语言,我说会java、python,java懂最多,然后他本人不太用java,就问了一个问题box是什么,从来不知道这是什么鬼。。。网上查了一下,难道是说swing里面的box容器?“好吧,那就出个算法题喽”让我编一个链表反转的题目。代码如下:
class List{
int val;
List next;
}
class Reverse{
List reverse(List l){
if(l==null) return null;
Stack<List> s = new Stack<List>;
while(l!=null){
s.push(l);
l = l.next;
}
List newlist = s.pop();
List p = newlist;
while(!s.isEmpty()){
p = s.pop();
p.next = p;
}
return newlist;
}
}
代码顺利写出来了,但是面试官也没有任何特殊的感觉,只是说了句,恩恩,就是这样嘛。然后就谈起实习时间的问题了。这个题目我在路上回想了一下,另一种解题方法可能为:设置一个List p保存前面的节点,那么只要从前往后遍历时将当前节点的next换成p就好了,代码如下:
List Reverse(List l){
if(l==null) return null;
List p = l.next;
while(true){
if(p==null) return l;
List q = p.next;
p.next = l;
l = p;
p = q;
}
}
还三种方法直接新建一个list,然后一个一个放进去,第四种方法是:对于一条链表,从第2个节点到第N个节点,依次逐节点插入到第1个节点(head节点)之后,(N-1)次这样的操作结束之后将第1个节点挪到新表的表尾即可。这个解法的另一种方式是从头到尾遍历原链表,每遍历一个结点,将其摘下放在新链表的最前端。注意链表为空和只有一个结点的情况。时间复杂度为O(n)。
// 反转单链表
ListNode * ReverseList(ListNode * pHead)
{
// 如果链表为空或只有一个结点,无需反转,直接返回原链表头指针
if(pHead == NULL || pHead->m_pNext == NULL)
return pHead;
ListNode * pReversedHead = NULL; // 反转后的新链表头指针,初始为NULL
ListNode * pCurrent = pHead;
while(pCurrent != NULL)
{
ListNode * pTemp = pCurrent;
pCurrent = pCurrent->m_pNext;
pTemp->m_pNext = pReversedHead; // 将当前结点摘下,插入新链表的最前端
pReversedHead = pTemp;
}
return pReversedHead;
}
看似简单的问题,解法往往有很多种,但是要找到时间和空间效率最高的解法才能让面试官眼前一亮。
总之,这次面试考察了方方面面,总结来说主要考察了以下几点:
一面:实习和项目经历、机器学习算法、概率论、编程能力;
二面:数据挖掘各个环节处理细节、问题解决能力、学习迁移能力、编程能力。
总结来说,我现阶段仍欠缺的能力是:
【紧急】机器学习算法推导、应用场景;
【紧急】基础概率论;
【重要】数据挖掘流程的各方面问题处理细节;
【重要】算法和编程能力;
得到的经验是:
1.自我介绍要突出最有竞争力的特点;
2.遇到不会的问题坦然承认并且努力拓展思路解决,实在不行就求助面试官;
3.复杂问题能解决尽量解决,搞定是王道,简单问题多思考,优化才出彩。