程序员老鬼

年薪90万,税后到手也就68万,天天加班到凌晨一点,回家还被老婆数落,家里柴米油盐、人情往来全是老婆打理,算到年底居然只存了20万出头

刚看到个贴子:有哥们年薪90万,税后68万,天天加班到凌晨一点,回家还被老婆数落“拿命换钱”,平时省吃俭用,一年底一算只存了二十来万,心态直接崩。

Image

这事背后是典型的“看着光鲜、实际困顿”。房贷、车贷、老人小孩、人情往来,一样都不便宜,钱就跟下水道一样哗啦啦流。

从我的角度看,关键有三点: 第一,夫妻得把账摊开算清楚,钱到底花哪了,不要只靠情绪吵。 第二,想明白阶段目标,是先拼本金,还是先保身体和家庭关系。 第三,公司真是拿命换钱、又看不到出口,那就得早点谋退路。

面试题:自定义字符串排序

昨天晚上快十一点了,我在公司楼下啃着外卖,产品突然微信我一句:”东哥,这个列表能不能按我们自己定的顺序排一下,不是字典序那种。“我当时就笑了,这不就是经典的“自定义字符串排序”嘛。

先把题目用人话说清楚哈: 给你两个字符串:

  • order:表示“我想要的字符优先级顺序”,比如 "cba"
  • s:原始字符串,比如 "abcd"

要求是:把 s 里的字符,按照 order 里给的顺序排好,order 里没提到的字符就随便丢后面就行。 上面这个例子,结果就是 “cbad”,因为 c 在前,b 第二,a 第三,d 没在规则里就排最后。

我一般会先用一种好理解、好写、性能也不错的写法:计数 + 拼接,就像先数清楚有哪些菜,再按顺序装盘。

你可以想象一张“统计表”:

  1. 先扫一遍 s,把每个字符出现次数记下来。
  2. 然后按 order 的顺序,把这些字符一个个按“记的次数”拼到结果里。
  3. 最后,把那些 order 没提到,但在 s 里出现过的字符,也按它们本来的样子补到后面。

这个过程就完全符合产品的要求了,而且时间复杂度其实就是 O(n),n 是 s 的长度。

先写一个通用一点的版本,支持所有 ASCII 字符,不只小写字母:

publicclassCustomSort{

// 核心方法:按 order 里面的顺序,对 s 进行自定义排序
public String customSortString(String order, String s){
// 简单粗暴:开 256,够 ASCII 用了
int[] cnt = newint[256];

// 1. 统计 s 里每个字符的次数
for (char c : s.toCharArray()) {
            cnt[c]++;
        }

        StringBuilder sb = new StringBuilder();

// 2. 先按照 order 的顺序,把对应字符都拼上去
for (char c : order.toCharArray()) {
while (cnt[c] > 0) {
                sb.append(c);
                cnt[c]--;
            }
        }

// 3. 把剩下的、order 里没定义的字符也补上去
for (int i = 0; i < 256; i++) {
while (cnt[i] > 0) {
                sb.append((char) i);
                cnt[i]--;
            }
        }

return sb.toString();
    }

publicstaticvoidmain(String[] args){
        CustomSort cs = new CustomSort();
        String order = "cba";
        String s = "abcd";
        System.out.println(cs.customSortString(order, s)); // 输出 cbad
    }
}

这个写法几个点你脑子里过一下就行:

  • cnt[c] 里就存着字符 c 在 s 中出现了多少次
  • 第一个循环干完后,所有字符都被记账了
  • 第二个循环严格按 order 排序、并且每次 append 会把对应计数减掉
  • 最后一个循环扫一遍所有字符,把没用完的都补上去,顺序就“无所谓”了,题目也不要求

如果你确定题目只会给你小写字母 'a' ~ 'z',那数组可以开成 int[26],索引用 c - 'a',更节省一点。

上面这个是偏“算法题”一点的写法。但我工作里遇到的,有时候是“对象列表”要自定义排序,比如按“城市优先级”、“频道优先级”之类,这时候计数数组就不太好用了,更常见的是写一个自定义比较器。

比如把“字符优先级”抽成一个 rank 表,然后交给 Arrays.sort 去排:

public String customSortString2(String order, String s){
// 1. 先把每个字符在 order 里的优先级记下来
    Map<Character, Integer> rank = new HashMap<>();
for (int i = 0; i < order.length(); i++) {
        rank.put(order.charAt(i), i);  // 越靠前优先级越高
    }

// 2. Java 想自定义排序得用包装类型
    Character[] arr = new Character[s.length()];
for (int i = 0; i < s.length(); i++) {
        arr[i] = s.charAt(i);
    }

// 3. 自定义比较器:没出现在 order 里的,统一给个很大的 rank
    Arrays.sort(arr, (a, b) -> {
int ra = rank.getOrDefault(a, Integer.MAX_VALUE);
int rb = rank.getOrDefault(b, Integer.MAX_VALUE);
return Integer.compare(ra, rb);
    });

// 4. 再拼成字符串
    StringBuilder sb = new StringBuilder(arr.length);
for (char c : arr) {
        sb.append(c);
    }
return sb.toString();
}

这个思路跟我们平时给“状态”、“等级”这些东西排优先级是一个套路:先映射成数字,再用比较器搞定。写复杂业务排序的时候,也可以直接套这个思路,字符换成对象、rank 换成各种业务权重就行了。

总之,这道“自定义字符串排序”,你如果能把上面两种写法都吃透,以后产品说“我要按我说的顺序来排”,你心里就一点都不慌了,顶多吐槽一句:你早点说啊,我都准备下班了…

-END-

我为大家打造了一份RPA教程,完全免费:songshuhezi.com/rpa.html

最后给大家分享一份不错的副业资料,点击下方公众号,回复关键字: 副业 领,也可以链接我微信:hls404