数字的位操作——393、172、258、319、405、171

393. UTF-8 编码验证(中等)

给定一个表示数据的整数数组 data ,返回它是否为有效的 UTF-8 编码。

UTF-8 中的一个字符可能的长度为 1 到 4 字节,遵循以下的规则:

  1. 对于 1 字节 的字符,字节的第一位设为 0 ,后面 7 位为这个符号的 unicode 码。
  2. 对于 n 字节 的字符 (n > 1),第一个字节的前 n 位都设为1,第 n+1 位设为 0 ,后面字节的前两位一律设为 10 。剩下的没有提及的二进制位,全部为这个符号的 unicode 码。

这是 UTF-8 编码的工作方式:

      Number of Bytes  |        UTF-8 octet sequence
                       |              (binary)
   --------------------+---------------------------------------------
            1          | 0xxxxxxx
            2          | 110xxxxx 10xxxxxx
            3          | 1110xxxx 10xxxxxx 10xxxxxx
            4          | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

x 表示二进制形式的一位,可以是 0 或 1

注意:输入是整数数组。只有每个整数的 最低 8 个有效位 用来存储数据。这意味着每个整数只表示 1 字节的数据。

示例 1:

输入:data = [197,130,1]
输出:true
解释:数据表示字节序列:11000101 10000010 00000001。
这是有效的 utf-8 编码,为一个 2 字节字符,跟着一个 1 字节字符。

示例 2:

输入:data = [235,140,4]
输出:false
解释:数据表示 8 位的序列: 11101011 10001100 00000100.
前 3 位都是 1 ,第 4 位为 0 表示它是一个 3 字节字符。
下一个字节是开头为 10 的延续字节,这是正确的。
但第二个延续字节不以 10 开头,所以是不符合规则的。

提示:

  • 1 <= data.length <= 2 * 104
  • 0 <= data[i] <= 255

解法一、模拟

关键在于掩码数组和合法性数组。validNum可以确认首个码到底领导多少长度,记录长度,遍历每个子码的合法性。其中要注意,如果长度还没检索完、data数组已经结束,需要返回false。

掩码真的很好用!!!

class Solution {
    private static final int []mask = {0b11000000,0b10000000,0b11100000,0b11110000,0b11111000};
    private static final int []valid = {0b10000000,0b00000000,0b11000000,0b11100000,0b11110000};
    public static boolean validUtf8(int[] data) {
        int lenSum = data.length;
        int ptr = 0;
        while(ptr < lenSum){
            int len = validNum(data[ptr]);
            if(len == -1)return false;
            for(int i = 1;i < len;i++){
                ptr++;
                if(ptr >= lenSum || (data[ptr]&mask[0]) != valid[0])return false;
            }
            ptr++;
        }
        return true;
    }
    public static int validNum(int a){
        for(int i = 1;i < 5;i++){
            if((a & mask[i]) == valid[i]){
                return i;
            }
        }
        return -1;
    }
}

 
172. 阶乘后的零(中等)

给定一个整数 n ,返回 n! 结果中尾随零的数量。

提示 n! = n * (n - 1) * (n - 2) * ... * 3 * 2 * 1

示例 1:

输入:n = 3
输出:0
解释:3! = 6 ,不含尾随 0

示例 2:

输入:n = 5
输出:1
解释:5! = 120 ,有一个尾随 0

示例 3:

输入:n = 0
输出:0

提示:

  • 0 <= n <= 104

进阶:你可以设计并实现对数时间复杂度的算法来解决此问题吗?

解法一、因数

只需要考虑1-n中多少个5

class Solution {
    public int trailingZeroes(int n) {
        int ans = 0;
        for (int i = 5; i <= n; i += 5) {
            for (int x = i; x % 5 == 0; x /= 5) {
                ++ans;
            }
        }
        return ans;
    }
}

作者:力扣官方题解
链接:https://leetcode.cn/problems/factorial-trailing-zeroes/solutions/1360892/jie-cheng-hou-de-ling-by-leetcode-soluti-1egk/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

 

 解法二、解法一优化

一是每个每个数,二则是分阶段。以221为例,除一次是44,代表从1-221里一共有44个5出现。再除一次是8,代表1-221里有8个5*5出现(其中有8个5已经在第一次统计过),再除一次是1,代表有一个5*5*5出现(其中有两个已经在前两次出现过)。

class Solution {
    public int trailingZeroes(int n) {
        int ans = 0;
        while (n != 0) {
            n /= 5;
            ans += n;
        }
        return ans;
    }
}

作者:力扣官方题解
链接:https://leetcode.cn/problems/factorial-trailing-zeroes/solutions/1360892/jie-cheng-hou-de-ling-by-leetcode-soluti-1egk/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

258. 各位相加(简单)

给定一个非负整数 num,反复将各个位上的数字相加,直到结果为一位数。返回这个结果。

示例 1:

输入: num = 38
输出: 2 
解释: 各位相加的过程为38 --> 3 + 8 --> 11
11 --> 1 + 1 --> 2
由于 2 是一位数,所以返回 2。

示例 2:

输入: num = 0
输出: 0

提示:

  • 0 <= num <= 231 - 1

进阶:你可以不使用循环或者递归,在 O(1) 时间复杂度内解决这个问题吗?

解法一、递归

逐位相加直到个位数

class Solution {
    public int addDigits(int num) {
        int sum = 0;
        while(num != 0){
            sum += num%10;
            num/=10;
        }
        return sum >=10? addDigits(sum):sum;
    }
}

 

解法二、数根

其实就是求数根。数根的性质:a的数根等于(a-1)%9 +1

class Solution {
    public int addDigits(int num) {
        return (num - 1) % 9 + 1;
    }
}


319. 灯泡开关

初始时有 n 个灯泡处于关闭状态。第一轮,你将会打开所有灯泡。接下来的第二轮,你将会每两个灯泡关闭第二个。

第三轮,你每三个灯泡就切换第三个灯泡的开关(即,打开变关闭,关闭变打开)。第 i 轮,你每 i 个灯泡就切换第 i 个灯泡的开关。直到第 n 轮,你只需要切换最后一个灯泡的开关。

找出并返回 n 轮后有多少个亮着的灯泡。

解法一、模拟

由题可见,其实是分析因数。如第六个,在1/2/3/6的时候会变化。变化偶数次则关灯,奇数次则开灯。最后是超时。。

class Solution {
    public int bulbSwitch(int n) {
        int sum = 0;
        for(int i = 1;i<= n;i++){
            int t = 0;
            for(int j = 1;j <= i;j++){
                if(i % j == 0)t++;
            }
            if(t % 2 == 1)sum++;
        }
        return sum;
    }
}

解法二、解法一优化

不妨发现,对于数字n,一旦k是约数,那么n/k也是,成对出现。则一定是完全平方数才能是奇数,也就是看n里面有多少完全平方数。0.5是为了精度考虑

class Solution {
    public int bulbSwitch(int n) {
        return (int) Math.sqrt(n + 0.5);
    }
}

作者:力扣官方题解
链接:https://leetcode.cn/problems/bulb-switcher/solutions/1099002/deng-pao-kai-guan-by-leetcode-solution-rrgp/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

 

405. 数字转换为十六进制数(简单)

 给定一个整数,编写一个算法将这个数转换为十六进制数。对于负整数,我们通常使用 补码运算 方法。

注意:

  1. 十六进制中所有字母(a-f)都必须是小写。
  2. 十六进制字符串中不能包含多余的前导零。如果要转化的数为0,那么以单个字符'0'来表示;对于其他情况,十六进制字符串中的第一个字符将不会是0字符。 
  3. 给定的数确保在32位有符号整数范围内。
  4. 不能使用任何由库提供的将数字直接转换或格式化为十六进制的方法。

示例 1:

输入:
26

输出:
"1a"

示例 2:

输入:
-1

输出:
"ffffffff"

解法一、补码+补数

对于负数,它的十六进制码所对应的无符号整数,和它相差2^32次方。 

public class Solution {
    public static String toHex(int num) {
        // 定义十六进制字符映射
        char[] hexChars = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
        if (num == 0) {
            return "0";
        }

        StringBuilder ans = new StringBuilder();
        long unsignedNum = num;
        
        // 处理负数的情况,转换为无符号数
        if (num < 0) {
            unsignedNum = (long) num + (long) Math.pow(2, 32);
        }

        // 转换为十六进制
        while (unsignedNum != 0) {
            int digit = (int) (unsignedNum % 16);
            ans.append(hexChars[digit]);
            unsignedNum /= 16;
        }

        return ans.reverse().toString();
    }
}

 

解法二、补码运算

正数的补码是其本身,负数的补码是取反加一,二进制本身就已经是补码的表达了(我忽略了这个···),所以可以直接按4分组来处理。

class Solution {
    public String toHex(int num) {
        if (num == 0) {
            return "0";
        }
        StringBuffer sb = new StringBuffer();
        for (int i = 7; i >= 0; i --) {
            int val = (num >> (4 * i)) & 0xf;
            if (sb.length() > 0 || val > 0) {
                char digit = val < 10 ? (char) ('0' + val) : (char) ('a' + val - 10);
                sb.append(digit);
            }
        }
        return sb.toString();
    }
}

作者:力扣官方题解
链接:https://leetcode.cn/problems/convert-a-number-to-hexadecimal/solutions/1027297/shu-zi-zhuan-huan-wei-shi-liu-jin-zhi-sh-2srt/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

 
171. Excel 表列序号(简单)

给你一个字符串 columnTitle ,表示 Excel 表格中的列名称。返回 该列名称对应的列序号 。

例如:

A -> 1
B -> 2
C -> 3
...
Z -> 26
AA -> 27
AB -> 28 
...

示例 1:

输入: columnTitle = "A"
输出: 1

示例 2:

输入: columnTitle = "AB"
输出: 28

示例 3:

输入: columnTitle = "ZY"
输出: 701

提示:

  • 1 <= columnTitle.length <= 7
  • columnTitle 仅由大写英文组成
  • columnTitle 在范围 ["A", "FXSHRXW"] 内

 解法一、模拟运算

正着数直接*26,比Math.pow每次另算简单。本质上是进制转换题

class Solution {
    public int titleToNumber(String columnTitle) {
        int ans=0;
        for (int i = 0; i < columnTitle.length(); i++) {
            ans= columnTitle.charAt(i)-'A'+1+ans*26;
        }

        return ans;
    }
}

 


碎碎念

  • 熟悉了一点补码运算和进制转换,学了不少有关约数的,懂了数根
  • 学会了掩码
  • 昨天刚破100题~接下来再写一天,然后要断一段时间咯,等回学校
  • 11
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值