前n个数字二进制形式中1的个数

题目:输入一个非负数n,请计算0到n之间每个数字的二进制形式中1的个数,并输出一个数组、例如,输入的n位4,由于0,1,2,3,4的二进制形式中1的个数分别为0,1,1,2,1,因此输出数组[0,1,1,2,1]。

方法一:简单计算每个整数的二进制形式中1的个数

该问题关键所在其实是求解一个整数i的二进制形式中1的个数。对于求解该问题,我们需要知道一个知识点,i&(i-1)会将i最右边的1变为0(其中i和i-1均为整数二进制)。因为整数i减去1后,其最右边的1会变成0,若该“1”右边有0,则全部变成1,而该“1”左边保持不变。例如:

1   0001
2   0010
3   0011
4   0100
由此,我们发现一个整数二进制i能做几次这样的&运算,那么它就有几个1。这就是方法一的核心。

//j与j-1的与运算会消除最右边的1
    public static int[] countBits1(int num) {
        //result存放结果并充当计数的作用
        int[] result = new int[num + 1];
        for (int i = 0; i <= num; i++) {
            int j = i;
            while (j != 0) {
                result[i]++;
                j = j & (j - 1);
            }

        }
        return result;
    }

如果一个整数有k位,那么可能有k个1,while循环将执行O(k)次,时间复杂度为O(nk)。

方法二:根据“i&(i-1)”计算i的二进制形式中1的个数

由“i&(i-1)将i的二进制形式中最右边的1变成0”这句话进一步思考,整数i的二进制形式中1的个数比“i&(i-1)"的二进制形式中1的个数多1。

 //整数i的二进制中1的个数比i&(i-1)的二进制形式中的1的个数多1
    public static int[] countBits2(int num) {
        int[] result = new int[num + 1];
        for (int i = 1; i <= num; i++) {
            result[i] = result[i & (i - 1)] + 1;
        }
        return result;
    }

不管i有几位,该法只需O(1)的时间就可计算出i的二进制中1的个数,因此时间复杂度是O(n)。

方法三:根据”i/2“计算i的二进制形式中1的个数

如果i是偶数,那么i相当与”i/2“左移一位的结果,因此,二者二进制形式中1的个数相同。如果i是奇数,那么i相当与”i/2“左移一位之后再将最右边一位设为1的结果,因此,奇数i的二进制中1的个数比i/2的多1。

    //i为偶数,i相当于i/2左移一位的结果
    //i为奇数,i相当于i/2左移一位后再将最右边一位设为1
    //i & 1用来计算 i % 2
    public static int[] countBits3(int num){
        int[] result = new int[num + 1];
        for (int i = 1; i <= num; i++) {
            result[i] = result[i >> 1] + (i & 1);
        }
        return result;
    }

位运算比除法运算和求余运算更高效,所以用i>>1计算i/2,i&1计算i%2。该方法时间复杂度也为O(n)。

完整代码

import java.util.Scanner;

public class CountBits {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        String s = sc.nextLine();
        Integer i = Integer.valueOf(s);
        int[] arr1 = countBits1(i);
        print(arr1);
        int[] arr2 = countBits2(i);
        print(arr2);
        int[] arr3 = countBits3(i);
        print(arr3);
    }

    private static void print(int[] arr) {
        System.out.print("[");
        for (int i = 0; i < arr.length; i++) {
            if (i == arr.length - 1) {
                System.out.print(arr[i]);
            } else {
                System.out.print(arr[i] + " ");
            }
        }
        System.out.println("]");
    }
    //n与n-1的与运算会消除最右边的1
    public static int[] countBits1(int num) {
        int[] result = new int[num + 1];
        for (int i = 0; i <= num; i++) {
            int j = i;
            while (j != 0) {
                result[i]++;
                j = j & (j - 1);
            }

        }
        return result;
    }
    //整数i的二进制中1的个数比i&(i-1)的二进制形式中的1的个数多1
    public static int[] countBits2(int num) {
        int[] result = new int[num + 1];
        for (int i = 1; i <= num; i++) {
            result[i] = result[i & (i - 1)] + 1;
        }
        return result;
    }
    //i为偶数,i相当于i/2左移一位的结果
    //i为奇数,i相当于i/2左移一位后再将最右边一位设为1
    //i & 1用来计算 i % 2
    public static int[] countBits3(int num){
        int[] result = new int[num + 1];
        for (int i = 1; i <= num; i++) {
            result[i] = result[i >> 1] + (i & 1);
        }
        return result;
    }
}

  • 15
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不过是条河鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值