java 位运算 + leetcode 22.8.3 前n个数字二进制中1的个数

java 位运算

java位运算符是对操作数的二进制位进行运算,操作数和计算结果都是整型,位运算符如下:

运算符含义实例结果
<<左移4<<216
>>右移4>>12
>>>无符号右移4>>>12
&与运算4&20
|或运算4|26
^异或运算4^26
~取反~4-5
&:

两个二进制位中只要有一个为0那么结果就为0,否则为1。

|:

两个二进制位中只要有一个为1那么结果就为1,否则为0。

^:

任何两个相同的二进制位进行异或运算,结果都为0;不同的两个进行运算,结果为1。

~:

0 --> 1,1 --> 0

<<:

将符号左边的操作数左移指定的位数。

首先将左边的操作数转为二进制。

然后按照要求左移指定位数,左边最高位丢弃,右边补齐0。

3<<2
3的二进制:
00000000 00000000 00000000 00000011
左移2位,左边最高位丢弃,右边补齐000000000 00000000 00000000 00001100
>>:

将符号左边的操作数右移指定的位数。

首先将左边的操作数转为二进制。

然后按照要求右移指定位数,最高位是0,左边补齐0;最高为是1,左边补齐1。

24>>2
24的二进制:
00000000 00000000 00000000 00011000
右移2位,最高位是0,左边补齐0;最高为是1,左边补齐100000000 00000000 00000000 00000110
>>>:

将符号左边的操作数右移指定的位数。

首先将左边的操作数转为二进制。

然后按照要求右移指定位数,无论最高位是0还是1左边补齐0。

24>>>2
24的二进制:
00000000 00000000 00000000 00011000
右移2位,最高位无论是什么都补000000000 00000000 00000000 00000110

代码分析

下面这段算法可以得出一个整型数字二进制形式中含有多少1。

这里用到了variable-precision SWAR 算法,应该是处理这个问题效率最高的通用算法了。

通过分析这个函数,我们来深入理解java的二进制位运算。

public static int countBits(int i) {
        i = (i&0x55555555)+((i>>>1)&0x55555555);
        i = (i&0x33333333)+((i>>>2)&0x33333333);
        i = (i&0x0f0f0f0f)+((i>>>4)&0x0f0f0f0f);
        i = (i&0x00ff00ff)+((i>>>8)&0x00ff00ff);
        i = (i&0x0000ffff)+((i>>>16)&0x0000ffff);
        return i;
    }

0x55555555,0x33333333,0x0f0f0f0f到底有啥用呢,其实在这里就是用来做掩码,来取奇偶位上的数,然后移位后进行二进制加法就可以记录更大范围内1的数量

16进制二进制描述
0xaaaaaaaa10101010 10101010 10101010 10101010偶数位为1,奇数位为0
0x5555555501010101 01010101 01010101 01010101偶数位为0,奇数位为1
0x3333333300110011 00110011 00110011 001100111和0每隔两位交替出现
0xcccccccc11001100 11001100 11001100 110011000和1每隔两位交替出现
0x0f0f0f0f00001111 00001111 00001111 000011111和0每隔四位交替出现
0xf0f0f0f011110000 11110000 11110000 111100000和1每隔四位交替出现

我们可以把i的二进制位理解成:长度为32的数组,每个元素取值区间[0,1],每个元素正好能代表这个位是不是1.

所以,问题就可以转化为,求这个数组的和。

根据分治法的思想,我们可以把相邻的两个数字相加,得到长度为16的数组,每个元素取值区间[0,2]。

并以此类推,最终求出总和。

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

剑指 Offer II 003. 前 n 个数字二进制中 1 的个数 - 力扣(LeetCode)
在这里插入图片描述

解题在这里插入图片描述
public static int[] countBits(int n) {
    int[] arr = new int[n+1];
    for (int i = 0; i <= n; i++) {
        int b = i;
        b = (b&0x55555555)+((b>>>1)&0x55555555);
        b = (b&0x33333333)+((b>>>2)&0x33333333);
        b = (b&0x0f0f0f0f)+((b>>>4)&0x0f0f0f0f);
        b = (b&0x00ff00ff)+((b>>>8)&0x00ff00ff);
        b = (b&0x0000ffff)+((b>>>16)&0x0000ffff);
        arr[i]=b;
    }
   return arr;
}

这里就用到了上面的位运算,该解法时间复杂度为O(N),空间复杂度为O(1);

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值