一.前言
Hello 大家好久不见,已经三个月左右没有更新了,那我这三个月在干什么呢?自2023年3月中旬开始到现在五月底这期间接近三个月的时间里,我一直在进行2023暑期实习的投递和面试。这期间投递了包括各大中厂(阿里,腾讯,蚂蚁,美团,携程,拼多多,华为,SHEIN,快手等),外企(PayPal,亚马逊,微软没开),银行金融(微众银行,招商银行信用卡,东方财富,花旗金融,Wind等),其他(联想,吉利,海信,中兴,OPPO)等数十家公司和企业。
从一开始的谨慎投递和筛选企业,到后面的没投投谁和查缺补漏;从一开始的磕磕巴巴,害怕面试和手撕,到后面的谈笑风生,来者不惧;从一开始的提心吊胆,自我怀疑,到后面的释然和豁达;找实习的整个期间是非常煎熬的,至少有三分之二的时间是没有任何音讯和结果的,同时你还要兼顾八股刷题和实验室工作以及论文开题。之前我每天都在担心自己找不到暑期实习从而丧失了秋招的主动权,甚至导致一个失败的人生,尤其是身边的同学都陆续接到Offer的时候;但到现在,要懂得人生的意义和方向有很多,在于你怎么寻找和自我定位,不要把眼光放窄,也不要期待过高。换句话说,失败总是贯穿于人生始终,这才是人生,共勉。
二.背景及结果
1.背景
- 年级:2024届毕业生,也就是要参加今年秋招
- 学历:双非本+某C9硕(保研专硕),均为科班
- 岗位:JAVA 后端开发
- 实习经历:无任何实习经历
- 项目经历:19年国创项目(JAVA)+社区网站(JAVA)+实验室项目(C#)
- 比赛经历:本科打ACM拿过一些小奖,以及数学建模等
- 科研成果:无任何科研成果(自我评价没啥科研天赋)
2.结果
截止到目前为止,博主一共拿到或者进入了最后流程泡池子的企业有五家,情况分别为:
- 美团-美团到家(base北京):两面+HR面,已offer且offer已接。团子是最早给我offer的,部门业务比较核心,也是面试体验最好的公司之一。
- 阿里菜鸟-产品技术部(base杭州):两面+HR面,已oc但最终拒了意向书。阿里菜鸟的实习福利是真的好,各种高薪资高房补高餐补,是我所有offer里最好的。
- 携程-机票(base上海):两面+HR面,已oc但最终拒了意向。携程业务不错,是我所有offer里最wlb的。
- 拼多多-服务端研发(base上海):两面+主管面+HR面,已oc但最终拒了意向。拼多多对实习生还是不错的,给的日薪资是所有offer里最高的,而且是面试体验最好的公司之二,实习具体部门岗位应该是去了再分配。
- 华为-华为云(base杭州):技术面+主管面,已过泡池子。线下面试,一天直通,线下管饭,值得好评。HR给的评价还不错,就是等待周期比较长。华子应该是所有企业里唯一一个笔试成绩可以延续到秋招用的。
博主最终在权衡之下,选择了去团子,目前人在北京,过两天入职(等我更新实习日常haha)🤩为什么选择团子呢?一方面是团子offer接的比较早,不想毁约;另一方面,团子秋招背书不错,部门业务比较核心,转正率高,开的薪资也还可以(中规中矩);然后北京离家近一点,我也没去过北京玩。(但是北京租房真贵啊,火大!
三.面试历程
1.美团
(1)简历投递:2023-03-25
团子暑期实习开得很早,在二月底三月初美团实习面试就已经进行的如火如荼了,我这已经算是投的很晚了,赶上了末班车。早投就会早两周拿offer,而且被捞的概率也会更大。建议大家多关注,早投早准备。
- 投递岗位:转正实习-后端开发工程师(JAVA)
- 事业群:都喜欢(都喜欢的话会被不同的部门捞,有明确意向群的话会流程快一点)
- 意向地:北京、上海(不选的意向地部门不会捞你,非北上打九折)
(2)笔试: 2023-04-01
团子没有各种测评,直接发笔试,这个好评。一共五道算法编程题,没有选择。个人感觉题目比较简单,相比前几批来说难度有所下降,博主做了 4.0/5.0。
- 第一题:字符串模拟 100%
题意:给定一行n个数字,数字序列之间默认使用加法连接。接下来给出m次操作,每次操作只改变一个+符号为其他运算符,请你输出每次改变符号后的结果,结果保留一位小数。
思路:题目很简单,直接模拟运算就行,每次只改变一个运算位置的符号,其他都是+号。主要保留精度,用Python的同学好像一直没法100%,我这里用C++一遍过。
- 第二题:贪心+排序 100%
题意:输入一行数字,可以对其任意两个数字的位置交换任意次,请你输出最小的相邻前一个数字减去后一个数字的差的绝对值的和。
思路:因为可以交换任意次,当所有相邻数字最接近时差值绝对值和最小,因此直接贪心排序,遍历求差值的绝对值之和即可。
- 第三题:线段树模板/树状数组 100%
题意:输入数字n代表有多少个收藏夹(收藏夹默认欣赏值为0),数字m代表对收藏夹进行了m次操作:
- 0 xi yi:0操作表示将xi位置的收藏夹欣赏值更新为yi
- 1 xi yi:1操作表示查询[xi,yi]的欣赏值区间总和
思路:单点更新,区间查询,典型的线段树/树状数组板子题,用前缀和会超时。
void UpdateTree(int P,int Val,int L,int R,int root){if(L >= R){files[P] = Val;sum[root] = Val;return;}int mid = (L+R)>>1;if(P <= mid)UpdateTree(P,Val,L,mid,root<<1);else UpdateTree(P,Val,mid+1,R,root<<1|1);sum[root] = sum[root<<1] + sum[root<<1|1];return;
}int QueryTree(int l,int r,int L,int R,int root){if(l <= L && r >= R){return sum[root];}int mid = (L + R)>>1;int ans = 0;if(l <= mid)ans += QueryTree(l,mid,L,mid,root<<1);if(r > mid)ans += QueryTree(mid+1,r,mid+1,R,root<<1|1);return ans;
}int main()
{int n,m;memset(sum,0,sizeof(sum));scanf("%d%d",&n,&m);for(int i = 0;i < m;i++){scanf("%d",&op[i]);}for(int i = 0;i < m;i++){scanf("%d",&x[i]);}for(int i = 0;i < m;i++){scanf("%d",&y[i]);}bool flag = false;for(int i = 0;i < m;i++){if(op[i] == 0){UpdateTree(x[i],y[i],1,n,1);}else{int res = QueryTree(x[i],y[i],1,n,1);if(flag)printf(" ");else flag = true;printf("%d",res);}}printf("\n");return 0;
}
- 第四题:DP 0%
题意:用魔力值可以凭空生成水,也可以从旁边的杯子转移水,要求装满指定杯子消耗最少魔法值。
思路:看了一会直接跳了, 有人说数据比较弱,直接暴力也可以过100%
- 第五题:树上DFS 100%
题意:计算二叉树根节点的权重。如果树上节点没有子节点,则权重为1;如果有子节点,且它是绿色,则它的权重为子节点权重的和,它是红色,则它的权重为子节点权重的异或。
思路:没什么好说的,直接 DFS 即可
struct Node{int color;vector<int> childs;
}node[maxn];int DFS(int root){if(node[root].childs.size() == 0)return 1;if(node[root].color == 1){return DFS(node[root].childs[0]) + DFS(node[root].childs[1]);}else{return DFS(node[root].childs[0]) ^ DFS(node[root].childs[1]);}
}int main()
{int n;scanf("%d",&n);for(int i = 2;i <= n ;i++){int p;scanf("%d",&p);node[p].childs.push_back(i);}for(int i = 1;i <= n;i++){scanf("%d",&node[i].color);}int ans = DFS(1);printf("%d\n",ans);return 0;
}
(3)一面: 2023-04-10 挂
一面部门是美团买菜(base 北京),时长大概一小时左右,面试形式是互相介绍+问项目和八股+手撕+闲聊。一面时状态有些不好,具体面试题目已经不太记得了。问的八股多为场景题,比如Redis缓存雪崩/击穿和解决方法、Redis大Key和热Key问题如何处理、分布式锁等等,感觉问的不是很常规答得一坨。
最后手撕题目为 Leetcode129. 求根节点到叶节点数字之和(medium),手撕状态不好而且很紧张,在写DFS的时候自底向上去写的,结果有很多边界条件没有处理好,导致做了很长时间被中断了,没有再写下去,面完之后就感觉自己寄了,结果4.14日发了感谢信,等捞。题目复盘如下:
题意:给你一个二叉树的根节点
root
,树中每个节点都存放有一个0
到9
之间的数字。每条从根节点到叶节点的路径都代表一个数字,要求计算从根节点到叶节点生成的 所有数字之和 。思路:自顶向下DFS即可,对路径上的数字进行十进制累加,如果是叶节点则直接返回结果;如果是非叶节点,则返回所有子节点的和。
class Solution {
public:int dfs(TreeNode* root,int preSum){if(root == nullptr)return 0;int res = preSum*10 + root->val;if(root->left==nullptr && root->right==nullptr){return res;}return dfs(root->left,res) + dfs(root->right,res);}int sumNumbers(TreeNode* root) {return dfs(root,0);}
};
(4)一面: 2023-04-24 过
4.19日被捞了,在4.24日进行第二次的一面。被捞一面的部门是美团到家(base北京),时长大概一小时左右,面试官是位小姐姐。面试形式仍然是互相介绍+问项目和八股+手撕+闲聊。该面体验较好,小姐姐很温柔!八股问的比较基础,没答上来的有两个:
- Redis集群脑裂以及解决方法
- MySQL B+树可以存放多少数据,从数据页角度来说
手撕题目是一道比较基础的DFS+回溯 Leetcode40. 组合总和 II(medium),当时的思路为直接暴力枚举所有可能的组合,记录答案即可,但有些边界和细节处理起来可能有点麻烦。题目复盘如下:
题意:给出一组候选数 c 和一个目标数 t ,找出候选数中起来和等于 t 的所有组合。c 中的每个数字在一个组合中只能使用一次。注意:
1. 题目中所有的数字(包括目标数 t )都是正整数
2. 组合中的数字要按非递减排序
3. 结果中不能包含重复的组合
4. 组合之间的排序按照索引从小到大依次比较,小的排在前面,如果索引相同的情况下数值相同,则比较下一个索引。要求:空间复杂度O(n) , 时间复杂度O(2^n)
思路:通过DFS+回溯的方式,枚举每个位置的数字选或者不选,统计最后的候选者集合即可。但是要注意结果不能包含重复集合,例如“当 candidates=[2,2],target=2时”,普通算法会将列表 [2] 放入答案两次。但是注意每个结果集合是可以包含重复数字出现的,所以对于相同数字来说,核心的处理方式为分层级去整合:
集合避免重复思想,这个方法最重要的作用是,可以让同一层级,不出现相同的元素。即: 例11/ \2 2 这种情况不会发生 但是却允许了不同层级之间的重复即:/ \5 5 例21/2 这种情况确是允许的/2
其实现方式有两种思路:
- 一是:对数组排序去重,将相同数字预处理统计数量;在枚举时,一次性判断该数字的枚举结果。比如数字x出现了y次,则在枚举数字x时一次性判断结果集中出现0次、1次、....的递归结果
- 二是:在递归过程中,分层级处理,同一递归层级上必须选择一个数字,且该数字不可重复选择;然后不同层级上可以选择相同数字,预先排序进行剪枝
class Solution {
public:int len;vector<int> res;vector<vector<int>> ans;void dfs(int index,int rest,vector<int>& candidates){if(rest == 0){ans.push_back(res);return;}//当前递归层级选择放置元素 + 剪枝for(int i = index;i < len && candidates[index] <= rest;i++){//同一层级过滤重复数字if(i > index && candidates[i] == candidates[i-1])continue;//选择数字+进入下一层级+回溯res.push_back(candidates[i]);dfs(i+1,rest-candidates[i],candidates);res.pop_back();}return;}vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {len = candidates.size();sort(candidates.begin(),candidates.end());dfs(0,target,candidates);return ans;}
};
(5)二面: 2023-04-26 过
4.19号一面结束之后,自我感觉表现很好希望很大,果然在4.26号安排了二面。二面面试官和一面面试官是一个部门,应该是leader级别,时长也是一小时左右。面试形式仍然是介绍+问项目+手撕+闲聊。二面和一面的区别在于,八股基础问的比较少,大部分时间在聊项目、设计和架构,聊优化,聊一些拓展的东西。面试官很温柔,会引导,学会了不少东西。
手撕是一道判断完全二叉树的题 Leetcode958. 二叉树的完全性检验(medium),当时思路是针对完全二叉树非叶子层都满层的性质直接用层序遍历的方式去判断,写的时候对最后一层叶子层的有些边界条件(最后一层的节点都必须自左向右依次排列)没考虑清楚debug了很长时间,面试官一直在耐心等做出来,现题目复盘如下:
题意:即给定一个二叉树,确定他是否是一个完全二叉树。完全二叉树的定义:若二叉树的深度为 h,除第 h 层外,其它各层的结点数都达到最大个数,第 h 层所有的叶子结点都连续集中在最左边,这就是完全二叉树。(第 h 层可能包含 [1~2h] 个节点)
思路:
- 方法一:使用层序遍历的特点,并根据完全二叉树的性质和每层的数量特点做判断。若一个节点的孩子节点不合法(比如只有右孩子没有左孩子)则一定false;若一个节点的孩子节点合法但不满,则后续的所有节点一定不能再有孩子,以此类推。
- 方法二:若一个结点左右孩子或右孩子为空,后面所有结点的孩子必然全为空,则满足完全二叉树,否则不是完全二叉树。那么,我们可以采用二叉树的层序遍历方法,将所有结点(包括空节点)全部入队,遇到第一个孩子为空的节点则检查其后面是否还有非空的节点,有的话不是完全二叉树,没有则是完全二叉树。
class Solution {
public:bool isCompleteTree(TreeNode* root) {queue<TreeNode*> que;que.push(root);bool isNull = false;while(!que.empty()){TreeNode* node = que.front();que.pop();if(node == nullptr){isNull = true;continue;}if(isNull)return false;que.push(node->left);que.push(node->right);}return true;}
};
撕完之后,也就是二面最后一部分携带了一些HR问题,包括一些生活上的事情,比如哪里人、未来规划、意向城市、兴趣爱好、玩啥游戏、目前offer等等,总之整个过程还是比较愉快和轻松的。
(5)offer: 2023-05-08 接受offer
二面完之后正好赶上五一劳动节放假,一般来说二面完三天左右就会出结果,我还以为凉了。五一假期之后5.08收到了邮件offer,并没有HR面。咨询之后确实是正常流程,美团开发确实没有HR面,二面完之后直接出结果到邮箱。考虑再三还是接了第一份来之不易的offer,感谢认可!xdm 团子见!
2.阿里巴巴-菜鸟
(1)简历投递:2023-03-29
阿里巴巴的简历投递一次最多可以投递三个不同的岗位方向,按照志愿顺序进行筛选,笔试是通用的。整个阿里巴巴集团包括淘天、阿里云、饿了么本地生活、菜鸟、飞猪、盒马、灵犀互娱等都用的一套招聘系统,所以投递之前先考虑好部门方向顺序,只有上一个挂了才能流转到下一志愿(不过最近阿里业务调整,各部门独立经营,不知道明年如何)。因为三月中旬刚开始投递了蚂蚁集团,八股算法都没怎么看,挂的飞快。隔了很久才决定再投一次阿里,博主的投递顺序是菜鸟、本地生活、CTO线(再给我一次机会的话,我会投大淘宝、本地生活、菜鸟)。
(2)一面: 2023-04-23 过
阿里是先电话一面或者叫简历面,一面通过之后才会发笔试邀请,而且一面不会预先发邮件给你,直接电话突袭,所以一定要留意手机。
一面是电话面,主要内容就是了解一下你的个人信息,简历上的项目介绍,会问一些基本的八股(MySQL 索引相关、分页、Redis 缓存过期策略等),然后是优缺点、兴趣爱好什么的。整个过程大概30分钟,没有手撕环节,反馈也很快,过了之后当天就会给你发笔试链接、素质测评链接。主要是投简历到一面的时间太长了,差不多等了快一个月才被捞出来。。
(3)笔试: 2023-04-27
一面过后,统一参加阿里4.27号的最后一场开发岗笔试,阿里的笔试难度在各大厂中处于中等偏上吧,后端开发包括选择题(单选+多选)+算法编程两类题型。选择应该是包括数据库、操作系统、计算机网络、数据结构的408类型题目,都是基本知识难度不大。编程题三道,具体题目等我后面完善一下,只做了第一二题:第一题是个模拟/构造,但是题意比较难懂,按自己的理解做了半天只有50%左右,最后剩了二十分钟看了眼第二题,时间截止之前一遍过100%,最后结果是1.5/3.0,做完感觉自己寄了。
- 第一题:构造/模拟 50%
题意:给一个n×m 0/1矩阵,要求恰好放置k个大小为2的联通块,剩下 m×n−2k个大小为1的联通块,输出任意一种方案, n ≤ 500, m ≤ 500。
思路:当大小为2的连通图能够刚好填满a行或者b列的时候才有答案,否则一定会出现一个大小至少为3的连通块。结论就是k要么是n的倍数,要么是m的倍数,否则无解。剩下的就是模拟下是横着放还是竖着放。
- 第二题: 思维 100%
题意:对于一个序列,ai ≠ ai− 1 并且 ai = ai − 2 那么它是一个长城序列,求长度为n的的序列中有多少个长城子序列(连续)。n ≤ 2∗10^5。
思路:结论是两个极长的长城最多只有收尾会重叠一位。那么我们从前往后依次找出所有极长的长城序列后用等差数列求和即可。由于最多只会重叠一位,所以复杂度是线性的。
- 第三题:最小生成树 0%
题意:给定n个点,m条边的无向带权连通图,最多可以删去k条边,要求删完边后图依旧是联通的,求删除的边权和最大是多少。n,m,k≤10^5。
思路:尽量去掉权值大的边等价于尽量留下权值较小的边并且保持联通,直接转化为求最小生成树的问题。如果 m−n+1≥k,那我们就从小到大继续加边。然后剩下的边权和就是答案,实现方式上可以通过并查集方式的Kruskal算法。思路其实很简单,被卡的没时间看了
(4)二面: 2023-05-08 过
笔试完以为自己寄了,结果过两天就收到了二面的面试邀请,面试平台是在钉钉会议。二面的面试官感觉比较忙,中间推迟了一会,整个面试过程只有20分钟,无手撕环节。
面试内容包括自我介绍、部门介绍(问对本部门的了解,介绍比较详细)、介绍简历上最有把握的一个项目(以及一些项目的优化、场景)、闲聊+反问,八股没怎么问,面试官还是很有压迫感的,多次打断八股吟唱施法(好了好了,我知道你会这个了 bushi
(5)HR面: 2023-05-23 过
二面完接着就收到了5.11号的HR面,仍然是在钉钉会议。但是由于HR复阳了,会议就推迟到了5.13号,然后13号面试官还是身体不适,就一直推迟到了5.23号这个很晚的节点才进行的HR面。
HR面共13分钟,面试内容包括自我介绍、对部门的了解以及部门和业务介绍、在项目开发中遇到的困难以及怎么解决的、个人的优缺点、未来职业规划(重在学习实践、经验积累、工程师->架构师发展)、未来意向城市+反问,整个过程以聊天为主,不用紧张。
(6)offer: 2023-05-24 已拒
HR面完第二天就收到了录用offer,流程还是很快的。offer当天会有电话oc,告诉你实习生的福利待遇,包括实习薪资、房补、餐补,给四五天的时间考虑是否接受。阿里的实习生福利是真滴好,不过最终考虑再三还是拒了这个offer,我们有缘秋招再见!(不过插一句,阿里菜鸟的整个面试过程都没有手撕环节,这个感觉还是可以的~
3.拼多多
(1)简历投递:2023-04-02
拼多多招聘投递官网比较简单,投递时可以选择两个志愿(一志愿优先),Java开发只有服务端研发实习生一个岗位,我就只投了一个志愿。意向城市或面试站点我选择的上海且不接受调剂。拼多多开的也很早,我投的时间也很晚了,而且pdd面试流程比较长,是三面技术面(第三面为主管技术面)+HR面,而且面试时间一般都安排在周末进行(面试官都比较忙QAQ),所以每个流程之间大概都间隔一周左右。
(2)个性测评(简历过筛):2023-04-15
等了差不多两周左右,周六收到了pdd的测评和笔试通知,说明简历被捞起来过筛了。个性测评随时可以做,会有一个专属的链接地址,只要在有效期内完成即可。主要内容就是性格测试、服从性测试等,题型可以参考北森题库。笔试的话是在固定日期分批统一进行的,一般在收到测评邮件的两三天后。
(3)笔试:2023-04-18
博主参与的4.18号的笔试属于拼多多第三批后端开发的笔试,笔试一共四道编程算法题,考试时长为120min,考试平台是牛客。整个笔试难度感觉不是很大,也有人说介于第一批和第二批难度之间,博主最后做了 3.2/4.0 。现在对笔试题目做一下复盘:
- 第一题:数学+边界判断 100%
题意:给定值X、Y,算出同时满足下列3个条件的所有菱形个数:
- 菱形存在正面积
- 菱形每个顶点的横坐标x_i为[0,X]的整数,纵坐标y_i同理
- 菱形的对角线平行于坐标轴
思路:题意很简单,在定义上只要知道菱形的特点是四条边相等的平行四边形且对角线相互垂直平分(注意对角线不一定相等),题目要求对角线平行于坐标轴且顶点坐标均为整数,因此直接暴力枚举左下顶点并进行合法性判断即可。
int main()
{int X,Y;scanf("%d%d",&X,&Y);int sum = 0;for(int i = 1;i <= X;i++){for(int j = 1;j <= Y;j++){sum += min(Y - j,j) * min(X - i,i);}}printf("%d\n",sum);return 0;
}
- 第二题: 暴力 100%
题意:二维平面上有n个点,他们的横纵坐标均为正整数,且没有两个点有相同的横坐标,把所有点从左到右依次通过直线连接,形成的折线与横坐标可形成一片投影区间。 若最多只能交换其中两点的纵坐标值,问如何交换可得到最大的投影面积。
思路:因为只能交换其中两点的纵坐标,而横坐标保持不变,交换后会影响该两点左右两侧共四个小阴影梯形的面积,因此直接暴力判断交换位置的最大增量面积即可,处理一下遍历条件要不会超时。
struct Node{int x,y;bool operator < (const Node& another)const{return x < another.x;}
}node[maxn];int main()
{int n;scanf("%d",&n);for(int i = 0;i < n;i++){scanf("%d%d",&node[i].x,&node[i].y);}sort(node,node+n);int ans = 0;int ans_x = 0,ans_y = 0;for(int i = 0;i < n;i++){for(int j = i+1;j < n;j++){int ori_i = (i > 0?((node[i-1].y + node[i].y)*(node[i].x - node[i-1].x)):0) + (node[i+1].y + node[i].y)*(node[i+1].x - node[i].x);int ori_j = (j < n-1?((node[j+1].y + node[j].y)*(node[j+1].x - node[j].x)):0) + (node[j].y + node[j-1].y)*(node[j].x - node[j-1].x);int new_i = (i > 0?((node[i-1].y + node[j].y)*(node[i].x - node[i-1].x)):0) + (node[i+1].y + node[j].y)*(node[i+1].x - node[i].x);int new_j = (j < n-1?((node[j+1].y + node[i].y)*(node[j+1].x - node[j].x)):0) + (node[i].y + node[j-1].y)*(node[j].x - node[j-1].x);int res = new_i + new_j - ori_i - ori_j;if(res > ans){ans = res;ans_x = i;ans_y = j;}}}if(ans > 0)printf("%d %d\n",node[ans_x].x,node[ans_y].x);else printf("-1\n");return 0;
}
- 第三题:模拟 20%
题意: 多多是一个服务端开发,线上服务需要维护一堆配置,为了效果,同一个配置对于不同的人群可能值不一样。比如下面表格,根据城市和是否是IT工程师维度的不同,取值不一样。多多想把线上参数都像上面合并一下,已知一个参数的配置列表,以及每个维度的所有取值,多多想知道这个参数配置最少能简化成多少行。最终输出一个整数代表配置最少能简化的行数。
思路:题意需要理解一下,就是给你一个多行多列的配置表,对于有些配置列的剩余列参数都是一样的,因此可以进行合并,让你输出最少能简化的行数。第一感觉是个模拟,输入也比较复杂,由于时间不够了,只做了最简单的判断,拿了20%。
- 第四题:博弈 100%
题意:多多君和皮皮虾在海外大探险中发现了一处宝藏一个装满了金币的宝箱宝箱中有N枚金币,怎么分配金币却成为了大麻烦,于是多多君和皮皮虾约定了以下的规则:
- 多多君和皮皮虾轮流进行金币的选取,由多多君先选
- 当金币数量是偶数时,可以选择拿走一半的金币,也可以只拿走一枚金币
- 当金币数量是奇数时,只能拿走一枚金币
多多君和皮皮虾两人都会选择自己能拿走最多金币的策略,请问两人分别可以拿走多少金币。
思路:经典博弈论问题,已知两人策略最优,问博弈结果。其实这种题只要找到二者的最优博弈策略即可模拟得到结果。分析可得两人的最优策略为:
- 若当前为奇数,则只能取一个;
- 若当前为偶数,则判断取一半之后是剩下偶数还是奇数,若剩下偶数则选择只取一个,否则取一半。这样可以让对方拿的尽可能少,让对方的选择尽可能少。
注意 2 是个特例,需要特判一下。代码找不到了,就先这样吧写起来不难。
(4)一面: 2023-04-22 过
笔试之后在4.19号收到一个登记表,就说明过了笔试被捞进了面试。一面约在 4.22 号下午,面试地址是拼多多自己的面试平台,面试时长大概一小时左右,拼多多的面试是我面试体验最好的公司之一,面试官都很温柔、耐心、平易近人,只能说nice。由于时间有些长了,面试内容记不太清楚了。面试形式仍然是自我介绍+项目+基础八股+手撕+反问的流程,问的问题都比较基础,没有特别离谱的。
其中,手撕题目为 Leetcode45. 跳跃游戏 II(medium) 。看到这个题目的时候面试官引导着用DP去做,最后也是用DP去写的,只不过写了个二重循环(时间复杂度比较高),看起来并没有达到面试官的预期要求。注意:拼多多的面试平台只能写代码没办法去跑case,只能由面试官在后台给你跑,因此大部分手撕都是白板写代码讲思路,一般并不会像美团一样要求去跑过样例100%。题目复盘如下:
题意:给定一个长度为 n 的 0 索引整数数组 nums。初始位置为 nums[0]。每个元素 nums[i] 表示从索引 i 向前跳转的最大长度。换句话说,如果你在 nums[i] 处,你可以跳转到任意 nums[i + j] 处。请返回恰好到达 nums[n - 1] 的最小跳跃次数。
思路:和跳跃游戏I一样,只不过加了一个求跳跃次数的限制。其实在I的基础上,当我们去更新最大跳跃范围时就意味着必须进行一次跳跃,我们在这时累积跳跃次数即可,不用DP去做。
class Solution {
public:int jump(vector<int>& nums) {int len = nums.size();int i = 0,j = 0;int ans = 0;while(i <= j && j < len-1){int index = i;ans++;for(int k = i;k <= j;k++){index = max(index,k + nums[k]);}i = j+1;j = index;}if(i > j || j < len-1)return -1;return ans;}
};
然后是反问环节,询问面试官的部门业务,面试官并没有告知。因为拼多多其实面试是随机分配的部门面试官,你面试的部门并不一定会成为你实习的部门,最终的实习生岗位分配是在入职时统一根据业务需要和面试情况进行分配的。最后,一面自我感觉中规中矩吧,基本八股和项目都答得不错,手撕虽然没有达到面试官的预期解法,但也是按照引导把思路写出来了(手撕写出来 > 全部通过test case > 实现标准解法 > 代码风格),进下一面希望还是蛮大的。
(5)二面: 2023-05-06 过
一面完大概两周左右,收到了二面的邀请。不知道一二面是否为平行面。二面仍然是自我介绍+项目+基础八股+手撕+反问的流程,相比一面来说八股问的比较少而侧重于项目的设计、细节、难点和亮点,整个过程大概也是一个小时。手撕题目为:
题意:现有一个长度为n的有序数组序列,并给你一个数值v,请在数组序列中寻找k个值,使其与数值v的差值的绝对值之和是最小的。
思路:注意到数组序列有序,且差值的绝对值之和最小即找k个数值上最接近v的数,因此我们只需要通过二分查找的方式找到第一个最接近v的数组位置,然后维护两个指针向左右方向扩展,直到找到k个最接近的数组值然后计算求和即可。
手撕不是很难,面试官要求手写二分查找函数。二面自我感觉良好,接下来就是等待三面也就是主管面。
(6)三面: 2023-05-10 过
二面之后两天左右就收到了三面的邀请,邮件会在标题上标明主管面。三面约在5.10号下午四点,不过前一天由于主管较忙,临时改到了5.10号下午的五点,也就是推迟了一个小时,也重新发了邀约邮件。主管面因人而异吧,有的人仍然会以技术为主,包括八股和手撕(比如部分拼多多主管面比较喜欢考手撕大数加法、乘法),有的人就直接以闲聊为主。
我的主管面时长大概一个小时左右,面试内容包括自我介绍+挖项目+一些拓展性的闲聊,无手撕环节也没有八股问答,感觉还是比较轻松的。在拓展性的闲聊方面花的时间比较多,主要聊了一些自己项目的架构设计、缺点以及优化方向,聊了一些最近看过的编程书籍、技术发展,我推荐了设计模式的Head First,然后就又开始聊设计模式相关的话题。整个过程主要都是面试官在讲我在听,听得出来不愧是主管,技术和经验积累非常深厚,像是听了一堂课,也算学到了不少东西。中间由于网络问题我还掉了一次线,手忙脚乱的让面试官等了一会,耽误了大概有个五六分钟。
(7)HR面: 2023-05-19 过
三面之后第二天也就是5.12号就收到了HR面的邀请,看来反馈是很迅速的。HR面约在了5.19号,大概一周之后。HR面大概20分钟,面试内容包括自我介绍、问了意向发展城市和预期薪资、介绍了实习的一些要求(比如出勤时长必须78月满勤),问了目前还拿到了哪里的offer(说了美团,以及一些在最终流程),然后最后聊了一些兴趣爱好、询问能否接受pdd的工作作息等等。
(8)offer: 2023-05-26 已拒
HR面一周之后,5.26号收到了拼多多的offer邮件,offer没有电话oc只有邮件。整个拼多多的面试流程时间跨度还是蛮长的,拼多多工作地点在上海,具体业务部门岗位暂未分配。房补合并到日薪资里一起发放,所以拼多多的日薪是最高的,福利包括上班的免费工作餐等。拼多多对实习生还是挺好的,转正率高,开的也不少。但是offer时间太晚了,期间也是在美团、菜鸟、拼多多之间纠结,最终还是拒绝了拼多多,有缘秋招再见!
4.携程
携程只有电话oc,不接的话就没有邮件offer,所以就放一个最终的英语测评邮件吧~
(1)简历投递:2023-03-29
携程其实开得很早,我是第三批投递的实习生,第一二批暑期实习在三月中上旬是统一去上海总部线下面试的(不报销,但线下offer会发的多),本来以为携程已经招的差不多没什么hc了,但在三月下旬公众号又发了一波招聘通知,所以抱着都投投试试的心理在官网投递了简历。携程应该算是所有互联网中大厂中最wlb(work life balance)的,甚至很多组也在推行每周居家办公的作息制,而且实习生各种活动和团建都很多,暑期实习转正率也不错,槽点就是实习生薪资在上海来说的话还是有点低的,虽然有房补。
(2)专业笔试:2023-04-15
简历投递大概两周之后才接到了笔试,笔试平台为牛客的nowcoder,笔试题型内容为四道编程题,时间为两个小时,可以使用本地IDE。在所有参与笔试中,携程的题目是最简单的(不知道前两批难度如何),大部分暴力可过,博主也是全部 AK 4.0/4.0,现将题目复盘如下:
- 第一题:签到 模拟 100%
题意:给出一个n行m列的字符矩阵,计算出有多少个2*2的子矩阵中同时含有'y', 'o', 'u'
思路:题目数量级很小,所以直接暴力枚举每个2*2子矩阵的左上角位置,然后定义三个变量统计you是否出现,最后做个判断并累加即可
int dx[] = {0,1,0,1};
int dy[] = {0,0,1,1};
unordered_map<char,bool> judge;
int main()
{int n,m;scanf("%d%d",&n,&m);for(int i = 0;i < n;i++){cin>>str[i];}int sum = 0;for(int i = 0;i < n;i++){for(int j = 0;j < m;j++){if(i+1 >= n || j + 1 >=m)continue;//(i,j) (i+1,j) (i,j+1) (i+1,j+1)judge.clear();for(int k = 0;k < 4;k++){int kx = i + dx[k];int ky = j + dy[k];char ch = str[kx][ky];if(ch == 'y' || ch == 'o' || ch == 'u'){judge[ch] = true;}}if(judge['y'] && judge['o'] && judge['u']){sum++;}}}printf("%d\n",sum);return 0;
}
- 第二题:贪心/枚举 100%
题意:给出一个正整数n,现在要找到两个正整数a,b,使得a+b=n,并且a和b的最小公倍数是最大的。
思路:已知 a+b=n,则 b=n-a(a>=1&&a<=n/2,枚举不重复),则原式等价于max a*(n-a)/gcd(a,n-a) , 现有两个思路(注意开long long):
- 暴力枚举每一对a+b,直到
gcd(a,b) = 1
- 把 n 拆成两个值最接近的质数即可
typedef long long int LL;
LL gcd(LL a,LL b){return b==0?a:gcd(b,a%b);
}int main()
{int T;scanf("%d",&T);while(T--){LL n;scanf("%lld",&n);for(LL i = n/2;i >= 1;i--){LL j = n - i;if(gcd(i,j)==1){printf("%lld %lld\n",i,j);break;}}}return 0;
}
- 第三题:dfs 100%
题意:有一颗n个节点的树,每个节点值为0或1,然后给你一个范围 [l,r] ,判断树上有多少二进制路径值在这个范围内
思路:题目数量级较小,直接考虑暴力。dfs枚举每个节点做为二进制起始形成的值满足条件的路径,注意开long long
char val[maxn];
LL l,r,ans;
vector<vector<int>> to;void dfs(int node,LL preSum,int preNode){preSum = preSum*2 + (val[node-1] - '0');if(preSum > r){return;}else if(preSum >= l && preSum <= r && preNode != -1){ans++;}int len = to[node].size();for(int i = 0;i < len;i++){if(to[node][i] != preNode){dfs(to[node][i],preSum,node);}}return;
}int main()
{int n;scanf("%d%lld%lld",&n,&l,&r);cin>>val;to.push_back(vector<int>());for(int i = 1;i <= n;i++){to.push_back(vector<int>());}for(int i = 1;i < n;i++){int s,e;scanf("%d%d",&s,&e);to[s].push_back(e);to[e].push_back(s);}ans = 0;for(int i = 1;i <= n;i++){dfs(i,0,-1);}printf("%lld\n",ans);return 0;
}
- 第四题:思维 100%
题意:现有一个字符串仅由“0”和“1”组成,该字符串的表示方式为一个数组,每个数组值表示该段连续0或1字符的个数,比如第一个元素a1的值表示连接a1个“1”,第二个元素a2表示连接a2个“0”,以此类推。数组2301表示字符串“110000”,求这样的字符串有多少非空回文子串,答案要对1e9+7取模。
思路:可以分段去考虑,首先单个段肯定会构成回文串,其回文串贡献个数为 n*(n+1)/2;然后考虑多个段之间,肯定为奇数个段产生的结果,可以在
"0段"或者"1段"
的左右两侧添加相同数量的不同元素
构成新的回文串,如果左右两侧段的个数相同,则考虑继续扩展, 直到边界或者左右个数不同。在实现上可以采用以中心拓展、或者以边界中心收缩的方式
#define mod 1000000007
typedef long long int LL;
const int maxn = 1000 + 7;
LL nums[maxn];int main()
{int n;scanf("%d",&n);for(int i = 0;i < n;i++){scanf("%lld",&nums[i]);}LL ans = 0;for(int i = 0;i < n;i++){if(nums[i]%2==0){ans = (ans%mod + ((nums[i]/2)*(nums[i]+1))%mod)%mod;}else{ans = (ans%mod + (nums[i]*((nums[i]+1)/2))%mod)%mod;}int st = i%2==0?0:1;for(int j = st;j < i;j+=2){int p = j+1,q = i-1;while(p <= q){if(nums[p] == nums[q]){p++;q--;}else{break;}}if(p > q)ans = (ans%mod + min(nums[j],nums[i])%mod)%mod;}}printf("%lld\n",ans);return 0;
}
(3)一面:2023-04-26
笔试完大概十天左右,接到了携程的一面邮件邀请,还是会比较惊讶的(我以为携程实习0hc了)。携程的面试是自主预约制,可以在一定时间范围内自主选择面试时间,这点比较友好,每个时间段都有固定数量的面试hc可以抢占,我选了最近的一天。
一面面试时长45分钟左右,部门是机票,面试平台是腾讯会议,代码共享屏幕白板手撕。面试内容包括八股+项目+手撕,没有自我介绍和部门介绍,上来直接就问问题了,个人感觉很仓促加上携程到现在也没有自己的面试系统,面试体验并不是很好。由于时间有点久远了,具体的面试内容已经记不太清楚了,八股也算都比较常规和基础,手撕题目给的是 Leetcode5. 最长回文子串 ,要求共享屏幕使用本地IDE编写,面试官引导着使用DP去做,题目复盘如下:
题意:给你一个字符串
s
,找到s
中最长的任意一个回文子串。如果字符串的反序与原始字符串相同,则该字符串称为回文字符串。思路:本题可以使用DP或者中心拓展的方法去做,这里解释DP的思路。若一个字符串s为回文串,则它去除相同长度头尾的子串也一定是回文串,因此我们可以反过来去转移状态。使用 bool dp[i][j] 表示字符串中 s[i...j] 是否是一个回文串,则 dp[i-1][j+1] = (s[i-1] == s[j+1])?dp[i][j]:false
class Solution {
public:const static int maxn = 1000 + 7;bool dp[maxn][maxn];string longestPalindrome(string s) {int len = s.size();memset(dp,0,sizeof(dp));for(int i = 0;i < len;i++){dp[i][i] = 1;}int ans_pos = 0,ans_len = 1;for(int i = 2;i <= len;i++){for(int j = 0;j + i - 1 < len;j++){int st = j+1,ed = j+i-2;if(s[j] == s[j+i-1] && (st > ed || dp[st][ed])){dp[j][j+i-1] = 1;ans_pos = j;ans_len = i;} }}return s.substr(ans_pos,ans_len);}
};
(4)二面:2023-05-09
一面完自我感觉表现并不是很好,但在大概一周后接到了二面的面试邀请,仍然是自主预约制。二面面试时长43分钟左右,部门是旅游度假,面试平台是腾讯会议,二面面试官职级应该较高,面试体验比一面要好。
面试内容包括自我介绍+问项目+代码分析题+部门介绍和闲聊,八股问的较少。前期主要是问项目做的什么怎么实现的;然后手撕比较新颖,不是算法题,是一段Java代码的分析,先看代码如下:
funcA{this.funcB();getBean().funcB();
}@Metric
funcB{//todo
}@Aspect
class xxxx{@Around(@annotation("Metric"))void metricProcess(){writeMetric();}}
问题是在funcA方法中,两种funcB的调用方式哪一个会触发 metricProcess() 的方法执行,并解释为什么?我当时回答的是 getBean().funcB() ,因为这是一个切面的拦截调用,感觉应该由容器来托管,但让我们看看ChatGPT的回答(我应该是答错了):
ChatGPT:只有使用
this.funcB()
调用的funcB
方法会触发metricProcess()
方法执行。在切面中,this.funcB()
调用的是当前切面所织入的代理对象的方法,getBean().funcB()
调用的是容器中实际的目标对象的方法。具体来说,当使用
this.funcB()
调用方法时,Spring AOP 会在目标对象周围创建一个代理对象,并将该代理对象注入到目标对象中,然后通过代理对象来调用目标方法。这样做的好处是可以在代理对象中添加一些额外的逻辑,例如切面拦截等。而当使用getBean().funcB()
调用方法时,直接调用的是容器中实际的目标对象的方法,不会经过代理对象。需要注意的是,在使用 Spring AOP 时,只有在目标对象被注入到容器中并且从容器中获取时才会被 Spring AOP 代理,因此如果你直接在目标对象内部调用自己的方法时,应该使用
this.funcB()
而不是getBean().funcB()。
做完之后,面试官详细的介绍了旅游度假部门的业务方向、技术栈等体系,剩下的就是闲聊什么的,感觉这次面试的手撕还是挺有意思的。
(5)HR面:2023-05-12
二面完很快就接到了HR面,仍然是自主预约制。HR面面试时长30分钟左右,部门是机票的HR(机票的HR面试那入职就应该去机票部门了),面试平台是腾讯会议,期间HR迟到了10分钟左右。携程的HR面应该是时间比较长的企业HR面了,问的东西比较有特色。
刚上来是自我介绍和部门介绍,然后是经典的HR问题,比如意向城市、哪里人、兴趣爱好等等。接着问了我有没有用过携程,用哪些功能比较多,这些功能好用的地方在哪?这里我以火车票候补和抢票为例,对比了12306的缺点;然后是从技术层面大体阐述一下一次携程车票搜索的过程(从前端到后端)?这里我以web应用的请求响应模式说了一下;然后是对携程提一个优化建议,balabala开始胡说八道。感觉和HR聊的还是很开心的,貌似对方也企图从你这里找到一些优秀的产品需求和优化方案。。最后让我结束后等英语测评,并等待面试结果。
(6)英语测评:2023-05-13
5.12号HR面完半个小时左右,发了英语测评链接到邮箱。听说携程的英语测评也有及格线,会刷人,所以刚开始没急着做,先去牛客搜了一下攻略如下:
测评题型差不多一样吧,我英语六级不到500分,快一年没练英语听力、口语和阅读了,13号做完的,差不多半小时左右,半做半猜。。不要紧张,顺其自然。
(7)offer:2023-05-18
英语测评结束后四五天,5.18号电话oc,介绍了携程实习生的入职部门、薪资和房补情况,并给了几天时间考虑,让等待电话回访。等了一周,已经决定去团子了,5.26号等到回访时拒绝了本次offer,橙子秋招再见!
5.华为-华为云
最后一个简单写一下,华子暑期实习开的比较晚且无转正,但是华子的笔试成绩可以保留到秋招(有斩杀线),因此大部人对华子的优先级都是比较低的,而且华子的“爱华信华等华”懂得都懂。华为的投递要对应的部门捞才行,一般是先联系HR再投递才能笔试和面试流程,这点和其他厂都不太一样,我的投递意向是华为云。
(1)时间线
投递简历(4.10号) -> 笔试(4.19号) -> 在线测评(4.21号) -> 面试(4.27号) -> oc(6.5号)
(2)笔试复盘
我参加的笔试为第二批,一共三道编程算法题,分值依次为100、200、300,难度相对来说适中,允许使用本地IDE,平台是华为自己的笔试平台。博主过了1、2题,最终成绩为 300/600。
- 第一题:区间排序+双指针 100%
服务器能耗统计
服务器有三种运行状态:空载、单任务、多任务,每个时间片的能耗的分别为1、3、4;
每个任务由起始时间片和结束时间片定义运行时间:
如果一个时间片只有一个任务需要执行,则服务器处于单任务状态;
如果一个时间片有多个任务需要执行,则服务器处于多任务状态;
给定一个任务列表,请计算出从第一个任务开始,到所有任务结束,服务器的总能耗
#include <iostream>
#include <bits/stdc++.h>
using namespace std;
const int maxn = 10000 + 7;
struct Node{int st,ed;bool operator < (const Node& another)const{return st < another.st;}
}node[maxn];int main()
{int n;scanf("%d",&n);for(int i = 0;i < n;i++){scanf("%d%d",&node[i].st,&node[i].ed);}sort(node,node+n);int left_mul = -1,right_mul = -2;int left_task = -1,right_task = -2;int ans = 0;for(int i = 0;i < n;i++){if(node[i].st <= right_task){if(node[i].st <= right_mul){right_mul = max(right_mul,min(right_task,node[i].ed));}else{ans += (right_mul - left_mul + 1);left_mul = node[i].st;right_mul = min(right_task,node[i].ed);}right_task = max(right_task,node[i].ed);}else{if(right_task >= 0)ans += (node[i].st - right_task + 1 - 2);int task_len = right_task - left_task + 1;int mul_len = right_mul - left_mul + 1;ans += (3*task_len + mul_len);left_task = node[i].st;right_task = node[i].ed;left_mul = -1;right_mul = -2;}}int task_len = right_task - left_task + 1;int mul_len = right_mul - left_mul + 1;ans += (3*task_len + mul_len);printf("%d\n",ans);return 0;
}
- 第二题:DFS+路径输出 100%
树上逃离
给定一棵树,这个树有n个节点,节点编号从0开始依次递增,0固定为根节点。在这棵树上有一个小猴子,初始时该猴子位于根节点(0号) 上,小猴子一次可以沿着树上的边从一个节点挪到另一个节点,但这棵树上有一些节点设置有障碍物,如果某个节点上设置了障碍物,小猴子就不能通过连接该节点的边挪动到该节点上。问小猴子是否能跑到树的叶子节点(叶子节点定义为只有一条边连接的节点),如果可以,请输出小猴子跑到叶子节点的最短路径(通过的边最少),否则输出字符串NULL。
#include <iostream>
#include <bits/stdc++.h>
using namespace std;
const int maxn = 10000 + 7;
struct Node{int node,cost;Node(int _node,int _cost):node(_node),cost(_cost) {}bool operator < (const Node& another)const{return cost > another.cost;}
};
bool vis[maxn],blocks[maxn];
int pre_path[maxn];
int ans_tail,ans_cost;
vector<vector<int>> edges;void DFS(int node,int cost,int pre){if(edges[node].size() == 1 && edges[node][0] == pre){if(cost < ans_cost){ans_cost = cost;ans_tail = node;}return;}for(int i = 0;i < edges[node].size();i++){int next = edges[node][i];if(next != pre && !blocks[next]){pre_path[next] = node;DFS(next,cost+1,node);}}return;
}
int main()
{cin.getline()int n,m;memset(vis,0,sizeof(vis));memset(blocks,0,sizeof(blocks));scanf("%d%d",&n,&m);for(int i = 0;i < n;i++){edges.push_back(vector<int>());pre_path[i] = i;}for(int i = 0;i < m;i++){int st,ed;scanf("%d%d",&st,&ed);edges[st].push_back(ed);edges[ed].push_back(st);}for(int i = 0;i < n;i++){sort(edges[i].begin(),edges[i].end());}int t;scanf("%d",&t);for(int i = 0;i < t;i++){int node;scanf("%d",&node);blocks[node] = 1;}ans_cost = INT_MAX;ans_tail = -1;DFS(0,0,-1);if(ans_tail == -1)printf("NULL\n");else{stack<int> S;int index = ans_tail;while(index != pre_path[index]){S.push(index);index = pre_path[index];}printf("0");while(!S.empty()){int node = S.top();S.pop();printf("->%d",node);}printf("\n");}return 0;
}
(3)面试面经
华为的面试为线下面试,需要统一到当地华为研究所进行。我的面试批次为提前批,面试分为两面,包括一轮技术面+一轮主管面,技术面通过后会立马进行主管面,如果到饭点华子还会管饭(好评)。
技术面大概一个小时,主要内容包括自我介绍+项目+八股+手撕等,可能由于我是提前批次,面试难度并不大,其中项目和手撕占用时间较长,八股只问了Java GC相关的一部分就过了,手撕题目我抽的比较简单(结构体排序),是真手撕(在纸上写代码OvO)。一面结束之后回到等候室等待结果,会有短信。
一面通过之后,会立即进行主管面(二面),主管面内容包括自我介绍+项目介绍+企业理解和生活问答,不涉及技术。我的主管面比较偏压力面,整个过程大概30分钟,项目问的比较细,着重问你项目中碰到的难点、解决方案、成长等;然后就是会问你对华为的一些了解、兴趣爱好、意向城市等等,面试官压迫感十足语速较快,给人压力较大。二面结束后,就可以在官网查询状态,通过后就可以慢慢等结果了。。