大家好,我是程序员小灰。
在今年,小灰的一位读者在秋招提前批的时候,面试了阿里巴巴杭州base的淘宝部门,已顺利拿到offer。
这位读者为了能帮助到更多程序员朋友,把阿里第一轮面试的过程详详细细做了记录和总结,希望大家能够从中获得启发。
面试者背景介绍
先大概说一下我的背景吧,本科211自动化专业,硕士Top2学校非科班,字节,京东两端大厂实习,一段侧重推荐算法开发,另一段Java后端开发,各4个月的实习经历。
在今年24届秋招提前批,投递了阿里巴巴杭州base的淘宝部门,目前已oc拿到offer,全程面试内容主要包括三个部分:实习项目,项目经验,手撕代码,八股文理论基础考察。
以下为淘宝一面的内容,面试考察非常全面,整个面试的时长1小时零5分钟,选取了其中比较关键的干货内容与大家分享,包含了作者的面试后总结与反思。
一面面试过程
1. 面试官:同学你好,因为我们是第一轮技术面试,第一轮面试,我们会以Java的八股文基础以及算法题的考察为主。同学,现在可以开始了吧?同学你先做一个简单的自我介绍吧!
我:好的,面试官,我这边没有问题。自我介绍...
小结:其实虽然说是以八股文和算法题的考察为主,但是现在觉得自我介绍时,应该可以“引导”面试官多聊一聊项目,毕竟更加擅长。
2. 面试官:我看你本科是自动化专业的,研究生期间转到了软件工程。那你的计算机基础怎么样?我问你几个关于数据结构的问题,你能给我讲一下二叉树的遍历方法与具体原理吗?
我:二叉树的遍历方法包括前序,中序,后序和层次遍历,其中二叉树的前序遍历是先去访问二叉树的根节点,然后遍历左子树,右子树节点。
中序遍历是按照左子树,根节点,右子树顺序,后序遍历是按照左子树,右子树,根节点进行二叉树的遍历。
而层序遍历则是借助Queue队列进行按照二叉树的层次进行上下顺序的打印。
小结:几种遍历方法,感觉说完,就会有算法题要写了,果然...
面试官:好的,既然你已经提到了二叉树的数据结构,也说了几种遍历形式,那我现在想让你写一道算法题。
你刚才说的中序遍历你用代码实现一下,并且我希望你能使用迭代以及递归两种形式进行实现的。
我:
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 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二叉树节点类,也需要自己进行手动定义。同时需要自己构建测试用例,还是有一定难度的。
3. 面试官:我们再聊聊计算机网络的基础,TCP和UDP两种网络通信协议的区别,你说一下吧?
我:TCP和UDP两种通信协议区别在于TCP是面向连接的,传输具有可靠性,而UDP不保证数据可靠性的,TCP的建立连接需要三次握手和四次挥手,而UDP不需要,同时TCP通信具有流量控制和拥塞控制机制,UDP通信协议则没有;TCP传输可以保证数据的传输顺序,而UDP传输不一定能保证数据传输顺序。
4. 面试官:同学我看简历上你说你了解JAVA的垃圾回收机制。曾在一家实习中涉及了JVM调优,你能简单说一下,你是怎么对JVM内存进行调优的?
我:JVM的垃圾回收算包括标记-清除方法 复制方法 标记-整理方法 三种垃圾回收算法。
标记-清除方法会扫描堆中的对象,删除未标记的对象,使其内存可供再次使用。但是清除阶段会导致内存碎片,需要额外的工作来合并碎片。
复制方法是将堆内存分为两个区域:From区和To区。在垃圾收集时,从From区中将存活的对象复制到To区。清空From区,将From区和To区的角色互换,以便下一次垃圾收集,但是浪费了一半的内存空间。
标记-整理方法类似于标记-清除,但在标记阶段后,会将存活对象向一端移动,从而压缩内存,可以减少内存碎片
小结:这两个问题就是比较正常的八股文了,但是都联系了部分实习经历中的项目,所以八股文不能只死记硬背,需要理解其原理。
5. 面试官:阿里这边的技术栈,数据库使用的MySQL,如果现在MySQL的查询速度变慢的时候,你有什么优化办法吗?
我:首先使用Explain进行慢查询日志的执行计划解析,然后对查询的限定条件的数据库字段增加索引机制,也可以借用Redis缓存数据库的机制,进行缓存的查询加速。
小结:属于实际的场景题目,回答面试官觉得很满意,当然也可以采用分库分表的机制,但是相对说,工作量会比较大。
6. 面试官:我现在给你一条写好的SQL语句,你来判断它是否使用应用到了你加的索引呢?
发了一条SQL,判断是否能用到索引(select * from table where name like "%林")
我:这个SQL查询中的name字段使用like模糊查询符,但是模糊搜索字符串的模式是以通配符%开头的,即"%林"。这种情况下,通常无法充分利用索引。
7. 面试官:你刚才提到了Redis缓存数据库,那你有用过Redis操作命令吗?Redis数据库怎么查出所有Keys列表,并且扫描大key呢?
我:使用SCAN 0 命令在Redis数据库的终端执行,可以扫描Redis数据库中的键值对列表。扫描大Key这个没尝试过,但是一般来说,就是把List集合数据格式的key进行重点筛选。
小结:后续在面完后,查询了一下,可以使用Redis RDB Tools工具进行大Keys的扫描。
8. 面试官:说对了一部分,你可以下去再了解一下,为什么Redis它明明是单线程的,它的查询速度会这么快?
我:经典八股文,虽然Redis是单线程的,但它使用非阻塞I/O来处理多个客户端的请求。这意味着当一个客户端的请求需要等待外部I/O,同时Redis是基于内存机制读写,相比于MySQL的硬盘读写机制,速度更快!
9. 面试官:同学,我这边想问的技术内容就差不多了,最后我们再做一道简单的算法题,做完了讲一下你的思路,同时你有什么问题想问我的吗?
我:所有手撕算法需要使用IDEA,共享屏幕完成,并且面试官还会和你交流思路。
返回链表倒数第K个节点:
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