【阿里一小时电面】
8.13被蚂蚁金服捞起来面试,约在8.14这天,今天一看是七夕,面试官口头禅“兄弟”亲切又有力量,苦逼的心情好了点。
这次的岗位是算法工程专家(跟面试官确认了下只是部门的名字叫算法工程,有做算法有做java的,岗位是P5-P7都招,所以什么“专家”的可能是阿里最近的风格又换了吧)。毕竟社招,我工作一年进步了些,面试的要求也高了。面试官其实还比较专业的,声音也婉转,想给人一种比较有素质没距离感的感觉吧,但其实过不过只是结果,他也只是面试第一步。可能觉得我有潜力,也可能觉得我不适合职位。
拿过蚂蚁offer的人又不少我一个,但这种东西并不像梦想,没有缘分也好,不适合就是不适合,让你失望了一次,又岂会不再让你失望一次。把它们当作始终敲不开的门就好。
记录下面试,一个小时的电话面试。
-
简单的自我介绍,简单介绍下自己做过最有挑战的一个项目。
-
技术基础题:
kafka:
kafka如何消费已经被消费过的数据
kafka数据存储在哪里?为什么读效率高? -
Redis:
自己的项目中Redis的应用场景
Redis的缓存穿透和缓存雪崩解决 -
Java基础:
volatile关键字什么情况下使用?可以修饰数组保证内存可见性吗?
JVM的内存管理机制(Java内存,垃圾回收算法)
如何查看线程的dump文件,转储?
Java Swing是线程安全的吗?(非线程安全)
Java锁有哪些?(锁的不同分类)
Java泛型,要注意的问题 -
大数据了解? Hadoop、Spark这些。
-
设计题:
如何保证接口的幂等性? -
算法题:
代码重复度计算
字符串中最大重复度(重复次数*字符串长度)的子串,子串长度大于等于2,出现2次及以上算重复,如果存在多个,按出现的顺序返回。
如abcabcabab中ab的重复次数为4, abc的重复次数为2…… 最大重复度的子串为ab。
5分钟思考然后说思路,面试官说没问题,提示可以思路基础上考虑剪枝,20分钟编码。
我的思路是按子串长度(2开始递增)循环找到所有子串并用map记录子串出现的次数,并且记录当前最大重复度maxWeight,如果一个子串的长度乘以它最大可能出现的次数仍然小于当前最大重复度maxWeight,那么不考虑这种情况。最后遍历Map,把所有重复度等于maxWeight的子串添加进list。
由于人在外边,写代码时电脑快没电了,想着快点写完结束。开始题意理解错了,以为是最大重复次数,最后又改为最大重复度了,修改后面试官说这个解法没问题。其实由于白板写,可能有的拼写或者语法会有小问题。
- 反问:
1、算法工程这个岗位后端Java这块具体做什么的,使用的是Java哪些技术栈?
2、工作地点也是在杭州吗?说是的,因为总部在杭州,考虑到员工成长,老大希望新员工至少在杭州工作6个月,才能申请转到上海,然后面试官附带回答了工作强度,看部门。
3、聊些技术无关的,面试官您作为阿里的技术人员,也知道阿里最近爆出的事,您对此怎么看?
-----------------------------------------分割线
【某阿里一面三道题】7.30
某阿里刷面试KPI忒明显了,前天捞我约了一面,昨天面试,今天收到拒信才知道给我投的技术专家,对于我目前的工作经验来说,这个岗位有点高了。
电话大概聊了半个小时,然后面试官说做三道题,让我做完自主答案提交到链接里的系统(其实没有提交按钮,复制代码到答题板就行)。然后他也没有守着,也没有后续反问环节,直接挂了电话。当天晚上可答题,今天再点链接只有面试结束,题目和做题记录全部消失。
简历筛选了,聊得也很好,题也都做出来了,但第二天就给拒信了,果然大厂效率,不过有很明显走过场嫌疑。
题还是记录下吧。阿里,阿里巴巴。
1.实现函数,给定一个字符数组,求该数组的连续非空子集,分別打印出来各子集 ,举例数组为[abc],输出[a],[b],[c],[ab],[bc],[abc]。
我的答案:
import java.util.Arrays;
import java.util.LinkedHashSet;
public class Test {
public static void main(String[] args)
{
char[] s = new char[]{'a', 'b', 'c'};
LinkedHashSet<String> set = new LinkedHashSet<>();
for(int i=1; i<=s.length; i++)
{
char[] target = new char[i];
getContinousSubSet(s, 0, 0, i, target, set);
}
if(set.size() == 0) return;
for(String str : set)
{
System.out.println(str);
}
}
//s原始数组, start初始位置, idx目标数组当前索引, n连续子集长度也用于计算递归次数(s.length-(n-1)), target目标数组, set存放所有连续子集
private static void getContinousSubSet(char[] s, int start, int idx, int n, char[] target, LinkedHashSet<String> set)
{
for(int i = start; i < s.length-(n-1); i++)
{
target[idx] = s[i];
if(n == 1)
{
set.add(Arrays.toString(target));
}
else
{
getContinousSubSet(s, i+1, idx+1, n-1, target, set);
}
}
}
}
运行结果:
2.有3个线程和1个公共的字符数组。线程1的功能就是向数组输出A,线程2的功能就是向数组输出l,线程3的功能就是向数组输出i。要求按顺序向数组赋值AliAliAli,Ali的个数为n。
我的答案:
//使用Lock类保证线程安全,实现线程对数组的互斥写操作。
//测试用例N取10
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class WriteAli {
private final int THREAD_NUM = 3;
private final int STATE_MOD_A = 0;
private final int STATE_MOD_L = 1;
private final int STATE_MOD_I = 2;
private int times;
private int state = 0;
private char[] target;
private Lock lock = new ReentrantLock();
public static void main(String[] args) throws InterruptedException {
WriteAli writeAli = new WriteAli(10);
new Thread(() -> writeAli.printA()).start();
new Thread(() -> writeAli.printL()).start();
new Thread(() -> writeAli.printI()).start();
//just a test
Thread.sleep(2000);
System.out.println();
for(char c : writeAli.target)
{
System.out.print(c);
}
}
public WriteAli(int times) {
this.times = times;
this.target = new char[times * THREAD_NUM];
}
public void printA()
{
print('A', STATE_MOD_A);
}
public void printL()
{
print('l', STATE_MOD_L);
}
public void printI()
{
print('i', STATE_MOD_I);
}
private void print(char c, int stateMod)
{
for(int i=0; i<times;)
{
lock.lock();
if(state % THREAD_NUM == stateMod)
{
target[state++] = c;
i++;
System.out.print(c);
}
lock.unlock();
}
}
}
运行结果:
3.用户在交易下单购买时包含了A,B,C三款产品,个数不限;A、B、C各自的单价是unitPrice;由于存在一定的满减活动或者加价的购买行为,最终付款的总价是totalAmount;求每个产品按照金额占比分摊下的最终分摊后的金额是多少?
Long totalAmount;
public class Product {
/**
*产品单价
*/
private Long unitPrice;
/**
*产品数量
*/
private int quantity;
/**
*产品分摊所得金额
*/
private Long amount;
}
//除了基础编程,其实就是小学数学题吧,返璞归真。actualAmount = originAmount / sumAmount * totalAmount(actualAmount 产品分摊后的金额,originAmount 产品原始金额,sumAmount 产品原始总金额, totalAmount产品实际付款总价 )
import java.util.ArrayList;
import java.util.List;
public class Product {
/**
*产品单价
*/
private Long unitPrice;
/**
*产品数量
*/
private int quantity;
/**
*产品分摊所得金额
*/
private Double amount;
public Product(Long unitPrice, int quantity) {
this.unitPrice = unitPrice;
this.quantity = quantity;
}
public Long getUnitPrice() {
return unitPrice;
}
public void setUnitPrice(Long unitPrice) {
this.unitPrice = unitPrice;
}
public int getQuantity() {
return quantity;
}
public void setQuantity(int quantity) {
this.quantity = quantity;
}
public Double getAmount() {
return amount;
}
public void setAmount(Double amount) {
this.amount = amount;
}
public static void main(String[] args)
{
Long totalAmount = 10000L;
List<Product> productList = new ArrayList<>();
Product productA = new Product(300L, 20);
Product productB = new Product(400L, 10);
Product productC = new Product(200L, 10);
productList.add(productA);
productList.add(productB);
productList.add(productC);
Long sumAmount = 0L;
for(Product p : productList)
{
sumAmount += p.getUnitPrice() * p.getQuantity();
}
for(Product p : productList)
{
Long originAmount = p.getUnitPrice() * p.getQuantity();
Double actualAmount = 1.0 * originAmount / sumAmount * totalAmount;
p.setAmount(actualAmount);
}
for(int i=0; i<productList.size(); i++)
{
Product p = productList.get(i);
System.out.println("产品" + (char)('A'+i) + "的单价为:" + p.getUnitPrice() + " 数量为: "
+ p.getQuantity() + " 分摊后的总价为: " + p.getAmount());
}
}
}
运行结果: