华为OD后端开发岗笔面试记录
三道题, 分值分别为100, 100, 200. 做到150分以上就有面试机会, 半年内只能考一次.
笔试
第一题: 寻找子串
最后过了97%
字符串1和字符串2都是由数字和小写字母组成.
其中字符串1采用了数字和小写字母a-f作为分隔符, 可以被分割为多个子串.
要求输出字符串1的子串, 满足: 该子串中不同字符的数量不大于字符串2中不同字符的数量.
如果有多个子串, 按照子串的长度降序排列; 如果子串长度相同, 按照字母序降序排列.
输入为两行, 第一行为字符串1, 第二行为字符串2.
思路: 处理字符串, 自己写一个Comparator
.
import java.util.Arrays; |
第二题: 求排列组合数
最后过了95%
系统中有两个任务, 第一个任务的执行时间为task1, 第二个任务的执行时间为task2, 总共能够执行n个任务.
求执行的n个任务可能花费的时间.
输入: task1,task2,n
输出: [time1, time2, …]
思路: 组合.
- 易错点在于task1和task2执行时间相同时, 数组中不能有重复的时间.
- 直接打印数组:
Arrays.toString(arr);
.
import java.util.Arrays; |
第三题: 求树的第x行第y个节点
最后过了83%.
输入:
第一行为节点数, 接下来的每一行第一个数字是节点的value, 后面的数是它的孩子.
最后一行是”坐标”.
输出:
{节点值}, 如果不存在节点则输出{}
样例输入:6
15 1 2 3
6 4
7 5
23
9
11
2 2
样例输出:{7}
代码:import java.util.*;
public class Exm3 {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
// 构建树
//创建节点
int num = Integer.parseInt(in.nextLine());
TreeNode[] nodes = new TreeNode[num];
List<Integer>[] childs = new List[num];
for(int i = 0; i < num; i++) {
String[] line = in.nextLine().split("\\s+");
nodes[i] = new TreeNode(Integer.parseInt(line[0]));
childs[i] = new ArrayList<Integer>();
for(int j = 1; j < line.length; j++) childs[i].add(Integer.valueOf(line[j]));
}
//连接节点
for(int i = 0; i < num; i++) {
List<Integer> cs = childs[i];
TreeNode curNode = nodes[i];
for(int idx : cs) {
curNode.childs.add(nodes[idx]);
}
}
TreeNode root = nodes[0];
// 构建完成
// 用队列进行层次遍历
String[] loc = in.nextLine().split("\\s+");
int x = Integer.parseInt(loc[0]);
int y = Integer.parseInt(loc[1]);
Queue<TreeNode> queue = new ArrayDeque<TreeNode>();
int curRow = 0;
queue.offer(root);
int curLineNum = 1;
int nextLineNum = 0;
while(curRow < x && !queue.isEmpty()) {
TreeNode cur = queue.poll();
List<TreeNode> curChilds = cur.childs;
for(TreeNode child : curChilds) {
queue.offer(child);
nextLineNum++;
}
curLineNum--;
if(curLineNum == 0) {
curLineNum = nextLineNum;
nextLineNum = 0;
curRow++;
}
}
if(queue.isEmpty()) {
System.out.println("{}");
return;
}
//寻找y
for(int j = 0; j < y && !queue.isEmpty(); j++) {
queue.poll();
}
if(queue.isEmpty()) {
System.out.println("{}");
return;
} else System.out.printf("{%d}\n", queue.poll().value);
}
private static class TreeNode {
int value;
List<TreeNode> childs = new ArrayList<>();
private TreeNode(int value, List<TreeNode> childs) {
this.value = value;
this.childs = childs;
}
private TreeNode(int value) {
this.value = value;
}
}
}
技术一面
基础知识部分
- 问我的代码量. 我整个懵逼了, 怎么直接问这个的? 最后我说几千应该是有的.
- 进程和线程了解吗?进程间怎么进行通信?
- 线程间是如何进行同步的?(注意区分进程通信和线程同步)
- 怎么优化数据库的查询速度? (没复习到这个, 详见Javaguide)
答:- 使用InnoDB引擎;
- 统一使用UTF-8编码;
- 反范式;
- 控制数据表的大小, 过大的表会导致修改表的存储结构(or跨区存储);
- 限制索引数量(否则会导致插入和删除时效率变低)
- 选择索引的顺序: 区分度最高的索引在最左侧, 最频繁使用的放在最左侧, 字段长度小的放在最左侧;
- HTTP和TCP了解多少? 向我介绍一下?
代码部分
- 删除链表后面的一个节点;
- 查询表A的所有项目, 并且按照B进行排列.
- 还有一个Linux的题目, 我给忘记了呜呜..
手撕代码
[输入] 第一行一个整数T(1 <= T <= 100), 表示有T组测试数据。对于每组测试数据:一行无序的括号,最长有3000个半括号。
[输出] 一个整数,即最少要添加的括号数使之成为满足题意的括号序列。
[样例1]
输入样例:3
[[[
[)(]
[)
输出样例:3
2
2
技术二面
手撕代码: 力扣554 砖墙 怎么新建一个
- java的进程和线程了解吗?
- ArrayList怎么进行扩容?
- ArrayList和LinkedList区别?
- 线程池了解吗?
- ThreadLocal了解吗?
- JVM的垃圾回收算法?
- Java的锁向我介绍一下?
知识点
ThreadLocal
ThreadLocal类主要解决的就是让每个线程绑定自己的值,可以将ThreadLocal类形象的比喻成存放数据的盒子,盒子中可以存储每个线程的私有数据。
- Thread.start() 和 run()的区别?
直接运行run方法, 会把它当成main线程下的一个普通方法运行, 并不是多线程.
start方法会创建新的线程, 等待分配到时间片就开始运行.
- ThreadLocal有什么用?
让每个线程都绑定自己的值. - ThreadLocal原理了解吗?
每一个线程都有一个ThreadLocalMap
, key是ThreadLocal, value是存储的对象. - ThreadLocal内存泄漏问题是怎么导致的?
key是弱引用而value是强引用. ThreadLocal在调用set, get, remove方法后会清理key为null的记录. 使用完ThreadLocal的方法后最好手动调用remove方法.
补充: Java中强引用, 弱引用, 软引用, 虚引用测试
理解Java中各种引用
tips: 软连接那里, 在命令行执行javac softref.java
java -Xmx20m softref
线程池
- 什么是线程池?
线程池就是管理一系列线程的资源池. 当有任务要处理时,直接从线程池中获取线程来处理,处理完之后线程并不会立即被销毁,而是等待下一个任务. - 为什么要使用线程池?
节约资源; 提高响应速度; 提高线程可管理性. - 如何创建线程?
使用ThreadPoolExecutor
构造函数的方式创建, 而不使用Executer
.
原因:FixedThreadPool
,SingleThreadPool
和ScheduledThreadPool
使用无界的阻塞队列 - 线程池的重要参数
一图流corePoolSize
任务队列未达到队列容量时, 最大可以同时运行的线程数量;maxiumumPoolSize
任务队列中存放的任务达到队列容量的时候,当前可以同时运行的线程数量变为最大线程数;workQueue
新任务来的时候会先判断当前运行的线程数量是否达到核心线程数,如果达到的话,新任务就会被存放在队列中. - 线程池的饱和策略?
ThreadPoolExcuter.AbortPolicy
: 抛出RejectedExcutionException
来拒绝新任务的处理.ThreadPoolExcuter.CallerRunsPolicy
: 把任务退回给调用者, 使用调用者的线程来执行任务.ThreadPoolExcuter.DiscardPolicy
: 不处理新任务, 直接丢掉.ThreadPoolExcuter.DiscardOldestPolicy
: 丢弃最早的未处理请求; - 线程池常用的阻塞队列有哪些?
- 无界队列
LinkedBlockingQueue
:FixedThreadPool
,SingleThreadExcuter
- 同步队列
CacheThreadPool
- 延迟阻塞队列
ScheduledThreadPool
和SingleThreadScheduledExcutor
ArrayList扩容机制
JVM垃圾回收详解
技术三面
因为前两面定级不一致, 开始第三面.
第三面主要就是挖简历.
手撕代码: 力扣105: 从前序和中序恢复二叉树.
最后定岗定到测开了, HR面完就拒了.