快手java面经
写在前面
快手面试还算是简单一些,人性化一些,更多的是考虑技术选型,比如多种MQ的优缺点,未来如何做技术选型等。
一面
快手的第一面聊的时间久些,历时1小时45分钟,主要是把我工作多年的4个大项目都详细的聊一下
项目
所问的项目,基本套路就是:为了解决什么业务做了什么样的架构设计,其中遇到了什么难点和亮点。你可以围绕高并发、高性能、稳定性、可靠性、一致性等等描述一下你的亮点...
技术
1、技术选型
double与grpc的对比
这里回答的不太好,没了解过grpc,回答的double与springcloud区别
redis与memcached
从存储结构、持久化等对比,重点讲的是redis的知识
rocketmq与kafka
这里回答的也不好,对比的少,介绍rocket的较多,rocket的发送端可靠性、服务端可靠性、消费端可靠性,注册中心、事务等
2、基础知识
Executors 与 ThreadPoolExecutor的选择,为什么选择后者
1、可读性强,便于开发与合作
2、重点是线程的数量和阻塞队列可控,并不会轻易导致线程无限分配抢占所有cpu和内存泄漏等问题
redis的list结构压缩表和跳表
算法
问题
数组里引用数组,死循环后break
输入
A : a,b,c,B
B: C, c
C: A,
输出
A: a,b,c,c
B: a,b,c,c
C: a , b, c,c
isGroup()
Map<String, Set<String>>
public void convert(Map<String, Set<String>> data) {
}
我的解题算法,感觉写的并不是很好
public static void main(String[] args) {
Map<String, Set<String>> va = new HashMap();
Set<String> A = new HashSet<>();
A.add("a");
A.add("b");
A.add("c");
A.add("B");
va.put("A", A);
Set<String> B = new HashSet<>();
B.add("C");
B.add("c");
va.put("B", B);
Set<String> C = new HashSet<>();
C.add("A");
va.put("C", C);
va.forEach((s, values) ->{
Map<String, List<String>> temp = new HashMap();
getValue2(va, temp, s, values);
System.out.println(s + ":" + JSONObject.toJSONString(temp.get(s)));
});
}
private static void getValue2(Map<String, Set<String>> va, Map<String, List<String>> temp, String key,
Set<String> value) {
if (temp.containsKey(key)) {
return;
}
temp.put(key, null);
List<String> list = new ArrayList<>();
value.forEach(s ->{
if (!s.equals(s.toLowerCase())) {// 大写的字母的时候
if (temp.get(s) == null) {
getValue2(va, temp, s, va.get(s));
}
if (temp.get(s) != null) {
list.addAll(temp.get(s));
}
} else {//小写的字母直接加减
list.add(s);
}
});
temp.put(key, list);
}
二面
二面就没问业务,上来就是技术的问题,历时1:30分钟
技术
1、mq,遇到过什么问题,怎么解决的?offset对于失败的消息如何记录
2、redis 内存击穿,如何存储空值以节省空间
1.Redis 集合(Set)
2.位图存储
3.布隆过滤器
3、set的结构,在使用的时候有什么问题
4、JVM:OOM的问题排查,CMS收集器
设计题
请设计一个服务上云的方案
需要考虑3点1.web 2.redis 3.mysql
数据迁移的过程中,考虑好一些碰撞,和存量数据和增量数据的处理。
算法
表达式计算,数字的加减乘除
int calculate(String exp)
正整数,+ - * /,
int calculate(String exp)
exp = "(3-2)*12+3*3*2"
Positive integer, + - *,
这道题其实压栈的思想
思路:
1. 如果碰到数字, 则把数字入栈
2. 如果碰到空格, 则继续下一步
3. 如果碰到 '+' '-' '*' '/', 则查找下一个数字num
A.如果是'+', 下一个数字num直接入栈
B.如果是'-',-num入栈
C.如果是'*', num = stack.pop() * num, 入栈
D.如果是'/', num = stack.pop() / num, 入栈
4. 最后,把栈里的数相加就是结果
public int calculate(String s) {
char[] cs = s.trim().toCharArray();
Stack<Integer> st = new Stack();
int ans= 0, i= 0;
while (i< cs.length) {
if (cs[i] == ' ') {
i++;
continue;
}
char tmp = cs[i];
if (tmp == '*' || tmp == '/' || tmp == '+' || tmp == '-') {
i++;
while (i< cs.length && cs[i] == ' ') { i++; }
}
int num= 0;
while (i< cs.length && Character.isDigit(cs[i])) {
num= num* 10 + cs[i] - '0';
i++;
}
switch (tmp) {
case '-':
num= -num;
break;
case '*':
num= st.pop() * num;
break;
case '/':
num= st.pop() / num;
break;
default:
break;
}
st.push(num);
}
while (!st.isEmpty()) { ans+= st.pop(); }
return ans;
}
三面
技术题
1、redis集群主从方式,主机down机的处理,与memcache的区别,key的失效策略
2、rocketmq与kafaka的区别
3、nago协议和inode
设计与算法题
1、767的阶乘后边有多少个0
可以➗5的,25里有2个5
static int factorial(int num) {
int result= 0;
for (int i= 1; i<= num; i++) {
/*
//方法1
if (i % 5 == 0) {
result++;
int temp = i / 5;
while (temp % 5 == 0) {
result++;
temp = temp / 5;
}
}*/
if (i% 5 == 0) {
int temp= i;
do {
result++;
temp= temp/ 5;
} while (temp% 5 == 0);
}
}
return result;
}
public static void main(String[] args) {
System.out.println(factorial(26));
}
2、大文件1G ,每个词条16字节,1M内存,求出词频最高的100条数据
由于内存的限制,所以不能同时将1G的文件进行分析计算,可以使用分而治之的思想,将文件分为多个,可以分为某一个只有1M的,这样将小文件的计算就不会出现超出内存的问题了。
分隔的方法就是将每一个单词进行hash后,hash%5000这样将单词分割到5000个小文件里,1G/5000大约一个文件200k,重复单词一定被分割到同一个文件中。
由于没一项是一个单词,可以采用字典树Trie进行统计/hashmap,统计每个文件中出现的次以及频率。字典树的时间复杂度为单词最长的数值+遍历一遍n*O(k),hash为遍历一遍+产生hash+冲突解决
对每一个小文件取出其中频率最大的前100个单词,然后进行合并,或者直接进行归并排序/堆排序,nlog(k)
问答环节
基本上是我来问了,问一问部门的业务,用到什么的技术,有什么苦难和瓶颈?
HR
一些基本问题,工作表现,个人的优缺点,遇到过什么问题,怎么解决的