面试了一堆985、211的研究生只是一个月薪6500的基础岗位,结果最后却要了一个普通二本生,找工作太疯狂了。
下午刷到个HR吐槽:来了一堆985、211研究生,抢一个月薪6500的基础岗,最后他却录了普通二本。
现在找工作跟抢回城票差不多,站票也得先上车。
6500招研究生,像用服务器跑Hello World。
我觉得HR最后选二本不奇怪,基础岗图的就是稳定、踏实、能上手。真要缓解这种疯狂,招聘软件那套‘按技能筛选+岗位测评’挺香,要求写清楚,候选人少走弯路,双方都省下加班面试的命。
面试题:最大交换
那个…我先说个场景哈,你们应该都有共鸣。
昨晚快下班,领导路过我工位,手一拍我肩膀:"东哥,来个简单的算法题放松一下,大不了明天不用来上班。" 我当时心里一咯噔,这话听着就不简单。
题目就是这个——最大交换。 意思是:给你一个非负整数,你最多只能把它里面两个数字对调一次,要让结果尽可能大。比如 2736,换完之后要变成 7236 这种。
听着很像老板说:“团队结构保持不变,就换两个人位置,利润给我翻最大”,对吧,熟悉的味道就来了。
一开始我脑子里蹦出来的就是最暴力那个写法: 反正就几位数嘛,那我把所有的俩位置都枚举一遍,全部交换一遍,看哪个结果最大不就完了。 代码脑补一下,大概就这样:
publicintmaximumSwapBruteForce(int num){
char[] arr = String.valueOf(num).toCharArray();
int n = arr.length;
int best = num;
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) {
swap(arr, i, j);
int cur = Integer.parseInt(new String(arr));
if (cur > best) {
best = cur;
}
swap(arr, i, j); // 换回去
}
}
return best;
}
privatevoidswap(char[] arr, int i, int j){
char t = arr[i];
arr[i] = arr[j];
arr[j] = t;
}
这个写法吧,小场面没问题,面试官要是心情好也能放你一马。 但你想想,要是这个数字长度上来点,比如搞个 10w 位的大整数,你这一套双层 for 下去,手机得当场起飞,和当年我不改 SpringBoot 默认配置直接上生产一个效果。
所以我扒拉了一下脑子,换个思路。 你想要“最大”,直觉是不是:越靠左的位数越关键? 第一位能变大一丁点,收益都比最后一位多得多,这不就是典型的“贪心”嘛。
那问题就变成了: 对每一位数字,我问一句: “后面有没有比我大的数字?有的话,把最靠右、最大的那一个换过来,我这辈子就值了。”
为啥要找“最靠右”的最大的? 你想,后面如果有多个 9,比如 1 9 8 9,这时候第一位 1 想换 9,你是换左边那个 9 还是右边那个 9? 当然是换最右边,留下一个 9 在前面,对整体更友好,后面的结构破坏得更少。
那就得先知道: 每个数字 0~9,它最后一次出现在哪个下标。 这个一想就简单了,字符串从左往右扫一遍,不断更新 last[digit] 就行,最后留下来的就是“最靠右”的位置。
这段核心逻辑我就写成这样了:
publicintmaximumSwap(int num){
char[] digits = String.valueOf(num).toCharArray();
int n = digits.length;
// 记录每个数字最后一次出现的位置
int[] last = newint[10];
for (int i = 0; i < n; i++) {
int d = digits[i] - '0';
last[d] = i;
}
// 从左往右,给每一位找“能换来的最大数字”
for (int i = 0; i < n; i++) {
int cur = digits[i] - '0';
// 从 9 往比它大的数字找
for (int d = 9; d > cur; d--) {
if (last[d] > i) { // 后面真的有更大的
char tmp = digits[i];
digits[i] = digits[last[d]];
digits[last[d]] = tmp;
return Integer.parseInt(new String(digits));
}
}
}
// 没找到更好的,就保持原数字
return num;
}
这个版本就挺顺手的: 一次从左到右扫描记录 last,O(n)。 再一次从左到右尝试换,里层最多 10 次,还是 O(n)。 相当于从“全公司两两调岗”变成了“每个人往上看 10 级领导有没有位置给你”,效率一下就上来了。
我写完自己测了几个数,你们可以脑补下这个调试输出,特别有画面感:
publicstaticvoidmain(String[] args){
System.out.println(debugSwap(2736)); // 7236
System.out.println(debugSwap(9973)); // 9973
System.out.println(debugSwap(109090)); // 910090
}
staticintdebugSwap(int num){
System.out.println("原始数字:" + num);
int ans = new 最大交换工具().maximumSwap(num);
System.out.println("交换后:" + ans);
System.out.println("----");
return ans;
}
staticclass 最大交换工具 {
publicintmaximumSwap(int num){
char[] digits = String.valueOf(num).toCharArray();
int[] last = newint[10];
for (int i = 0; i < digits.length; i++) {
last[digits[i] - '0'] = i;
}
for (int i = 0; i < digits.length; i++) {
int cur = digits[i] - '0';
for (int d = 9; d > cur; d--) {
if (last[d] > i) {
char t = digits[i];
digits[i] = digits[last[d]];
digits[last[d]] = t;
return Integer.parseInt(new String(digits));
}
}
}
return num;
}
}
你自己脑补一下控制台:
2736:第一位 2 看见后面有 7,直接和最后那个比 2 大的最大数字 7 换,变 7236,游戏结束。 9973:第一位 9 看后面最大也是 9;第二位 9 看后面只有 7、3,都比它小,这种就是已经“人生巅峰”,不用动。 109090:这个稍微有点意思,首位 1 往后看,能看到 9,而且最后那个 9 在比较靠后的位置,就把 1 和那个 9 换,变成 910090,看上去就“很有钱”的感觉。
写到这儿你会发现,这题其实就俩关键点:
一个是“从左到右尽早动手”,别磨叽; 另一个是“想换就换最大的那个,而且选离你最远那个”,这就很社会。
实际工作里也挺像的,SpringBoot、MySQL、消息队列那些默认配置,要是不自己动手调一调,迟早给你来一次“最大事故”,这个教训我已经用生产环境学过很多遍了,就不展开说了。
行,差不多就这样,我去续杯咖啡,你要是想把这个题写成别的语言,比如 Python、Go 啥的,也就把这套思路翻译一下就行,思路本身别换就稳了。