Number of Bit Changes to Make Two Integers Equal
EasyPrompt
You are given two positive integers n and k.
You can choose any bit in the binary representation of n that is equal to 1 and change it to 0.
Return the number of changes needed to make n equal to k. If it is impossible, return -1.
Example 1:
Input: n = 13, k = 4
Output: 2
Explanation:
Initially, the binary representations of n and k are n = (1101)2 and k = (0100)2.
We can change the first and fourth bits of n. The resulting integer is n = (0100)2 = k.
Example 2:
Input: n = 21, k = 21
Output: 0
Explanation:
n and k are already equal, so no changes are needed.
Example 3:
Input: n = 14, k = 13
Output: -1
Explanation:
It is not possible to make n equal to k.
Constraints:
1 <= n, k <= 106
Approaches
3 approaches with complexity analysis and trade-offs.
This approach involves converting both integers n and k into their binary string representations. After ensuring both strings have the same length by padding the shorter one with leading zeros, we can iterate through them character by character to check for the conditions of the problem.
Algorithm
- Convert
nandkto binary strings,nStrandkStr. - Determine the maximum length between
nStrandkStr. - Pad the shorter string with leading zeros to match the maximum length.
- Initialize a
changescounter to 0. - Iterate through the strings from
i = 0tolength - 1. - At each position
i, comparenStr.charAt(i)andkStr.charAt(i). - If
nStr.charAt(i) == '0'andkStr.charAt(i) == '1', return -1. - If
nStr.charAt(i) == '1'andkStr.charAt(i) == '0', incrementchanges. - After the loop, return
changes.
Walkthrough
The core idea is to work with the numbers as text.
- First, we obtain the binary strings for
nandkusingInteger.toBinaryString(). - The binary strings might have different lengths. To compare them bit by bit, we need to align them. We find the maximum length and pad the shorter string with leading '0's until it matches the maximum length.
- We then iterate through the aligned strings from left to right (most significant to least significant bit).
- For each position
i, we compare the charactersn_str.charAt(i)andk_str.charAt(i).- If
nhas a '0' wherekhas a '1', the transformation is impossible, as we cannot change a '0' to a '1'. We return -1 immediately. - If
nhas a '1' wherekhas a '0', this is a required change. We increment a counter. - If the bits are the same, no action is needed.
- If
- If the loop completes without finding an impossible case, the final value of the counter is our answer.
class Solution { public int minChanges(int n, int k) { String nStr = Integer.toBinaryString(n); String kStr = Integer.toBinaryString(k); int nLen = nStr.length(); int kLen = kStr.length(); int maxLen = Math.max(nLen, kLen); // Pad with leading zeros to make lengths equal while (nStr.length() < maxLen) { nStr = "0" + nStr; } while (kStr.length() < maxLen) { kStr = "0" + kStr; } int changes = 0; for (int i = 0; i < maxLen; i++) { char nBit = nStr.charAt(i); char kBit = kStr.charAt(i); if (nBit == '0' && kBit == '1') { return -1; // Impossible to change 0 to 1 } if (nBit == '1' && kBit == '0') { changes++; // Required change from 1 to 0 } } return changes; }}Complexity
Time
O(log(max(n, k))). The length of the binary representation of a number `x` is proportional to `log(x)`. String operations like conversion and padding take time proportional to this length.
Space
O(log(max(n, k))) to store the binary string representations of `n` and `k`.
Trade-offs
Pros
Conceptually straightforward, especially for those more comfortable with string manipulation than bitwise operations.
Cons
Inefficient due to the overhead of string creation, concatenation (for padding), and character-by-character access.
The implementation is more verbose compared to bitwise solutions.
This approach avoids the overhead of string conversion by working directly with the integer representations. We can inspect the bits of n and k one by one, from the least significant bit upwards, using bitwise operators.
Algorithm
- Initialize a
changescounter to 0. - Start a loop that continues as long as
n > 0ork > 0. - Inside the loop, get the least significant bit of
n(n_bit = n & 1) andk(k_bit = k & 1). - If
n_bitis 0 andk_bitis 1, return -1. - If
n_bitis 1 andk_bitis 0, incrementchanges. - Right-shift both
nandkby one bit (n >>= 1,k >>= 1) to process the next pair of bits. - After the loop finishes, return
changes.
Walkthrough
Instead of converting numbers to strings, we can use bitwise operations to achieve the same goal more efficiently. The logic remains the same: compare each corresponding bit of n and k.
We can use a loop that continues as long as either n or k is not zero. In each iteration, we examine the last bit (least significant bit) of both numbers.
- The last bit of a number
xcan be obtained using the bitwise AND operation:x & 1. - We compare
n & 1andk & 1.- If
n's last bit is 0 ((n & 1) == 0) andk's last bit is 1 ((k & 1) == 1), the transformation is impossible. We return -1. - If
n's last bit is 1 andk's last bit is 0, we must perform a change. We increment ourchangescounter.
- If
- After checking the last bits, we discard them by right-shifting both
nandkby one position (n >>= 1,k >>= 1). This makes the next bits the new last bits for the next iteration. - The loop terminates when both
nandkbecome 0, meaning we have processed all their bits.
class Solution { public int minChanges(int n, int k) { int changes = 0; // Loop until all bits of both n and k have been processed while (n > 0 || k > 0) { int nBit = n & 1; // Get the last bit of n int kBit = k & 1; // Get the last bit of k if (nBit == 0 && kBit == 1) { return -1; // Impossible case: cannot change 0 to 1 } if (nBit == 1 && kBit == 0) { changes++; // Required change: 1 to 0 } // Move to the next bit n >>= 1; k >>= 1; } return changes; }}Complexity
Time
O(log(max(n, k))) or O(B) where B is the number of bits in the integer type (e.g., 32). The loop runs once for each bit position until both numbers are zero.
Space
O(1). We only use a few integer variables to store the count and intermediate bit values, regardless of the input size.
Trade-offs
Pros
Much more efficient than the string-based approach as it avoids allocations and string operations.
Uses constant extra space.
Cons
Can be slightly less intuitive than string manipulation for those unfamiliar with bitwise operators.
This is the most efficient approach, leveraging the properties of bitwise operations to solve the problem in a very concise and fast manner. It separates the problem into two parts: checking for impossibility and counting the changes, both of which can be done with single bitwise expressions.
Algorithm
- Check if
kis a "bitwise subset" ofnby evaluating the condition(n & k) == k. - If the condition is false, it's impossible to form
kfromn. Return -1. - If the condition is true, the transformation is possible. The number of required changes (flipping a
1innto a0) is the number of set bits innminus the number of set bits ink. - Return the result of
Integer.bitCount(n) - Integer.bitCount(k).
Walkthrough
This solution is based on two key bitwise insights.
1. Impossibility Check:
The transformation from n to k is only possible if for every bit that is set to 1 in k, the corresponding bit is also 1 in n. We cannot create a 1 bit where n has a 0. This property is perfectly captured by the bitwise AND operation. If (n & k) == k, it means that all set bits in k are also set in n. If (n & k) != k, it implies there is at least one bit position where k has a 1 and n has a 0, making the transformation impossible.
2. Counting Changes:
If the transformation is possible, we need to count the number of bits that must be flipped from 1 to 0. These are the bits that are 1 in n but 0 in k. Since we've already established that all of k's 1s are also 1s in n, the number of bits to change is simply the total number of 1s in n minus the number of 1s in k. The 1s in k are the ones we don't change. The number of set bits is also known as the population count (popcount). Most languages provide a built-in function for this, like Integer.bitCount() in Java.
The algorithm is therefore extremely simple:
- Check if
(n & k) != k. If true, return -1. - Otherwise, return
Integer.bitCount(n) - Integer.bitCount(k).
class Solution { public int minChanges(int n, int k) { // Check for the impossible case: k has a '1' where n has a '0'. // If (n & k) is not equal to k, it means there's a bit set in k // that is not set in n. if ((n & k) != k) { return -1; } // If possible, the number of changes is the number of bits that are '1' in n // but '0' in k. This is equivalent to the difference in the total number // of set bits in n and k. return Integer.bitCount(n) - Integer.bitCount(k); }}Complexity
Time
O(1). The bitwise AND, comparison, and `Integer.bitCount` operations are typically single, highly optimized machine instructions.
Space
O(1). No extra space proportional to the input size is used.
Trade-offs
Pros
Extremely efficient in both time and space.
Very concise and elegant code.
Leverages hardware-optimized instructions for bit counting on many platforms.
Cons
Requires a good understanding of bitwise logic, which might make it less immediately obvious than other methods.
Solutions
Solution
class Solution {public int minChanges(int n, int k) { return (n & k) != k ? -1 : Integer.bitCount(n ^ k); }}class Solution {public: int minChanges(int n, int k) { return (n & k) != k ? -1 : __builtin_popcount(n ^ k); }};class Solution: def minChanges(self, n: int, k: int) -> int: return - \ 1 if n & k != k else (n ^ k). bit_count()Video walkthrough
Newsletter
One sharp idea, every week
System design and interview prep — short enough to finish.
No spam. Unsubscribe anytime.
Practice
Same difficulty — related problems to reinforce the pattern.