大家好,我曾经是寒流,本来想安安心心在家玩儿,奈何家里缺个牛牛抱枕,母亲有令,被迫营业,找人死皮赖脸的嫖了2份面经。
言归正传,今天带大家复盘一位小伙伴的美团面试经历:
*
*
一面
1.自我介绍
2.手撕代码单链表翻转。
//给定单链表的头节点 head ,请反转链表,并返回反转后的链表的头节点。 //例如:输入:head = [1,2,3,4,5] // 输出:[5,4,3,2,1] public ListNode reverseList(ListNode head) { // 1. 递归终止条件 if (head == null || head.next == null) { return head; } ListNode p = reverseList(head.next); head.next.next = head; head.next = null; return p; }
3.为什么不让实习。
4.做这个系统遇到过什么问题。
(并发问题,性能问题,代码规范问题)
5.如何防止超卖。
(redis缓存预热,不要一次性上所有商品,并发锁。)
6.超卖的定义,怎么解决?
(并发问题,比如库存变成负数)
7.其他框架用过吗?现在可以应用的框架有哪些?
(很遗憾,没用过)
8.RabbitMQ使用?与卡夫卡相比有什么区别?负载均衡?主要有什么区别?为什么RabbitMQ吞吐量不如卡夫卡?
(--现在用的RabbitMQ是哪个版本?)
9.介绍一下HashMap和ConcurrentHashMap结构区别。1.7-1.8的区别。
(JDK1.8之前HashMap的结构为数组+链表,JDK1.8之后HashMap的结构为数组+链表+红黑树;JDK1.8之前ConcurrentHashMap的结构为segment数组+数组+链表,JDK1.8之后ConcurrentHashMap的结构为数组+链表+红黑树。)
10.JDK,1.7-1.8的区别。为什么做这样一个改变?
(https://blog.csdn.net/qiuchaoxi/article/details/79869817)
11.CMS,G1的垃圾回收算法相同吗?有什么区别?
(不同,原理啥的差别很大,详见https://blog.csdn.net/shlgyzl/article/details/95041113)
12.线程池,在什么场景下应用。大概的一个实现过程是什么样的(工作方式)?
(线程池适合单系统的大量的异步任务处理,比如发送短信、保存日志。https://www.cnblogs.com/yanggb/p/10629387.html)
13.sleep()和wait()的区别?
(一个需要时间,一个需要notify,以及一个是thread的方法,一个是object类)
14.wait是会释放锁吗?等待区是指什么?
(wait是指在一个已经进入了同步锁的线程内,让自己暂时让出同步锁,以便其他正在等待此锁的线程可以得到同步锁并运行,只有其他线程调用了notify方法(notify并不释放锁,只是告诉调用过wait方法的线程可以去参与获得锁的竞争了,但不是马上得到锁,因为锁还在别人手里,别人还没释放),调用wait方法的一个或多个线程就会解除wait状态,重新参与竞争对象锁,程序如果可以再次得到锁,就可以继续向下运行。)
15.wait被唤醒还会竞争锁吗?竞争锁时也是runnable状态吗?
(同上)
16.介绍下Mysql索引,有哪几种索引,怎么实现的。
(hash索引,红黑树索引)
17.写sql注意哪些,如何设计索引,聚簇索引的缺点有什么?
什么是二级索引,什么是回表操作?为什么更新代价很高?为什么有影响?插入时会做什么操作?
(是否安全,是否需要加索引。等)
18.系统运行时如果CPU持续百分之百从哪些方面处理?
(清理无用进程,查询异常进程,检查是否有循环递归等)
二面
1.自我介绍。
2.做项目目的。
3.在实验室主要做什么项目?简单介绍一下。
4.秒杀系统核心?特性在哪里?挑战在哪里?
(并发和限流)
5.如何用MQ做异步下单?
(消息队列,放在队列里,下完单就处理别的,不需要反馈)
6.秒杀之后没有立即生成订单如何支付?
(不懂)
7.轮询在前端还是后端轮询?轮询时间多久,一直轮询不到怎么办?
(时间1分钟1次,轮询不到,放弃,显示秒杀失败)
8.数据用的什么,外部高并发如何处理,大量的并发请求是怎么处理的?
(JSON,增加消费者(服务器)的数量)
9.有了解过SpringBoot对高并发的处理方式吗?能处理多高的并发?如果并发量超过线程池大小怎么处理?
(没用过)
10.每个对象到达的时候调用的都是一个Controller吗,怎么保证线程安全?
(介绍THREADLOCAL)
11.在你们研究专业方面用什么做开发?
12.写一小段代码。两个有序整数数组,合并成一个有序整数数组。
给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2,另有两个整数 m 和 n ,分别表示 nums1 和 nums2 中的元素数目。 请你 合并 nums2 到 nums1 中,使合并后的数组同样按 非递减顺序 排列。 注意:最终,合并后数组不应由函数返回,而是存储在数组 nums1 中。为了应对这种情况,nums1 的初始长度为 m + n,其中前 m 个元素表示应合并的元素,后 n 个元素为 0 ,应忽略。nums2 的长度为 n 。 输入:nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3 输出:[1,2,2,3,5,6] 解释:需要合并 [1,2,3] 和 [2,5,6] 。 合并结果是 [1,2,2,3,5,6] ,其中斜体加粗标注的为 nums1 中的元素。 class Solution { public void merge(int[] nums1, int m, int[] nums2, int n) { int p1 = 0, p2 = 0; int[] sorted = new int[m + n]; int cur; while (p1 < m || p2 < n) { if (p1 == m) { cur = nums2[p2++]; } else if (p2 == n) { cur = nums1[p1++]; } else if (nums1[p1] < nums2[p2]) { cur = nums1[p1++]; } else { cur = nums2[p2++]; } sorted[p1 + p2 - 1] = cur; } for (int i = 0; i != m + n; ++i) { nums1[i] = sorted[i]; } } }
13.top(k)问题;
(快排,和最小K堆两种思路)
( 堆排序是通过维护大顶堆或者小顶堆来实现的。堆排序法来解决N个数中的TopK的思路是:先随机取出N个数中的K个数,将这N个数构造为小顶堆,那么堆顶的数肯定就是这K个数中最小的数了,然后再将剩下的N-K个数与堆顶进行比较,如果大于堆顶,那么说明该数有机会成为TopK,就更新堆顶为该数,此时由于小顶堆的性质可能被破坏,就还需要调整堆;否则说明这个数最多只能成为Top K+1 th,因此就不用管它。然后就将下一个数与当前堆顶的数作比较,根据大小关系如上面所述方法进行操作,知道N-K个数都遍历完,此时还在堆中的K个数就是TopK了。
( 快速排序法的原理这就不多说了,可见https://blog.csdn.net/qq_28114615/article/details/86064412
在快速排序中,每一轮排序都会将序列一分为二,左子区间的数都小于基准数,右子区间的数都大于基准数,而快速排序用来解决TopK问题,也是基于此的。N个数经过一轮快速排序后,如果基准数的位置被换到了i,那么区间[0,N-1]就被分为了[0,i-1]和[i+1,N-1],这也就是说,此时有N-1-i个数比基准数大,i个数比基准数小,假设N-1-i=X那么就会有以下几种情况:
①X=K。这种情况说明比基准数大的有K个,其他的都比基准数小,那么就说明这K个比基准数大的数就是TopK了;
②X<K。这种情况说明比基准数大的数不到K个,但是这X肯定是属于TopK中的TopX,而剩下的K-X就在[0,i]之间,此时就应当在[0,i]中找到Top(K-X),这就转换为了TopK的子问题,可以选择用递归解决;
③X>K。这种情况说明比基准数大的数超过了K个,那么就说明TopK必定位于[i+1,N-1]中,此时就应当继续在[i+1,N-1]找TopK,这样又成了TopK的一个子问题,也可以选择用递归解决。
总结
二面很多关于框架的理论以及最新的一些技术栈没怎么了解,显得不是很熟悉行业发展现状,吃了大亏。
对于一些项目场景总结不足,临场应变能力不足。
对于一些设计模式设计思路上有所欠缺,需要补强
提问
如何高效的面向面试了解最新的技术呢?
全部评论
(6) 回帖