大家好,我是程序员小灰。
在今年,小灰的一位读者在秋招提前批的时候,面试了阿里巴巴杭州base的淘宝部门,已顺利拿到offer。
这位读者为了能帮助到更多程序员朋友,把阿里第一轮面试的过程详详细细做了记录和总结,希望大家能够从中获得启发。
面试者背景介绍
先大概说一下我的背景吧,本科211自动化专业,硕士Top2学校非科班,字节,京东两端大厂实习,一段侧重推荐算法开发,另一段Java后端开发,各4个月的实习经历。
在今年24届秋招提前批,投递了阿里巴巴杭州base的淘宝部门,目前已oc拿到offer,全程面试内容主要包括三个部分:实习项目,项目经验,手撕代码,八股文理论基础考察。
以下为淘宝一面的内容,面试考察非常全面,整个面试的时长1小时零5分钟,选取了其中比较关键的干货内容与大家分享,包含了作者的面试后总结与反思。
一面面试过程
- 面试官:同学你好,因为我们是第一轮技术面试,第一轮面试,我们会以Java的八股文基础以及算法题的考察为主。同学,现在可以开始了吧?同学你先做一个简单的自我介绍吧!
我:好的,面试官,我这边没有问题。自我介绍...
小结:其实虽然说是以八股文和算法题的考察为主,但是现在觉得自我介绍时,应该可以"引导"面试官多聊一聊项目,毕竟更加擅长。
- 面试官:我看你本科是自动化专业的,研究生期间转到了软件工程。那你的计算机基础怎么样?我问你几个关于数据结构的问题,你能给我讲一下二叉树的遍历方法与具体原理吗?
我:二叉树的遍历方法包括前序,中序,后序和层次遍历,其中二叉树的前序遍历是先去访问二叉树的根节点,然后遍历左子树,右子树节点。
中序遍历是按照左子树,根节点,右子树顺序,后序遍历是按照左子树,右子树,根节点进行二叉树的遍历。
而层序遍历则是借助Queue队列进行按照二叉树的层次进行上下顺序的打印。
小结:几种遍历方法,感觉说完,就会有算法题要写了,果然...
面试官:好的,既然你已经提到了二叉树的数据结构,也说了几种遍历形式,那我现在想让你写一道算法题。
你刚才说的中序遍历你用代码实现一下,并且我希望你能使用迭代以及递归两种形式进行实现的。
我:
go
import java.util.Stack;
public class Main {
public static class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode(int val) {
this.val = val;
}
}
public static void main(String[] args) {
// 构建二叉树
TreeNode root = new TreeNode(1);
TreeNode left = new TreeNode(2);
TreeNode right = new TreeNode(3);
TreeNode leftOfLeft = new TreeNode(4);
TreeNode rightOfLeft = new TreeNode(5);
TreeNode leftOfRight = new TreeNode(6);
TreeNode rightOfRight = new TreeNode(7);
root.left = left;
root.right = right;
left.left = leftOfLeft;
left.right = rightOfLeft;
right.left = leftOfRight;
right.right = rightOfRight;
// 执行中序遍历
System.out.print("Inorder Traversal (Recursive): ");
inorderTraversalRecursive(root);
System.out.println();
System.out.print("Inorder Traversal (Iterative): ");
inorderTraversalIterative(root);
}
// 中序遍历递归实现
public static void inorderTraversalRecursive(TreeNode root) {
if (root == null) {
return;
}
inorderTraversalRecursive(root.left);
System.out.print(root.val + " ");
inorderTraversalRecursive(root.right);
}
// 中序遍历迭代实现
public static void inorderTraversalIterative(TreeNode root) {
if (root == null) {
return;
}
Stack<TreeNode> stack = new Stack<>();
TreeNode current = root;
while (current != null || !stack.isEmpty()) {
while (current != null) {
stack.push(current);
current = current.left;
}
current = stack.pop();
System.out.print(current.val + " ");
current = current.right;
}
}
}
小结:面试时的手撕代码,和刷leetcode时的感觉还是不太一样的,类似TreeNode二叉树节点类,也需要自己进行手动定义。同时需要自己构建测试用例,还是有一定难度的。
- 面试官:我们再聊聊计算机网络的基础,TCP和UDP两种网络通信协议的区别,你说一下吧?
我:TCP和UDP两种通信协议区别在于TCP是面向连接的,传输具有可靠性,而UDP不保证数据可靠性的,TCP的建立连接需要三次握手和四次挥手,而UDP不需要,同时TCP通信具有流量控制和拥塞控制机制,UDP通信协议则没有;TCP传输可以保证数据的传输顺序,而UDP传输不一定能保证数据传输顺序。
- 面试官:同学我看简历上你说你了解JAVA的垃圾回收机制。曾在一家实习中涉及了JVM调优,你能简单说一下,你是怎么对JVM内存进行调优的?
我:JVM的垃圾回收算包括标记-清除方法 复制方法 标记-整理方法 三种垃圾回收算法。
标记-清除方法会扫描堆中的对象,删除未标记的对象,使其内存可供再次使用。但是清除阶段会导致内存碎片,需要额外的工作来合并碎片。
复制方法是将堆内存分为两个区域:From区和To区。在垃圾收集时,从From区中将存活的对象复制到To区。清空From区,将From区和To区的角色互换,以便下一次垃圾收集,但是浪费了一半的内存空间。
标记-整理方法类似于标记-清除,但在标记阶段后,会将存活对象向一端移动,从而压缩内存,可以减少内存碎片
小结:这两个问题就是比较正常的八股文了,但是都联系了部分实习经历中的项目,所以八股文不能只死记硬背,需要理解其原理。
- 面试官:阿里这边的技术栈,数据库使用的MySQL,如果现在MySQL的查询速度变慢的时候,你有什么优化办法吗?
我:首先使用Explain进行慢查询日志的执行计划解析,然后对查询的限定条件的数据库字段增加索引机制,也可以借用Redis缓存数据库的机制,进行缓存的查询加速。
小结:属于实际的场景题目,回答面试官觉得很满意,当然也可以采用分库分表的机制,但是相对说,工作量会比较大。
- 面试官:我现在给你一条写好的SQL语句,你来判断它是否使用应用到了你加的索引呢?
发了一条SQL,判断是否能用到索引(select * from table where name like "%林")
我:这个SQL查询中的name字段使用like模糊查询符,但是模糊搜索字符串的模式是以通配符%开头的,即"%林"。这种情况下,通常无法充分利用索引。
- 面试官:你刚才提到了Redis缓存数据库,那你有用过Redis操作命令吗?Redis数据库怎么查出所有Keys列表,并且扫描大key呢?
我:使用SCAN 0 命令在Redis数据库的终端执行,可以扫描Redis数据库中的键值对列表。扫描大Key这个没尝试过,但是一般来说,就是把List集合数据格式的key进行重点筛选。
小结:后续在面完后,查询了一下,可以使用Redis RDB Tools工具进行大Keys的扫描。
- 面试官:说对了一部分,你可以下去再了解一下,为什么Redis它明明是单线程的,它的查询速度会这么快?
我:经典八股文,虽然Redis是单线程的,但它使用非阻塞I/O来处理多个客户端的请求。这意味着当一个客户端的请求需要等待外部I/O,同时Redis是基于内存机制读写,相比于MySQL的硬盘读写机制,速度更快!
- 面试官:同学,我这边想问的技术内容就差不多了,最后我们再做一道简单的算法题,做完了讲一下你的思路,同时你有什么问题想问我的吗?
我:所有手撕算法需要使用IDEA,共享屏幕完成,并且面试官还会和你交流思路。
返回链表倒数第K个节点:
go
public class Main {
static class ListNode {
int val;
ListNode next;
ListNode(int val) {
this.val = val;
}
}
public static ListNode findKthFromEnd(ListNode head, int k) {
if (head == null || k <= 0) {
return null;
}
ListNode slow = head;
ListNode fast = head;
// 将快指针先移动k步
for (int i = 0; i < k; i++) {
if (fast == null) {
return null; // 处理k大于链表长度的情况
}
fast = fast.next;
}
// 同时移动快慢指针,直到快指针到达链表末尾
while (fast != null) {
slow = slow.next;
fast = fast.next;
}
return slow;
}
public static void main(String[] args) {
ListNode node = new ListNode(0);
node.next = new ListNode(1);
node.next.next = new ListNode(2);
node.next.next.next = new ListNode(3);
int k = 2;
ListNode K_node = findKthFromEnd(node,k);
System.out.println(K_node.val);
}
}
思路:这道算法题使用快慢指针进行解答,新建两个起始指针,快指针先走K步,然后快慢指针同时运行,当快指针先运行到链表的尾结点,恰好此时慢节点的指向位置就是倒数第K个节点,返回即可。
提问:我想问一下您,咱们部门是做什么业务的?使用什么技术栈?
- 面试官:我们这边主要是做辅助天猫数据中间数据存储的,你可以理解为辅助双十一的流量压力分流,使用的技术栈,我看和你挺匹配的,基本使用的都是MySQL数据库,以及kafka消息中间件。
我:好的,感谢面试官,我没有其他的问题了,拜拜~
小结:一般来说,如果面试官很愿意和你讲解公司的业务方向,面试的通过概率就非常大了,因为如果没有通过,没有必要和你浪费时间,同时你也可以表现的你和工作业务很匹配,增加一点印象分。
面试总结反思
一面总结:不同面试官的面试风格,可能真的不一样,我也遇到过第一轮面试就细聊项目经验的,但是阿里淘宝的这轮面试,感觉比较侧重Java的八股文以及算法题。
从一上来的二叉树的中序遍历算法题,以及最后一道算法题,甚至中间的SQL语句,整体面试风格比较务实。同时感觉面试的内容很灵活,对知识的掌握要求能灵活运用。整体感觉难度中等。
面试结果:顺利通过,已约二面
< END >
前几天,小灰发布了数据结构与算法课程的预售,很快就已经有700位小伙伴报名,名额很快就被抢光了。
为了帮助到更多朋友,小灰今天又放出一部分名额,后续随着课程不断更新,价格也会不断提升,大家抓紧时间扫描下单哦~~