LeetCode 算法题【简单】338. 比特位计数
语雀原文链接
1、LeetCode 链接
- 官方链接:https://leetcode.cn/problems/counting-bits/description/
- 标签:位运算
给你一个整数 n ,对于 0 <= i <= n 中的每个 i ,计算其二进制表示中 1 的个数 ,返回一个长度为 n + 1 的数组 ans 作为答案。示例 1:输入:n = 2
输出:[0,1,1]
解释:
0 --> 0
1 --> 1
2 --> 10
示例 2:输入:n = 5
输出:[0,1,1,2,1,2]
解释:
0 --> 0
1 --> 1
2 --> 10
3 --> 11
4 --> 100
5 --> 101提示:0 <= n <= 105进阶:很容易就能实现时间复杂度为 O(n log n) 的解决方案,你可以在线性时间复杂度 O(n) 内用一趟扫描解决此问题吗?
你能不使用任何内置函数解决此问题吗?(如,C++ 中的 __builtin_popcount )
2、个人写法
- 思路:除以2取余数
- 时间复杂度:O(NLogN)
- 空间复杂度:O(N)
class Solution {public int[] countBits(int n) {int[] ans = new int[n+1];for(int i = 0; i <= n; i++){int count = 0;int temp = i;while(temp > 0){if(temp % 2 == 1){count++;}temp = temp / 2;}ans[i] = count;}return ans;}
}
3、更优写法
优化写法1:x=x & (x−1)
- 思路:Brian Kernighan 算法:利用Brian Kernighan 算法,可以在一定程度上进一步提升计算速度。Brian Kernighan 算法的原理是:对于任意整数 x,令 x=x & (x−1),该运算将 x 的二进制表示的最后一个 1 变成 0。因此,对 x 重复该操作,直到 x 变成 0,则操作次数即为 x 的「一比特数」。
- 举例
数字3的二进制 0011
第一次 3 & 2 = 0011 & 0010 = 0010 = 2 去除了最后一个1
第二次 2 & 1 = 0010 & 0001 = 0000 = 0 又去除了最后一个1
重复了两次,所以3的二进制总共有两个1
- 时间复杂度:O(NLogN)
- 空间复杂度:O(N)
class Solution {public int[] countBits(int n) {int[] ans = new int[n+1];for(int i = 0; i <= n; i++){ans[i] = countOnes(i);}return ans;}public int countOnes(int num){int count = 0;while(num > 0){num &= (num -1);count++;}return count;}
}
优化写法2:y=x & (x−1) x 比 y多一个1
- 思路:令 y=x & (x−1),则 y 为将 x 的最低设置位从 1 变成 0 之后的数,显然 0≤y<x,bits[x]=bits[y]+1。因此对任意正整数 x,都有 bits[x]=bits[x & (x−1)]+1。
- 时间复杂度:O(n)。对于每个整数,只需要 O(1) 的时间计算「一比特数」。
- 空间复杂度:O(1)。除了返回的数组以外,空间复杂度为常数。
class Solution {public int[] countBits(int n) {int[] bits = new int[n + 1];for (int i = 1; i <= n; i++) {bits[i] = bits[i & (i - 1)] + 1;}return bits;}
}