微软(苏州)面试经历:从入门到入职

年末,微软面试终于告一段落,目前已经签定 Offer Letter,处于背调阶段,不出意外的话,月底就会入职。

在准备面试的过程中,一众大佬的面经给了我很多启发,如今到了薪火相传的时候,希望我的面经对你也能有所帮助。

微软内推,可以发送邮件至 progcz@yeah.net,记得附上简历、心仪的岗位及链接(可以官网搜索)和简单的自我介绍哦。

1 背景

面经,向来是典型的「小马过河」问题。不同的人有着不同的教育和工作经历,装备等级在一定程度上影响着打怪难度,所以你我的背景越相近,面经才越具备参考价值。

1.1 教育经历

2017 年本科毕业于厦门大学测控技术与仪器专业,2020 年硕士毕业于中国科学技术大学控制工程专业。所以,我并非计算机科班出身,更多是靠选修和自学来接触的编程。

1.2 工作经历

2020 年入职字节跳动(上海),担任推荐算法工程师。至于为什么离开字节,我想这应该是下一篇文章的主题了,写在正式离开字节之后。

1.3 面试岗位

微软(苏州),Windows 365 软件开发工程师。

JD 参考:https://careers.microsoft.com/us/en/job/1168023/Windows-365-Software-Engineer

2 简历

如下所示,我并没有套用模板,只是参考了下简历一般需要哪些信息,然后用 Word 最基础的功能,做了一份够简的简历出来。

几点可能有用的建议:

  • 准备中、英两版简历,做在同一个 Word 文件中,导入同一个 PDF 文件中。

  • 保持简洁,重要的信息,尤其是与所面岗位相契合的信息,应该大书特书,占最大的版面。

3 人生问题

开个文档,针对面试中常见的人生问题,把答案提前准备一遍,每个答案都尽量保持在 5 - 10 分钟。

准备的过程,其实也是与自己对话的过程,因此这样做并不是为了弄虚作假,而是为了在被问到的时候,能够从容应对,逻辑清晰地表达内心想法。

比如:

  • 为什么想离开上一家公司?为什么想加入我们这家公司?

  • 简单介绍一下简历上的某个项目?你在其中承担哪一部分的工作?遇到最大的挑战是什么?

  • 未来的职业规划是什么?想成为一个什么样的工程师?

  • 简单介绍一下自己的优缺点?

  • 有什么想问我的吗?

4 算法题目

优先刷下 LeetCode 的 Top Interview Questions,难度在 Medium 及以下即可。

几点可能有用的建议:

  • 每道题目限定 20 分钟,没做出来直接去看 Discuss 里 Most Votes 的答案(如果英文吃力,可以参考 LeetCode All in One 项目),看完之后自己手写一遍。

  • 不要满足于仅一种解法,要问这么几个问题:

    • 现在这个解法,时间复杂度、空间复杂度是多少?

    • 还有没有解法,可以降低时间复杂度,或者降低空间复杂度,或者写法上更简洁?

  • 会讲思路,在写代码前能把解法口头描述出来。

  • 会写测试代码和测试用例,在运行前能考虑到尽量多的 corner case,在运行后能根据 error case 分析代码中的 bug。

    面试过程中,推荐使用 LeetCode 的 Playground 编写代码,相比 Codeshare,Playground 可以在线编译并运行,测试起来非常方便(微软的面试官都非常注重测试)

    比如,我在常见排序算法的 C++ 实现、复杂度和稳定性分析中给出的 Playground

5 面试

简单记录一下我的面试过程,以供参考。

5.1 第一轮面试

常规,聊工作项目,做算法题目。

  • 算法题目:给定一个由 01 组成的矩阵,从左上角出发,只能向右或向下移动,0 表示可以通行,1 表示不可通行,求解到达右下角的不同路径的数量。原题参考:LeetCode 63. Unique Paths II

    解法:动态规划,维护一个同样尺寸的矩阵,矩阵中的每个值代表从左上角到这一格的不同路径的数量,那么容易得到,每个值都是其左侧格(如有)的值与其上方格(如有)的值之和,而该矩阵左上角的值确定为 1,则从左上角开始遍历更新,最终返回右下角的值即可。

    上述解法写完之后,我主动与面试官进一步讲了,如何将空间复杂度从 O(n^2) 优化至 O(n):不需要维护整个矩阵,只需要维护遍历位置的当前行即可。我相信这应该是个加分项。

5.2 第二轮面试(连续三场)

连续三场面试真的很累,一定注意精力管理。

第一场

科大师兄,聊学校,聊毕业论文(这个让我有点意外,我以为社招只会聊工作项目),做算法题目。

  • 算法题目:给定一个 long long 类型的数字,以 long long 类型返回其 reverse 后的数字。相似题目参考:LeetCode 7. Reverse Integer

    解法:题目并不难,主要考察如何处理 corner case,比如负数、结果值溢出、中间值溢出等情况。

第二场

常规,聊工作项目,做算法题目。

  • 算法题目一:给定一个尺寸为 n 的数组,其值均在 [1, n+1] 之间,有且只有一个数字不在这个数组中,找出这个数字。

    解法:题目并不难,实际上简单的数学运算就可以,但是面试官还是问了有没有其他解法,最后写了一个使用两轮异或运算的解法。

  • 算法题目二:给定一个数组和一个目标值,在数组中找到三数之和,使其最接近目标值。原题参考:LeetCode 16. 3Sum Closest

    解法:面试过程中只给了暴力解法,面试结束后才想起来,使用头尾双指针的解法,可以把时间复杂度优化到 O(n^2)

第三场

科大师兄,聊工作项目,问 C++ 题目,做算法题目。

  • C++ 题目:C++ 11 相比于 C++ 98,有什么新特性?智能指针 unique_ptr 是怎么实现资源独占的?

    答案:第一个问题老生常谈,可以参考面试中常见的 C++ 问题汇总;第二个问题,面试过程中猜错了,正确答案是 unique_ptr 使用 = delete 禁用了拷贝构造函数。

  • 算法题目:对于带有随机指针的链表,进行深拷贝。原题参考:LeetCode 138. Copy List with Random Pointer

    解法:第一轮遍历,先不考虑随机指针的拷贝,遍历原链表,构造新链表,在遍历过程中,将新链表节点的随机指针指向原链表节点,同时维护从原链表节点到新链表节点的映射;第二轮遍历,遍历新链表,从新链表节点的随机指针找到原链表节点,再从原链表节点的随机指针找到原链表随机节点,再从映射找到新链表随机节点,赋值给新链表的随机指针即可。

    解法的难度是一方面,测试的难度才是真正考察的点。

    在面试过程中,为了方便测试,我实现了 MyListNode* vec2list(vector<pair<int, int>>& vec); 将测试用例从数组形式构造为链表形式,实现了 bool valid(MyListNode* root_1, MyListNode* root_2); 用来判断两个链表是否互为深拷贝的关系。

    尤其是 valid 这个函数,千算万算还是百密一疏,面试官最后指出了其中可能存在的漏洞。

5.3 第三轮面试

最后一面是跟老板(竟然也是科大师兄)面,邮件里的 PRINCIPAL GROUP SW ENG MGR 格外显眼。

其实能到最后一面,说明候选人在技术方面没什么太大问题,老板考察更多的是,候选人是否具备成为优秀工程师的基本素养。

  • 人生问题:

    1. 为什么想离开字节,为什么想加入微软?

    2. 希望成为一个什么样的工程师?

    3. 在字节有什么样的成长?

    4. 对 Windows 365 产品有什么样的理解?

  • 算法题目:实现 sqrt 运算,计算算术平方根,结果向下取整。原题参考:LeetCode 69. Sqrt(x)

    解法:二分法。

6 致谢