引言
从今年1月份跳槽至今已经半年了, 由于得知公司大多数都是在年初普调1k, 想到明年三四月份已经工作3年, 加上普调的1k只有12k, 在苏州这个城市还是有点少了, 所以在年中先面试一波, 积累一下经验, 为明年的跳槽做准备
去年6月份做过一次OD的机考, 当时第一题是递归、第二题是循环打印、第三题用的是回溯(但是超出时间了, 只通过60%的用例), 当时一面二面过了, HR面试挂了, 如果过了估计我就去OD了. 在去年12月份又面试了OD(因为机考成绩半年有效所以不用考了), 面试都通过了, 到谈薪的时候, 给了9.5k+2k, 当时也有这个非外包的自研, 给了11k, 考虑到OD后期跳槽可能有污点, 而且加班比较严重就拒绝了.
今年6月份boss上又有OD联系了, 想着就做一下, 积累一波, 然后考了机考, 运气比较好, 题目都比较简单, 没有错(。ì _ í。)
下面是机考还有一面二面的题目还有代码.
机考代码
import java.util.*;
/**
* @program:
* @description:
* @create: 2021/6/27 下午7:03
**/
public class Main {
/**
* 模拟复制粘贴, 输入一个 整数数组, 数组元素在1-5之间
* 1 输入, a 在文本上输入a
* 2 复制, ctrl + c 复制所选的元素到复制板中
* 3 剪切: ctrl + x 复制所选的元素到复制板中, 将当前文本内容清空
* 4 粘贴: ctrl + v 将复制板中的元素粘贴至文本中
* 5 选择: ctrl + a 将文本内容全选中
* 输出执行操作后文本中的内容
*/
public static void questionOne() {
Scanner in = new Scanner(System.in);
List<Integer> commands = new ArrayList<>();
while (in.hasNextInt()) {
commands.add(in.nextInt());
}
char element = 'a';
StringBuilder content = new StringBuilder();
boolean isSelected = false;
String copyContent = "";
// 没有元素: content.length() == 0
// 没有选择: selectFlag != true
// 选择的内容: selectContent = "xefdsafd"
// ! 拷贝不会撤销全选状态
for (int command : commands) {
switch (command) {
case 1: // 输入a
if (isSelected == true) {
content = new StringBuilder();
isSelected = false;
}
content.append(element);
break;
case 2: // 复制
if (isSelected == true) {
copyContent = content.toString();
}
break;
case 3: // 剪切
if (isSelected == true) {
copyContent = content.toString();
content = new StringBuilder();
}
break;
case 4: // 粘贴
if (isSelected == true) {
content = new StringBuilder();
isSelected = false;
}
content.append(copyContent);
break;
case 5: // 选择全部
if (content.length() != 0) {
isSelected = true;
} else {
isSelected = false;
}
default:
break;
}
}
System.out.println(content.length());
}
/**
* 输入两个数, 第一个是指定的和sum, 第二个是元素的个数n
* 输出是否存在n个连续的正整数(如1, 2, 3, 4), 其和等于sum
*
* 要求: 给定sum和n, 输出符合条件的起始num集合
*/
public static void questionTwo() {
Scanner in = new Scanner(System.in);
int sum, n;
while (in.hasNextInt()) {
sum = in.nextInt();
n = in.nextInt();
int afterDiffSum = 0;
for (int i = 0; i < n; i++) {
afterDiffSum += i;
}
int beginNum = (sum - afterDiffSum) / n;
for (int i = 0; i < n - 1; i++) {
System.out.print(beginNum + i);
System.out.println(" ");
}
System.out.println(beginNum + n - 1);
}
}
/**
* 给定一个服务器依赖列表字符串, 如 A-B, B-C 则表示A依赖于B, B依赖于C
* 再给定一个服务器故障的列表, 如A,B,C 则表示A、B、C故障
* 如果A依赖于B,服务B依赖于服务C,且C发生了故障,那么服务B和服务A也被认定为不可用
*
* 要求: 给定服务依赖列表、服务故障列表, 按顺序输出可用服务的列表
*/
public static void questionThree() {
Scanner in = new Scanner(System.in);
String dependentString = in.nextLine();
String breakdownString = in.nextLine();
String[] dependentArr = dependentString.split(",");
String[] breakdownServices = breakdownString.split(",");
// 被关联映射, key 被依赖的服务器, value,依赖该服务的服务列表
Map<String, Set<String>> dependenceMap = new HashMap<>();
Set<String> orderServices = new LinkedHashSet<>();
for (String str : dependentArr) {
String[] dependence = str.split("-");
String service = dependence[0], dependentService = dependence[1];
if (!orderServices.contains(service)) {
orderServices.add(service);
dependenceMap.put(service, new HashSet<>());
}
if (!orderServices.contains(dependentService)) {
orderServices.add(dependentService);
dependenceMap.put(dependentService, new HashSet<>());
}
dependenceMap.get(dependentService).add(service);
}
for (String breakdownService : breakdownServices) {
Queue<String> breakdownQueue = new LinkedList<>();
breakdownQueue.add(breakdownService);
while (!breakdownQueue.isEmpty()) {
String tempService = breakdownQueue.poll();
if (orderServices.contains(tempService)) {
orderServices.remove(tempService);
for (String influencedService : dependenceMap.get(tempService)) {
breakdownQueue.offer(influencedService);
}
}
}
}
if (orderServices.isEmpty()) {
System.out.println(",");
return;
} else {
System.out.println(String.join(",", orderServices));
}
}
}
感觉机考的难度在LeetCode上算是简单中等的难度, 只要LeetCode刷的多, 问题不大, 而且只要超过150分就可以进行后续的面试了
一面手撕算法
应该也是LeetCode的原题
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @program:
* @description:
* 题目描述
* 给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。
* 给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。
* 20:00:54 heyile h00451788 : 示例:
* 输入:"23"
* 输出:["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"].
*
* 示例:
* 输入:"23"
* 输出:["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"].
* @create: 2021/7/5 下午7:24
**/
public class Main {
public static final Map<Integer, char[]> numCharacterMap = new HashMap<Integer, char[]>(){
{
put(2, new char[]{'a', 'b', 'c'});
put(3, new char[]{'d', 'e', 'f'});
put(4, new char[]{'g', 'h', 'i'});
put(5, new char[]{'j', 'k', 'l'});
put(6, new char[]{'m', 'n', 'o'});
put(7, new char[]{'p', 'q', 'r', 's'});
put(8, new char[]{'t', 'u', 'v'});
put(9, new char[]{'w', 'x', 'y', 'z'});
}
};
/**
* 根据输入字符串, 返回所有它能表示的字母组合。
* @param s 字符串
* @return 字母组合
*/
public List<String> getCombineCharacterList(String s) {
List<String> res = new ArrayList<>();
backtrack(res, s, new StringBuilder(), 0, s.length());
return res;
}
/**
* 回溯
* @param res 返回的结果集
* @param s 输入字符串
* @param sb 当前拼接的字符串
* @param index 当前下标
* @param length 字符串长度
*/
private void backtrack(List<String> res, String s, StringBuilder sb, int index, int length) {
if (index == length) {
res.add(sb.toString());
return;
}
char[] chars = numCharacterMap.get(s.charAt(index) - '0');
for (int i = 0; i < chars.length; i++) {
sb.append(chars[i]);
backtrack(res, s, sb, index + 1, length);
sb.deleteCharAt(sb.length() - 1);
}
}
public static void main(String[] args) {
Main main = new Main();
main.getCombineCharacterList("33");
}
}
二面手撕算法
LeetCode中的原题, 第二题
/**
* @program:
* @description:
* @create: 2021/7/10 下午2:58
**/
public class Main {
/**
* 给定两个用链表表示的整数,每个节点包含一个数位。这些数位是反向存放的,也就是个位排在链表首部。
* 编写函数对这两个整数求和,并用链表形式返回结果。
* 要求:自己实现链表
* 示例:
* 输入:(7 -> 1 -> 6) + (5 -> 9 -> 2),即617 + 295
* 输出:2 -> 1 -> 9,即912
*/
public ListNode getTwoListNodeSum(ListNode node1, ListNode node2) {
int sum = getListNodeValue(node1) + getListNodeValue(node2);
return getListNode(sum);
}
/**
* 根据num获取 执行链表
* @param num
* @return
*/
private ListNode getListNode(int num) {
ListNode dummyNode = new ListNode(0);
ListNode preNode = dummyNode;
while (num > 0) {
ListNode currNode = new ListNode(num % 10);
preNode.next = currNode;
preNode = currNode;
num /= 10;
}
return dummyNode.next;
}
/**
* 获取链表的值
* @param node
* @return
*/
private int getListNodeValue(ListNode node) {
int value = 0;
int count = 0;
ListNode dummyNode = node;
while (dummyNode != null) {
value += dummyNode.val * Math.pow(10, count);
count++;
dummyNode = dummyNode.next;
}
return value;
}
public static class ListNode {
int val;
ListNode next;
public ListNode(int val) {
this.val = val;
}
}
public static void main(String[] args) {
ListNode node1 = new ListNode(7);
ListNode node2 = new ListNode(1);
ListNode node3 = new ListNode(6);
node1.next = node2;
node2.next = node3;
ListNode node4 = new ListNode(5);
ListNode node5 = new ListNode(9);
ListNode node6 = new ListNode(2);
node4.next = node5;
node5.next = node6;
Main main = new Main();
main.getTwoListNodeSum(node1, node4);
}
}
之前刷过, 当时忘了, 没必要转换成数字再求和, 可以一边遍历一遍求和输出的, 面试官还说了一下, 这样实现不太好, 会有溢出的问题, 下面是修改后的代码
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
ListNode head = null, tail = null;
int carry = 0;
while (l1 != null || l2 != null) {
int n1 = l1 != null ? l1.val : 0;
int n2 = l2 != null ? l2.val : 0;
int sum = n1 + n2 + carry;
if (head == null) {
head = tail = new ListNode(sum % 10);
} else {
tail.next = new ListNode(sum % 10);
tail = tail.next;
}
carry = sum / 10;
if (l1 != null) {
l1 = l1.next;
}
if (l2 != null) {
l2 = l2.next;
}
}
if (carry > 0) {
tail.next = new ListNode(carry);
}
return head;
}
}
链表类题目的重点有以下几个方面
- 画图, 通过图可以很直观的看到节点的指向,
- 设置哨兵节点dummyNode, 这样在返回的时候直接返回dummyNode.next即可
- 如果有需要对前一个节点也要进行操作的话, 可需要申请一个 prevNode...
总结
手撕算法是挺好的一个环节, Talk is cheap, Show me the code, 从代码中能看出这个人的水平, 从最基本的变量命名 到 方法分层 、代码注释 、逻辑思维, 到对 数据结构和算法 的掌握程度.
强烈推荐 极客时间里王争的 《数据结构和算法之美》 (点击链接跳转到课程页面), 并搭配LeetCode(按标签来刷题) 使用更佳, 学习完对后面的工作、理解一些技术的原理帮助都很大. 说白了 程序就是殊绝结构加算法
慢慢来, 比较快