Topics › Bit Manipulation › Learn Bit Manipulation
Set/Unset the rightmost unset bit
Java source ↗ Edit this article ↗
Jump to code:
Problem
You are given a non-negative integer n. Two operations are asked for:
- Set the rightmost unset bit. Find the lowest
0in the binary representation ofnand turn it into a1. If the representation has no0at all (nis1, 3, 7, 15, ..., that is2^k - 1), returnnunchanged. Leading zeros do not count as part of the representation, so7 = 111has no unset bit even though theinthas 29 zeros above it. - Unset the rightmost set bit. Find the lowest
1and turn it into a0. Ifn = 0there is nothing to clear; return0.
Constraints: 0 ≤ n ≤ 2^31 - 1.
Example 1 (set)
n = 6
answer = 7 // 110 -> 111
Example 2 (set)
n = 11
answer = 15 // 1011 -> 1111, the lowest 0 sits above two trailing 1s
Example 3 (set, nothing to do)
n = 15
answer = 15 // 1111 has no unset bit
Example 4 (unset)
n = 12
answer = 8 // 1100 -> 1000
Approach 1: Brute force, scan from bit 0 upward
Intuition. Walk the positions 0, 1, 2, ... and test each bit with (n >> i) & 1. For the “set” operation stop at the first 0 and OR in 1 << i; for “unset” stop at the first 1 and AND with ~(1 << i). The only care needed is the stopping rule for “set”: we must not run past the highest set bit, or we would set a leading zero and turn 7 into 15. So compute the number of binary digits first and scan only inside it.
Algorithm (set).
- Compute
len, the number of binary digits ofn(1 forn = 0). - For
ifrom0tolen - 1: if bitiis0, returnn | (1 << i). - If the loop finishes every digit was
1; returnn.
static int bitLength(int n) {
int len = 0;
do {
len++;
n >>>= 1;
} while (n != 0);
return len;
}
static int setRightmostUnsetBruteForce(int n) {
int len = bitLength(n);
for (int i = 0; i < len; i++) {
if (((n >> i) & 1) == 0) return n | (1 << i);
}
return n; // every digit is already 1
}
Algorithm (unset).
- For
ifrom0to30: if bitiis1, returnn & ~(1 << i). - If the loop finishes,
nwas0; returnn.
static int unsetRightmostSetBruteForce(int n) {
for (int i = 0; i < 31; i++) {
if (((n >> i) & 1) == 1) return n & ~(1 << i);
}
return n; // n == 0, nothing to clear
}
Complexity. Time O(log n) for each: at most one pass over the digits. Space O(1).
Approach 2: Optimal, let the carry find the bit
Intuition for “set”. Adding 1 to n is exactly a search for the rightmost 0: the carry turns each trailing 1 into 0, then lands on the first 0 and turns it into 1. So n + 1 has the right bit set, but the trailing ones have been wiped. OR-ing with the original n puts them back. Hence n | (n + 1) sets the rightmost unset bit in two operations. The one special case is n = 2^k - 1, all ones: there n + 1 is 2^k, a brand-new digit, and the OR would produce 2^(k+1) - 1. The problem says to leave such n alone, and the test for “all ones” is itself a bit trick: n & (n + 1) == 0, because n + 1 has no bit in common with n only when the carry ran all the way off the top. n = 0 also satisfies that test, but its single digit is a 0 that should be set, so it is excluded explicitly.
Algorithm (set).
- If
n != 0and(n & (n + 1)) == 0, returnn. - Otherwise return
n | (n + 1).
static int setRightmostUnsetOptimal(int n) {
if (n != 0 && (n & (n + 1)) == 0) return n; // n is 2^k - 1, all ones, nothing to set
return n | (n + 1);
}
Dry run on n = 11 = 1011:
| step | binary | value |
|---|---|---|
| n | 1011 | 11 |
| n + 1 | 1100 | 12 (carry cleared the two trailing 1s and set bit 2) |
| n | (n + 1) | 1111 | 15 |
And on n = 15 = 1111: n + 1 = 10000, n & (n + 1) = 0, so return 15.
Intuition for “unset”. Subtracting 1 is the mirror image: the borrow turns each trailing 0 into 1, then lands on the first 1 and turns it into 0. So n - 1 differs from n exactly in the lowest set bit and the zeros below it. AND-ing with n keeps everything above (identical in both) and clears the lowest set bit (now 0 in n - 1), and the borrowed-in ones below are killed by the zeros in n. For n = 0 the result is 0 & -1 = 0, which is the required answer with no special case.
Algorithm (unset).
- Return
n & (n - 1).
static int unsetRightmostSetOptimal(int n) {
return n & (n - 1);
}
On 12 = 1100: n - 1 = 1011, 1100 & 1011 = 1000 = 8.
Complexity. Time O(1), space O(1) for both.
A third sibling: isolate the rightmost set bit
Having removed the lowest set bit, you often want its value on its own, for instance to iterate over the bits of a mask one at a time. -n is ~n + 1: flipping all bits and adding one turns the trailing zeros back into zeros, turns the lowest set bit back into a 1, and leaves everything above inverted. So n and -n agree only at that one bit, and n & -n isolates it. Equivalently it is n - (n & (n - 1)), which the Java file checks for every n up to 5000.
static int isolateRightmostSet(int n) {
return n & -n;
}
On 12 = 1100: -12 = ...110100, 1100 & 0100 = 0100 = 4.
Edge cases and pitfalls
- All ones.
1, 3, 7, 15, ...have no unset bit inside their representation. A naiven | (n + 1)turns7into15; then & (n + 1) == 0guard is what the problem statement requires. n = 0. For “set”, the representation0has a single unset digit and the answer is1; the guard must exclude0or it would be misread as “all ones”. For “unset” the answer is0, andn & (n - 1)gives it with no special code.- Leading zeros. The brute force must stop at the number’s bit length. Scanning all 32 positions would “find” an unset bit for every number except
-1. Integer.MAX_VALUE.2^31 - 1is 31 ones. It is all-ones in the problem’s sense, so “set” returns it unchanged; “unset” returns2^31 - 2. Noten + 1overflows toInteger.MIN_VALUEhere, andMAX & MINis0, so the guard still works by luck of two’s complement.- Negative inputs. Not part of the problem. The optimal formulas still behave sensibly on the 32-bit pattern (for example
-2 = ...1110becomes-1), but the brute force’sbitLengthwould report 32 digits and the two would agree only by treating all 32 bits as the representation. - Exactly one bit changes. A useful self-check used in the tests: either the result equals
n, or its popcount differs fromn’s by exactly one.
Related problems
- Introduction to Bits and Tricks lists
n & (n - 1)andn & -namong the core identities. - Check if a Number is Power of 2 or Not is “does unsetting the rightmost set bit leave zero”.
- Count the Number of Set Bits repeats the unset operation until nothing is left.
- Check if the i-th bit is Set or Not is the per-position test the brute force relies on.