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

202 阅读2分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

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

题目要求

在这里插入图片描述

思路一:模拟

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

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((rightleft)logright)O((right-left)*\log right)lowbit的复杂度为O(logright)O(\log right)
  • 空间复杂度: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((rightleft)logright)O((right-left)*\log right)
  • 空间复杂度:O(C)O(C)

思路二:分治

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

分治统计11的个数

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

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((rightleft)loglogright)O((right-left)*\log \log right)
  • 空间复杂度: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((rightleft)loglogright)O((right-left)*\log \log right)
  • 空间复杂度: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((rightleft)logright)O((right-left)* \sqrt{ \log right}),判断是否为质数的复杂度为O(x)O(\sqrt{x})
  • 空间复杂度:O(C)O(C)

C++

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

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((rightleft)logright)O((right-left)*\sqrt{ \log right})
  • 空间复杂度:O(C)O(C)

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

由数据范围可以判定二进制中11的个数不会超过1919

2,3,5,7,11,13,17,192, 3, 5, 7, 11, 13, 17, 19

可以用一个二进制数来表示,质数位为11,也就是把前面的哈希表写成一个数,即

mask=101000101000101011002=665772mask={10100010100010101100}_2=665772

那么只要把目标二进制数中11的个数与之按位与,结果不为00则为质数。

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((rightleft))O((right-left))
  • 空间复杂度: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((rightleft))O((right-left))
  • 空间复杂度:O(C)O(C)

总结

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

那么、虽然没有假期、但是……春天快乐!


欢迎指正与讨论!