程序员老鬼

以前大家都担心被公司裁员,现在就不一样了!似乎只要公司愿意支付N+1,立马就有一堆员工自告奋勇

刚看到个贴子,网友吐槽说现在职场怪得很——以前怕被裁员,现在倒好,只要公司喊一句“N+1”,一堆人主动排队,整得像抢名额一样。

Image

我觉得这事吧,说到底反映的是大家对工作的态度变了。很多人早就对公司没感情了,留着只是图那点稳定,一旦能体面走人,还顺便拿一笔补偿,那确实比死耗着强。

这不是大家变佛了,而是算账算明白了。钱少、压力大、成长有限,那“N+1”就像是雨天打车遇到顺风车,能走就走,谁还跟自己过不去。

不过话说回来,职场也别全靠逃跑解决问题。能提升的能力还是得提升,该积累的经验也别落下。【备注:文末可领最新资料】

面试题:UTF-8 编码验证

这个题说白了,就是让你当一回“UTF-8 检票员”,一串整型数组丢给你,每个数字是一个字节(0~255),你要判断:这一串字节是不是一段合法的 UTF-8 编码。

我先用很白话的方式把 UTF-8 规则过一遍,别急着看代码,不然位运算一上来,脑子一下就糊了。

先记一个大概场景:UTF-8 里,一个“字符”会被拆成 1~4 个字节。那这些字节长啥样呢?

1)如果这个字符只用 1 个字节表示,那这个字节的二进制长相是:0xxxxxxx也就是最高位是 0,后面 7 位随便。

2)如果是 2 个字节: 第一字节:110xxxxx第二字节:10xxxxxx

3)3 个字节: 第一字节:1110xxxx后面两个字节:都得是 10xxxxxx

4)4 个字节: 第一字节:11110xxx后面三个字节:都得是 10xxxxxx

所以有几个关键点你得死记一下(面试也基本就考这几个):

  • 开头字节只可能是这几种模式:

    • 0xxxxxxx(单字节)
    • 110xxxxx(2 字节开头)
    • 1110xxxx(3 字节开头)
    • 11110xxx(4 字节开头) 其他比如 111110xx、10xxxxxx 开头的,都不合法当“起始字节”。
  • 只要是后续字节,统一长相:10xxxxxx,也就是二进制前两位必须是 10。

  • 字节数量必须刚刚好,比如你说这是个 3 字节字符,那后面就必须跟着 2 个合法的 10xxxxxx;中途断了、数量不够、格式不对,都算非法。

有了上面的规则,算法思路就很自然了,按顺序扫数组就行:

  1. 从左到右遍历每一个字节。

  2. 对当前这个字节,如果它是起始字节:

  • 看它属于哪种模式(单字节 / 2 字节 / 3 字节 / 4 字节),顺便算出“后面还应该跟几个字节”。
  • 然后检查后面的这些字节是不是都满足 10xxxxxx。
  • 如果当前字节看起来是“后续字节”(比如最高两位是 10),但我们此时并不在一个多字节字符的中间,那就直接非法。

  • 遍历完以后,还要确认:我们没有处在一个“还差几个字节没读完”的半截状态。

  • 整个过程就是一个线性扫描,时间复杂度 O(n),空间 O(1),还挺清爽。

    具体到 Java 代码,常见实现会用按位与来判断模式,比如:

    • (b & 0x80) == 0    → 最高位是 0
    • (b & 0xE0) == 0xC0 → 110xxxxx
    • (b & 0xF0) == 0xE0 → 1110xxxx
    • (b & 0xF8) == 0xF0 → 11110xxx
    • (b & 0xC0) == 0x80 → 10xxxxxx

    注意一点:题目里给的是 int[] data,但每个元素只是 0~255 范围,我们做位运算前一般会 & 0xFF 一下,避免被 Java 当成带符号搞出负数来。

    直接上一个完整、可用的实现:

    publicclassUtf8Validator{

    /**
         * 校验一串字节是否是合法的 UTF-8 编码
         * data[i] 范围:0~255
         */

    publicbooleanvalidUtf8(int[] data){
    int n = data.length;
    int i = 0;

    while (i < n) {
    int b = data[i] & 0xFF; // 强制当无符号字节看

    int len; // 当前这个字符总共需要的字节数

    if ((b & 0x80) == 0) {
    // 0xxxxxxx,单字节字符
                    len = 1;
                } elseif ((b & 0xE0) == 0xC0) {
    // 110xxxxx,2 字节
                    len = 2;
                } elseif ((b & 0xF0) == 0xE0) {
    // 1110xxxx,3 字节
                    len = 3;
                } elseif ((b & 0xF8) == 0xF0) {
    // 11110xxx,4 字节
                    len = 4;
                } else {
    // 其他模式一律非法
    returnfalse;
                }

    // 看剩下的字节够不够
    if (i + len > n) {
    returnfalse;
                }

    // 检查后续字节是否都是 10xxxxxx
    for (int j = 1; j < len; j++) {
    int c = data[i + j] & 0xFF;
    if ((c & 0xC0) != 0x80) {
    returnfalse;
                    }
                }

    // 跳到下一个字符的起始字节
                i += len;
            }

    returntrue;
        }

    publicstaticvoidmain(String[] args){
            Utf8Validator validator = new Utf8Validator();

    int[] ok = {197, 130, 1}; // 合法:[11000101][10000010][00000001]
    int[] bad = {235, 140, 4}; // 非法,最后一个不是 10xxxxxx

            System.out.println(validator.validUtf8(ok));   // true
            System.out.println(validator.validUtf8(bad));  // false
        }
    }

    你可以脑子里过一下主流程,比如那个合法的例子 197,130,1:

    • 197 → 二进制大概是 11000101,判定为 2 字节起始,len=2
    • 检查下一个字节 130:10xxxxxx ✔
    • 然后 i 跳到最后一个字节 1:00000001,单字节 ✔
    • 扫完数组没有半截,返回 true。

    这类题本质就是“按规格写状态机”,先把规则在脑子里变成几个固定的二进制模板,再配一点位运算,代码就顺了。你要是面试碰到,边写可以一边嘴里念:0 开头单字节,110 开头俩,1110 开头仨,11110 开头四个,后面全是 10 开头……记住这句基本就过关了。

    -END-

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

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