A clear explanation of finding the bitwise AND of every number in an inclusive range using the common binary prefix.
Problem Restatement
We are given two integers left and right.
They define an inclusive range:
[left, left + 1, left + 2, ..., right]We need to return the bitwise AND of every number in that range.
The official statement says: given two integers left and right representing the range [left, right], return the bitwise AND of all numbers in this range, inclusive. The constraints are 0 <= left <= right <= 2^31 - 1.
Input and Output
| Item | Meaning |
|---|---|
| Input | Two integers left and right |
| Output | One integer: the AND of all numbers from left to right |
| Range | Inclusive |
| Constraint | 0 <= left <= right <= 2^31 - 1 |
Example function shape:
def rangeBitwiseAnd(left: int, right: int) -> int:
...Examples
Example 1:
left = 5
right = 7The numbers are:
5 = 101
6 = 110
7 = 111Now compute:
101
& 110
& 111
= 100So the answer is:
4Example 2:
left = 0
right = 0There is only one number in the range, so the answer is:
0Example 3:
left = 1
right = 2147483647This range crosses many powers of two and contains enough variation that every bit position becomes 0 after AND.
So the answer is:
0First Thought: Brute Force
The direct solution is to AND every number in the range.
class Solution:
def rangeBitwiseAnd(self, left: int, right: int) -> int:
ans = left
for x in range(left + 1, right + 1):
ans &= x
return ansThis is easy to understand.
For example:
left = 5
right = 7We compute:
ans = 5
ans = ans & 6
ans = ans & 7The final answer is 4.
Problem With Brute Force
The range can be very large.
For example:
left = 1
right = 2147483647A loop over every number would require more than two billion iterations.
So we need to use the structure of binary numbers instead of visiting every number.
Key Insight
Bitwise AND keeps a bit as 1 only when every number in the range has that bit set to 1.
If a bit changes from 0 to 1 or from 1 to 0 somewhere inside the range, then that bit becomes 0 in the final answer.
So the final answer is the common binary prefix of left and right.
For example:
left = 5 = 101
right = 7 = 111The common prefix is:
1__The changing lower bits become 0.
So the answer is:
100 = 4Another example:
left = 26 = 11010
right = 30 = 11110The common prefix is:
11___The lower changing bits become 0.
So the answer is:
11000 = 24Algorithm
We can find the common prefix by shifting both numbers right until they become equal.
Each right shift removes one lower bit.
We count how many bits we removed.
When left == right, the remaining value is the common prefix.
Then we shift it back left by the number of removed bits.
Steps:
- Set
shift = 0. - While
left != right:- Shift
leftright by one bit. - Shift
rightright by one bit. - Increase
shift.
- Shift
- Shift
leftback left byshift. - Return the result.
Walkthrough
Use:
left = 5
right = 7Binary:
left = 101
right = 111They are different, so shift both right.
left = 10
right = 11
shift = 1Still different. Shift again.
left = 1
right = 1
shift = 2Now they are equal.
The common prefix is:
1Shift it back left by 2:
1 << 2 = 100So the answer is:
4Correctness
The bitwise AND of a range keeps only the bit positions that are 1 in every number from left to right.
When left and right differ at some binary position, the range between them must vary across lower positions. Those lower positions cannot remain fixed as 1 for every number in the interval. Therefore, every bit after the first differing position becomes 0 in the final AND.
The only bits that can survive are the shared leading bits of left and right.
The algorithm repeatedly shifts both numbers right. This removes lower bits until only the shared leading prefix remains. When the two shifted values become equal, that value is exactly the common prefix.
The algorithm then shifts the prefix back to its original position. The removed lower bits are filled with zeros, which matches the behavior of AND over the full range.
Therefore, the algorithm returns the correct bitwise AND of all numbers in [left, right].
Complexity
| Metric | Value | Why |
|---|---|---|
| Time | O(log right) | Each loop removes one binary bit |
| Space | O(1) | Only a few integer variables are used |
Since right <= 2^31 - 1, the loop runs at most 31 times.
Implementation
class Solution:
def rangeBitwiseAnd(self, left: int, right: int) -> int:
shift = 0
while left != right:
left >>= 1
right >>= 1
shift += 1
return left << shiftCode Explanation
We start with:
shift = 0This counts how many lower bits we remove.
The loop continues while the two numbers have different remaining prefixes:
while left != right:Inside the loop, we remove one low bit from both numbers:
left >>= 1
right >>= 1Then we record that one more bit was removed:
shift += 1When the loop ends, left and right are equal. That value is the common prefix.
Finally:
return left << shiftThis restores the prefix to its original bit position and fills the removed lower bits with zeros.
Alternative Implementation
There is another common solution using this identity:
right &= right - 1This removes the lowest set bit of right.
We keep doing this while right > left.
class Solution:
def rangeBitwiseAnd(self, left: int, right: int) -> int:
while left < right:
right &= right - 1
return rightThis works because every removed bit is in a lower changing region that cannot survive the range AND.
The shifting version is often easier to explain. The right &= right - 1 version is shorter.
Testing
def run_tests():
s = Solution()
assert s.rangeBitwiseAnd(5, 7) == 4
assert s.rangeBitwiseAnd(0, 0) == 0
assert s.rangeBitwiseAnd(1, 2147483647) == 0
assert s.rangeBitwiseAnd(8, 8) == 8
assert s.rangeBitwiseAnd(26, 30) == 24
assert s.rangeBitwiseAnd(10, 12) == 8
print("all tests passed")
run_tests()| Test | Why |
|---|---|
(5, 7) | Standard example with changing low bits |
(0, 0) | Single zero |
(1, 2147483647) | Very large range |
(8, 8) | Single number range |
(26, 30) | Preserves a longer common prefix |
(10, 12) | Checks a small nontrivial range |