|
1 | 1 | package com.thealgorithms.bitmanipulation; |
2 | 2 |
|
3 | | -public class CountSetBits { |
| 3 | +/** |
| 4 | + * Utility class to count total set bits from 1 to N |
| 5 | + * A set bit is a bit in binary representation that is 1 |
| 6 | + * |
| 7 | + * @author navadeep |
| 8 | + */ |
| 9 | +public final class CountSetBits { |
| 10 | + |
| 11 | + private CountSetBits() { |
| 12 | + // Utility class, prevent instantiation |
| 13 | + } |
4 | 14 |
|
5 | 15 | /** |
6 | | - * The below algorithm is called as Brian Kernighan's algorithm |
7 | | - * We can use Brian Kernighan’s algorithm to improve the above naive algorithm’s performance. |
8 | | - The idea is to only consider the set bits of an integer by turning off its rightmost set bit |
9 | | - (after counting it), so the next iteration of the loop considers the next rightmost bit. |
10 | | -
|
11 | | - The expression n & (n-1) can be used to turn off the rightmost set bit of a number n. This |
12 | | - works as the expression n-1 flips all the bits after the rightmost set bit of n, including the |
13 | | - rightmost set bit itself. Therefore, n & (n-1) results in the last bit flipped of n. |
14 | | -
|
15 | | - For example, consider number 52, which is 00110100 in binary, and has a total 3 bits set. |
16 | | -
|
17 | | - 1st iteration of the loop: n = 52 |
18 | | -
|
19 | | - 00110100 & (n) |
20 | | - 00110011 (n-1) |
21 | | - ~~~~~~~~ |
22 | | - 00110000 |
| 16 | + * Counts total number of set bits in all numbers from 1 to n |
| 17 | + * Time Complexity: O(log n) |
| 18 | + * |
| 19 | + * @param n the upper limit (inclusive) |
| 20 | + * @return total count of set bits from 1 to n |
| 21 | + * @throws IllegalArgumentException if n is negative |
| 22 | + */ |
| 23 | + public static int countSetBits(int n) { |
| 24 | + if (n < 0) { |
| 25 | + throw new IllegalArgumentException("Input must be non-negative"); |
| 26 | + } |
23 | 27 |
|
| 28 | + if (n == 0) { |
| 29 | + return 0; |
| 30 | + } |
24 | 31 |
|
25 | | - 2nd iteration of the loop: n = 48 |
| 32 | + // Find the largest power of 2 <= n |
| 33 | + int x = largestPowerOf2InNumber(n); |
26 | 34 |
|
27 | | - 00110000 & (n) |
28 | | - 00101111 (n-1) |
29 | | - ~~~~~~~~ |
30 | | - 00100000 |
| 35 | + // Total bits at position x: x * 2^(x-1) |
| 36 | + int bitsAtPositionX = x * (1 << (x - 1)); |
31 | 37 |
|
| 38 | + // Remaining numbers after 2^x |
| 39 | + int remainingNumbers = n - (1 << x) + 1; |
32 | 40 |
|
33 | | - 3rd iteration of the loop: n = 32 |
| 41 | + // Recursively count for the rest |
| 42 | + int rest = countSetBits(n - (1 << x)); |
34 | 43 |
|
35 | | - 00100000 & (n) |
36 | | - 00011111 (n-1) |
37 | | - ~~~~~~~~ |
38 | | - 00000000 (n = 0) |
| 44 | + return bitsAtPositionX + remainingNumbers + rest; |
| 45 | + } |
39 | 46 |
|
40 | | - * @param num takes Long number whose number of set bit is to be found |
41 | | - * @return the count of set bits in the binary equivalent |
42 | | - */ |
43 | | - public long countSetBits(long num) { |
44 | | - long cnt = 0; |
45 | | - while (num > 0) { |
46 | | - cnt++; |
47 | | - num &= (num - 1); |
| 47 | + /** |
| 48 | + * Finds the position of the most significant bit in n |
| 49 | + * |
| 50 | + * @param n the number |
| 51 | + * @return position of MSB (0-indexed from right) |
| 52 | + */ |
| 53 | + private static int largestPowerOf2InNumber(int n) { |
| 54 | + int position = 0; |
| 55 | + while ((1 << position) <= n) { |
| 56 | + position++; |
48 | 57 | } |
49 | | - return cnt; |
| 58 | + return position - 1; |
50 | 59 | } |
51 | 60 |
|
52 | 61 | /** |
53 | | - * This approach takes O(1) running time to count the set bits, but requires a pre-processing. |
| 62 | + * Alternative naive approach - counts set bits by iterating through all numbers |
| 63 | + * Time Complexity: O(n log n) |
54 | 64 | * |
55 | | - * So, we divide our 32-bit input into 8-bit chunks, with four chunks. We have 8 bits in each chunk. |
56 | | - * |
57 | | - * Then the range is from 0-255 (0 to 2^7). |
58 | | - * So, we may need to count set bits from 0 to 255 in individual chunks. |
59 | | - * |
60 | | - * @param num takes a long number |
61 | | - * @return the count of set bits in the binary equivalent |
| 65 | + * @param n the upper limit (inclusive) |
| 66 | + * @return total count of set bits from 1 to n |
62 | 67 | */ |
63 | | - public int lookupApproach(int num) { |
64 | | - int[] table = new int[256]; |
65 | | - table[0] = 0; |
66 | | - |
67 | | - for (int i = 1; i < 256; i++) { |
68 | | - table[i] = (i & 1) + table[i >> 1]; // i >> 1 equals to i/2 |
| 68 | + public static int countSetBitsNaive(int n) { |
| 69 | + if (n < 0) { |
| 70 | + throw new IllegalArgumentException("Input must be non-negative"); |
69 | 71 | } |
70 | 72 |
|
71 | | - int res = 0; |
72 | | - for (int i = 0; i < 4; i++) { |
73 | | - res += table[num & 0xff]; |
74 | | - num >>= 8; |
| 73 | + int count = 0; |
| 74 | + for (int i = 1; i <= n; i++) { |
| 75 | + count += Integer.bitCount(i); |
75 | 76 | } |
76 | | - |
77 | | - return res; |
| 77 | + return count; |
78 | 78 | } |
79 | 79 | } |
0 commit comments