Java&C++题解与拓展——leetcode762. 二进制表示中质数个计算置位【分治统计二进制中的1学习】

每日一题做题记录,参考官方和三叶的题解

题目要求

在这里插入图片描述

思路一:模拟

统计 1 1 1的数量……DNA动了,那不是昨天树状数组的lowbit么!
首先用数组替代哈希表记录一下质数,然后用lowbit统计当前值二进制中 1 1 1的数量与表作比较即可。

Java

class Solution {
    static boolean[] hash = new boolean[35];
    static {
        int[] prime = new int[]{2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31}; //质数表
        for(int p : prime)
            hash[p] = true;
    }
    public int countPrimeSetBits(int left, int right) {
        int res = 0;
        for(int i = left; i <= right; i++) {
            int x = i, cnt = 0;
            while(x != 0 && ++cnt >= 0) //统计计算置位
                x -= (x & -x); //lowbit
            if(hash[cnt])
                res++;
        }
        return res;
    }
}
  • 时间复杂度: O ( ( r i g h t − l e f t ) ∗ log ⁡ r i g h t ) O((right-left)*\log right) O((rightleft)logright)lowbit的复杂度为 O ( log ⁡ r i g h t ) O(\log right) O(logright)
  • 空间复杂度: O ( C ) O(C) O(C)

C++

【因为没有静态初始化块,所以就写里面了。】

class Solution {
public:
    int countPrimeSetBits(int left, int right) {
        bool hash[35];
        memset(hash, false, 35);
        int prime[10] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29};
        for(int p : prime)
            hash[p] = true;

        int res = 0;
        for(int i = left; i <= right; i++) {
            int x = i, cnt = 0;
            while(x != 0 && ++cnt >= 0) //统计计算置位
                x -= (x & -x); //lowbit
            if(hash[cnt])
                res++;
        }
        return res;
    }
};
  • 时间复杂度: O ( ( r i g h t − l e f t ) ∗ log ⁡ r i g h t ) O((right-left)*\log right) O((rightleft)logright)
  • 空间复杂度: O ( C ) O(C) O(C)

思路二:分治

更优的统计二进制中 1 1 1的数量的方法是分治思路,分组进行统计。

分治统计 1 1 1的个数

  • 思路:
    • 将待求的 v a l val val位一组进行划分,统计每组内 1 1 1的个数,将结果写在当前组所在的位置上,得到新的 v a l val val
    • 将更新的 v a l val val位一组进行划分,计算当前组内 1 1 1的个数,将结果写在当前组所在的位置上,得到新的$val;
    • …………;
    • ……十六……;
    • 此时得到的 v a l val val即为当前值含有的 1 1 1的数量。
  • 原数值可看作位一组划分并统计的结果;
  • 这个图画的不错
  • 其时间复杂度仅为 O ( log ⁡ log ⁡ n ) O(\log \log n) O(loglogn)

Java

class Solution {
    static boolean[] hash = new boolean[35];
    static {
        int[] prime = new int[]{2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31}; //质数表
        for(int p : prime)
            hash[p] = true;
    }
    public int countPrimeSetBits(int left, int right) {
        int res = 0;
        for(int i = left; i <= right; i++) {
            int x = i;
            x = (x & 0x55555555) + ((x >>> 1) & 0x55555555);
            x = (x & 0x33333333) + ((x >>> 2) & 0x33333333);
            x = (x & 0x0f0f0f0f) + ((x >>> 4) & 0x0f0f0f0f);
            x = (x & 0x00ff00ff) + ((x >>> 8) & 0x00ff00ff);
            x = (x & 0x0000ffff) + ((x >>> 16) & 0x0000ffff);
            if(hash[x])
                res++;
        }
        return res;
    }
}
  • 时间复杂度: O ( ( r i g h t − l e f t ) ∗ log ⁡ log ⁡ r i g h t ) O((right-left)*\log \log right) O((rightleft)loglogright)
  • 空间复杂度: O ( C ) O(C) O(C)

C++

【注意无符号右移】

class Solution {
public:
    int countPrimeSetBits(int left, int right) {
        bool hash[35];
        memset(hash, false, 35);
        int prime[10] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29};
        for(int p : prime)
            hash[p] = true;

        int res = 0;
        for(int i = left; i <= right; i++) {
            unsigned int x = i;
            x = (x & 0x55555555) + ((x >> 1) & 0x55555555);
            x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
            x = (x & 0x0f0f0f0f) + ((x >> 4) & 0x0f0f0f0f);
            x = (x & 0x00ff00ff) + ((x >> 8) & 0x00ff00ff);
            x = (x & 0x0000ffff) + ((x >> 16) & 0x0000ffff);
            if(hash[x])
                res++;
        }
        return res;
    }
};
  • 时间复杂度: O ( ( r i g h t − l e f t ) ∗ log ⁡ log ⁡ r i g h t ) O((right-left)*\log \log right) O((rightleft)loglogright)
  • 空间复杂度: O ( C ) O(C) O(C)

思路三:库函数

枚举+挨个判断

Java

class Solution {
    private boolean isPrime(int x) {
        if(x < 2)
            return false;
        for(int i = 2; i * i <= x; ++i)
            if(x % i == 0)
                return false;
        return true;
    }
    public int countPrimeSetBits(int left, int right) {
        int res = 0;
        for(int x = left; x <= right; ++x)
            if(isPrime(Integer.bitCount(x)))
                res++;
        return res;
    }
}
  • 时间复杂度: O ( ( r i g h t − l e f t ) ∗ log ⁡ r i g h t ) O((right-left)* \sqrt{ \log right}) O((rightleft)logright ),判断是否为质数的复杂度为 O ( x ) O(\sqrt{x}) O(x )
  • 空间复杂度: O ( C ) O(C) O(C)

C++

库函数builtin_popcount用于统计二进制数中 1 1 1的个数。

class Solution {
    bool isPrime(int x) {
        if(x < 2)
            return false;
        for(int i = 2; i * i <= x; ++i)
            if(x % i == 0)
                return false;
        return true;
    }
public:
    int countPrimeSetBits(int left, int right) {
        int res = 0;
        for(int x = left; x <= right; ++x)
            if(isPrime(__builtin_popcount(x)))
                res++;
        return res;
    }
};
  • 时间复杂度: O ( ( r i g h t − l e f t ) ∗ log ⁡ r i g h t ) O((right-left)*\sqrt{ \log right}) O((rightleft)logright )
  • 空间复杂度: O ( C ) O(C) O(C)

思路四:常数复杂度判断质数

由数据范围可以判定二进制中 1 1 1的个数不会超过 19 19 19 2 , 3 , 5 , 7 , 11 , 13 , 17 , 19 2, 3, 5, 7, 11, 13, 17, 19 2,3,5,7,11,13,17,19
可以用一个二进制数来表示,质数位为 1 1 1,也就是把前面的哈希表写成一个数,即 m a s k = 10100010100010101100 2 = 665772 mask={10100010100010101100}_2=665772 mask=101000101000101011002=665772
那么只要把目标二进制数中 1 1 1的个数与之按位与,结果不为 0 0 0则为质数。

Java

class Solution {
    public int countPrimeSetBits(int left, int right) {
        int res = 0;
        for(int x = left; x <= right; ++x)
            if(((1 << Integer.bitCount(x)) & 665772) != 0)
                res++;
        return res;
    }
}
  • 时间复杂度: O ( ( r i g h t − l e f t ) ) O((right-left)) O((rightleft))
  • 空间复杂度: O ( C ) O(C) O(C)

C++

class Solution {
public:
    int countPrimeSetBits(int left, int right) {
        int res = 0;
        for(int x = left; x <= right; ++x)
            if(((1 << __builtin_popcount(x)) & 665772) != 0)
                res++;
        return res;
    }
};
  • 时间复杂度: O ( ( r i g h t − l e f t ) ) O((right-left)) O((rightleft))
  • 空间复杂度: O ( C ) O(C) O(C)

总结

今天的题做得很顺利,想到了昨天那个lowbit也是很快乐。
分治的方法本来打算直接背算了,后来推了几遍发现了思路。
后两种是看到官方用了个库函数还挺有意思。
一二侧重于优化计算 1 1 1的数量,三四侧重于优化质数的判断,其实是可以结合起来用的。
那么、虽然没有假期、但是……春天快乐!


欢迎指正与讨论!
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值