0% found this document useful (0 votes)
19 views25 pages

Bit Manipulation

Uploaded by

f20221245
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
19 views25 pages

Bit Manipulation

Uploaded by

f20221245
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 25

Bit Manipulation

Note ->

● Remember whenever you do n >> 2 , it does not automatically changes the value of n , to actually change the value
of n you need to do n = n >> 2

● Position of brackets always place brackets like → ( arr[i] & (1<<k) ) // inner and then outer
If u place like ( arr[i] ) & (1<<k) , or arr[i] & (1<<k) , this will give wrong results

● 2^n in binary can be written as 1 << n

● For arrays we do indexing from left to right , but note that in case of binary number , if we have to do indexing we do it
from right to left, because 5 for ex in binary is 101 , but in computer it is not stored as 101 but 0000…....101, so we
can not actually find a starting point from left , that’s why we do indexing from right to left

● Xor with 0 , gives back the same number

★ Theory

Bit_Magic_Theory.pdf

1) Single number

// a^a^a^a....(even times) = 0

// a^a^a^a....(odd times) = a

int singleNumber(vector<int>& nums)


{
int x_o_r = 0;

for(int i = 0 ; i < nums.size() ; i++)


{
x_o_r = nums[i] ^ x_o_r;
}
return x_o_r;
}

i) Single number II
Approach 1 -> use map and maintain freq of all elements , and at last return those elements which have freq as one , T.C ->
O(nlogn) S.C -> O(n)

Approach 2 -> sort the array , after sorting the element which has both of its adjacent neighbors different will be your ans ,
for ex array after sorting look like , arr[ ] = { 1 , 1 ,1 , 3, 3, 3, 4 , 5 ,5 5} , so clearly 4 has both of its neighbor different , handle
corner cases when your ans element is at extreme edges of arr,
T.C -> O(nlogn) S.C -> O(1)

Approach 3 -> We know that each integer is represented as a 32 bit binary number internally, the idea is to count the
number of 1’s, in each of these 32 indexes, and if at any any of these indexes the overall count of 1’s is of of the form 3n +1 ,
then this means ,the unique element which we are searching for has its bit set at that specific index .

The i pointer moves from left to right , covering each of the 32 bit indexes, and for each of the ith index column , the j pointer
moves from top to bottom to find the count of 1’s in that ith index column

—---------------------------------------------------------------
Count of 1’s in each of 32 indexes : 0 ….0 0 0 0 0 3 6 4

Since at index 0 the overall count of 1’s is 4 which is of form 3n + 1, we can say that the unique element
that we are searching for has its bit set at index 0,

While at rest all of the indexes , the overall count of 1’s is in the form of 3n , so we can safely say that the unique element
that we are searching for has its bit clear at all other indexes,

Hence ,the unique element that we are searching for is 0… 0 001

int singleNumber(vector<int>& nums)


{
int ans = 0;

for(int i = 0 ; i < 32 ; i++) // i pointer to traverse the 32 indexes , it moves from right
to left
{
int count = 0; // for each new column , initialize count with 0 again

for(int j = 0 ; j < nums.size() ; j++) // j pointer to traverse that whole column , it


moves from bottom to top
{
if( (nums[j]&(1<<i)) != 0){count++;} // extracting the bit at ith index of nums[j]
and checking if that bit is == 1 , if yes then we increment the count by ++
}

if( (count %3) == 1){ans = ans |(1<< i);} // if after traversing that whole column the
count of 1's is of form 3n+1 ,the we set the ith index bit in our ans variable as 1
}
return ans; // return the ans
}

T.C -> O(32 * n) approx as O (N) , S.C -> O(1)

ii) Single number III

Brute -> use nested for loop , for( i = 0 to n)


for( j = 0 to n)
if(a[i] == a[j]) count++;

at last print those laments with count = 1, T.C = O(n^2)

Better -> Create a map<int,int> mp , and push all elements in it , while maintaining freq and at last print those elements which
have freq = 1 , T.C = O(nlogn)

Best -> Using Bit - Manipulation, we can get best time complexity , T.C = O(n)

Let a and b be the two unique numbers


● XORing all numbers gives you (a xor b)
● (a xor b) must be non-zero , otherwise they are equal

● If bit_i in (a xor b) is 1, then this means bit_i at a and b are different.


● To find bit_i you can find rightmost set bit using the formula m & -(m-1)
● Partition the numbers into two groups: one group with bit_i == 1 and the other group with bit_i == 0.
● a is in one group and b is in the other.

● a is the only single number in its group.

● b is also the only single number in its group.

● XORing all numbers in a's group to get a


● XORing all numbers in b's group to get b
We can see that 5 and 7 appear one time, and xoring 5 ^ 7 gives 2 which in binary is 0 1 0 , we can observe that at
index 1 we get bit 1, so now we form two groups , in one group all those numbers are present which have at index 1
as their bit as 0 , and other group which have at index 1 as their bit as 1,

Clearly we can see if we xor all numbers in 5’s group we get 5 , because all other members in 5’s group appear two
times and 5 appear only one time and similarly if we xor all numbers in 7’s group we get 7

vector<int> singleNumber(vector<int>& nums)


{
int x_o_r = 0;

for( int i = 0 ; i < nums.size(); i++)


{
x_o_r = x_o_r ^ nums[i];
}

long long int n = x_o_r; // when we are doing x_o_r - 1 , it is causing integer overflow ,
for a very large -ve no , so we store it in long long data type

int index = log2(n & (~(n-1))); // finding index at which rightmost bit is set

int ans1 = 0,ans2 = 0;

for(int i = 0 ; i < nums.size() ; i++)


{
if( (nums[i] & (1<<index)) == 0) // finding all those numbers which have at req index
as their bit as 0
ans1 = ans1 ^ nums[i];
else // all those numbers which have at req index as their bit
as 1
ans2 = ans2 ^ nums[i];
}

vector<int> v{ans1,ans2};
return v;
}

2) Find XOR from 1 to N in O(1)

Brute -> O(n)


int ans = 0;
for( int i = 0 ; i <= n ; i++)
ans = ans ^ i;

Best -> O(1)

We simply find ans for different values of N , and then after observing we can see a pattern is found,

i) XOR of no’s b/w range [L,R]

For ex if L = 3 AND R= 6 , we need to find value of 3 ^ 4 ^ 5 ^ 6 in O( 1 ) time

Using the previous method find XOR(6) = 1 ^ 2 ^ 3 ^ 4 ^ 5 ^ 6


and XOR(2) = 1 ^ 2

Now , we need to remove 1 ^ 2 term from this expression 1 ^ 2 ^ 3 ^ 4 ^ 5 ^ 6 , to get our ans

And we know the best way to cancel elements in XOR, is to take XOR of that no with itself
So we do

-> (1^2^3^4^5^6) ^(1^2)

= 3^4^5^6

So , find XOR till R , and XOR till L-1 , and then take their XOR’S

3) extract bit at Kth index


/*Approach 1 -> using left shift operator
let's say for n = 10 check whether its 2nd bit is set or not

3 2 1 0 --> indexes
now 10 = 1 0 1 0 in binary ,

since k = 2 , we keep 1 at index 2 and rest bit as zero , and hence we get 100 as our mask and
now,we know that

if n & 100 == 0 , then this means at 2nd index, we have bit 0


and if n & 100 != 0 , then this means at 2nd index, we have bit 1

so somehow , we need to move 1 at 2nd bit position to create a mask with which
we can do then & operation

here k = 2 , so we need to move 1 to 2nd index

for general if we do (1 << k) --> then this moves 1 to kth index and after that we can
do & operation so ,

if n & (1 << k) == 0 , then this means at Kth index, we have bit 0


and if n & (1 << k) != 0 , then this means at Kth index, we have bit 1

*/

/*Approach 2 -> using right shift operator ,


let's say for n = 10 check whether its 2nd bit is set or not

3 2 1 0 --> indexes
now 10 = 1 0 1 0 in binary ,

we know that if n & 1 == 0 , then this means at 0th index, we have bit 0
and if n & 1 != 0 , then this means at 0th index, we have bit 1

so ,if somehow we could bring its 2nd index bit at 0th index , then after that using & operator
with 1 we could check that whether 2nd bit is set or not

to bring 2nd index bit at 0th index , we do n >> k ( k = 2 here)

and after that simply use the n & 1 property

so if ( n>>k ) & 1 == 0 --> means 2 index bit is 0


and if ( n>>k ) & 1 != 0 --> means 2 index bit is 1
*/

bool checkKthBit(int n, int k)


{
if( ((1<<k) & 1) != 0) return true; // means 1 is present at kth index
else return false; // means 0 is present at kth index
}
4) set the Kth index bit

// let ‘s say k = 3 , then to set 3rd bit our mask would be 1000 , so generalizing for any k
for setting the kth bit , our mask would be 1 << k, after that once we have generated our mask
We simply need to take OR operation with input n

int setKthBit(int n, int k)


{
n = ( ( 1 << k ) | n );
return n;
}
5) clear the Kth index bit

// suppose to clear 2nd bit , your mask would be 0 1 1 , but how to generate 0 1 1 , for this
first generate 1 0 0 using ( 1 << 2) and then simply negate it so ~( 1 0 0 ) = 0 1 1

Generalizing this for any k , to generate mask , we first do (1 << k) and then negate it to get our
mask , so our mask is ~(1 << k)
Once the mask is generated we need to simply take & with the given input n

int clearKthBit(int n, int k)


{
n = n & ~(1 << k);
return n;
}

6)Toggle the Kth index bit


// to toggle the 2nd bit , our mask would be 100 , so generalizing for any k , our mask would be
1 << k , and once we have generated our mask we need to simply take XOR with given input n

int toggleKthBit(int n, int k)


{
return ((1 << k) ^ n);
}
7) clear the rightmost set bit

Meaning if suppose n = 10010100 ,


then we need we need to make n = 10010000 ,
The rightmost set bit is colored red , and we need to make it zero

If we do n & (n-1) it will do the job, not much intuition behind it take example to verify for n = 13 and for n = 12

int clearRightMostSetBit(int n, int k)


{
n = n & (n-1);
return n;
}

i) Power of 2 or not

If we take any 2’s power binary form we will observe a pattern


ex->
2 ^ 3 = 8 = 100
2 ^ 4 = 16 = 1000
2 ^ 5 = 32 = 10000

We will observe that any 2’s power binary representation has only 1 set bit and that is also present at MSB

So if we clear the rightmost set bit of any 2’s power binary form , we are sure that we will get all zeros

bool isPowerOfTwo(int x)
{

if(x == 0) return false; // corner case deal it separately u can’t find x-1 for x = 0

if( (x & (x-1)) == 0) return true;


else return false;
}
ii) find position the only set bit

Ques said to return the position of the only set bit, if it has more than 1 set bit , or 0 set bit then return -1;

int findPosition(int n)
{
if( n == 0 ) return -1; // when no set bit , it's all zero in binary i.e 0 in decimal so
return -1

if( (n & (n-1)) == 0)


{

/* for it to have only one set - bit , we can ensure that by clearing its rightmost-set bit ,
since it has only 1 set bit ,so after clearing its rightmost set bit we should get all zeros in
binary i.e 0 in decimal , in other words we could also say that for it to have only 1 set bit, it
should be power of 2 and , so we have used the condition to check power of 2
*/

return log2( (n & (~(n-1))) ) + 1; // position of the rightmost set bit , is the
position of the only set bit

}
else return -1; // more than one set bit case , so return -1
}

iii) Count no of set bits

Approach 1 -> iterate the whole binary number


the idea is to keep right shifting each bit one by one and then checking whether that bit is 1 or not , we keep doing this until all
the bits in its binary representation are over , i.e n reaches zero

T.C → O( 𝑙𝑜𝑔2n ) , to understand time complexity , don’t think in terms of binary number simply think that we are running a
loop until n reaches zero , and at each iteration we are doing n = n / 2, hence the time complexity is O( 𝑙𝑜𝑔2n )

int count = 0;

While ( n != 0)
{
if( n & 1 == 1) count;
n = n >> 1;
}
return count;

Approach 2-> the idea is to use the clear rightmost set bit ques concept , we keep clearing the rightmost set bit , until we
reach zero , and the no of times we need to clear the rightmost set bit = count of no of set bits

So for n = 13 , we have binary form = 1101


1. Once, We clear rightmost set bit we get = 1100
2. We again , clear rightmost set bit we get = 1000
3. We again ,clear rightmost set bit we get = 0000

T.C -> O(no of set bits present) , this is better than first approach

while( n != 0)
{
n = n & (n - 1);
count++;
}
return count;

iv) bits needed to be flipped to convert A to B

/* bits needed to be flipped to convert A to B , this ques can be re - stated to find count of the
no of bits that differ in A and B , because for ex: A = 0 1 1 1 0
B = 1 0 1 0 0
once we get the count of no of bits that differ, at those position only we have to toggle the bit
*/

/*
we use the property of xor , that xor of two same bits is 0 , and xor of two different bits is 1

for ex -> 0 1 1 1 0 ---> A


^ 1 0 1 0 0 ---> B
---------------------------
1 1 0 1 0

so we could say that count of set bits in A ^ B , is nothing but the count of number of bits that
differ in A and B
*/

int countBitsFlip(int a, int b)


{
int n = a ^ b;

int count = 0; // finding count of set bits in variable n


while( n != 0)
{
n = n & (n - 1);
count++;
}
return count;
}
8) index of rightmost set bit

Approach 1 ->Again use the same concept of iterating the whole binary number , and as soon as you find 1 break out, as you
keep shifting bits towards right , keep increment your index variable

T.C → O( 𝑙𝑜𝑔2n ) think that we are running a loop until n reaches zero , and at each iteration we are doing n = n / 2, hence the
time complexity is O( 𝑙𝑜𝑔2n )

int getFirstSetBit(int n)
{
int index = 0;
int ans = 0;

while(n != 0)
{
if((n & 1) == 1){ans = index;break;}
else
{
n = n >> 1;
index++;
}
}
return ans;
}
Approach 2 -> a simple formula , not much intuition , put values to verify the result

T.C → O( 𝑙𝑜𝑔2n ) same as that of first approach, but one line solution

int getFirstSetBit(int n)
{
return log2( n & (~(n-1)) );
}

9) find XOR of the XOR’s of subsets

The question is to find XOR of the XOR’s of all subsets. i.e if the set is { 1 , 3, 2 }. All subsets are : [{1}, {2}, {3}, {1, 3}, {1, 2},
{3, 2}, {1, 2, 3}]. Find the XOR of each of the subset and then find the XOR of every subset result

The ans is always going to be zero , because each element will eventually come even number of times when we find XOR of
each of the subset and then find the XOR of every subset result,

Only in one case when n == 1 i.e size of array is one , in that case ans will be that one element only
int findXOR(int arr[], int n)
{
if (n == 1)
return arr[0];
else
return 0;
}

10) Generating subsets using Bit -manipulation

Power Set | Print all Subsequences

One way to go about is using back-tracking , the other way could be by using bit- manipulation

For the arr[ ] = { 1 , 2 , 3 } , we have


The idea is that each possible subset can be written in terms of 0 and 1 , if u include an element in your subset consider it as
1 , and if u don’t include an element for subset consider it as 0

0 1 2 —> indexes
For ex - let’s say our arr[ ] is { 1 , 2 , 3 } , if i want to include 0th index value and 1st index value in my subset then i will set
the set the 0th and 1st index bit as 1 and for excluding the 2nd index value in my subset, i will set 2nd index bit as 0 , so the
value that we get is is 0 1 1 , and not 1 1 0

NOTE : -> There is a difference in indexing of array and binary numbers, when i say 0th index for array it means left
most - value , whereas 0th index for binary is right most bit

2 1 0 → indexes
So our mask is 0 1 1

❖ So , just remember that which-ever index value of array you want to include in subset , make that
corresponding index bit as 1,

So given an arr[ ] = { 1 , 2 , 3 } and If i say mask is 1 0 1 , then this mask corresponds to which subset ?

Ans -> find all those indexes whose bits value are set,

2 1 0 → indexes
So our mask is 1 0 1

We can see index 0 , and index 2 has their bits = 1, so our subset = {arr[0] ,arr[2] } = { 1 , 3 }

Now we need to find out range of our mask

All the possible mask values for arr [] = { 1 , 2 , 3 } are

0 0 0 —-> 0 in decimal
0 0 1 —-> 1 in decimal
0 1 0 —-> 2 in decimal
0 1 1 —-> 3 in decimal
1 0 0 —-> 4 in decimal
1 0 1 —-> 5 in decimal
1 1 0 —-> 6 in decimal
1 1 1 —-> 7 in decimal

Hence we can conclude that mask varies from 0 to 7 , or to be more generalized we can say that
mask varies from 0 to 2^n - 1 , where n is the size of array

// each mask value corresponds to one subset , and the subset which that mask represent is
founded using inner while loop , while the outer loop cover all the mask values from 0 to 2^n -1

// pow(2,n) is equivalent to writing 1<<n

vector<vector<int>> ans;

for (int mask = 0; mask < pow(2, n) ; mask++) // let ‘s say at any moment mask is 5 , then this
corresponds to mask as 1 0 1
{
vector<int> temp;

int index = 0; // to keep track of those indexes whose bit value is 1 in mask ,here mask = 1 0 1

int x = mask; // storing mask in another variable , so that mask doesn't gets changed

while ( x != 0 ) // iterating the binary number 1 0 1 , and as soon as we find a bit


whose value is 1 , we make a note of its corresponding index value , and then push arr[index]
{
if ((x & 1) == 1) {temp.push_back(v[index]);}
x = x >> 1;
index++;
}

ans.push_back(temp);
}

11 ) Divide without using * , /

Approach 1 -> see division as repeated subtraction

To find the remainder and quotient of 43 / 8 using subtraction


The moment dividend becomes smaller than divisor , at that moment , value of dividend = remainder , as we can see in this
example at the last stage our dividend becomes = 3 and is less than 8(divisor)
So remainder = 3

Also,the number of times this whole subtraction process needs to be done = 5 = quotient

// handle corner cases of negative inputs


int quotient = 0;
while (dividend >= divisor)
{
dividend = dividend - divisor;
quotient;
}
Approach 2- >

In short the algo for division using bit manipulation is ,let’s say u want to find 23 / 3

So here dividend = 23 , and divisor = 3,

𝑖
We try to find the highest possible value of i that satisfy this equation - > divisor * 2 < dividend , once we get that i
value ,we do 2 things

𝑖
1. We subtract ( divisor * 2 ) from our current dividend , to get new dividend ,
𝑖
so dividend = dividend - ( divisor * 2 )

𝑖 𝑖
2. And we also add this 2 to our ans variable so , ans = ans + 2

And we keep doing this whole process until our dividend is greater than divisor
For dry run of this algo , see the leetcode discuss section link
Complete Thinking Process | Intuitive Explanation | All rules followed | C++ code - LeetCode Discuss

while( dividend >= divisor)


{
int i = 0;

while( divisor * 2^i <= dividend ) keep doing i++;

Once you will come out of this inner while loop , the current i value will be such that this
equation -> divisor * 2^i <= dividend,will not be satisfied , but we need to find the highest
possible value of i , that satisfy this equation ,So we just take 1 value less than the current i
value ,

So do i - - ;

Now since we have got the required i value ,we need to do those two task that were mentioned above

1. dividend = dividend - (divisor * 2^i) ;


2. ans = ans + 2^i ;

}
return ans;

𝑖
But if you notice we have used operation of multiplying by 2 , but we can use left shift command to get the same result , this
was the whole reason why we were dealing with power of 2’s this whole time

divisor * 2^ i <= dividend ; is equivalent to -> (divisor << i) <= dividend ;

dividend = dividend - (divisor * 2^i) ; is equivalent to -> dividend = dividend - (divisor << i) ;

ans = ans + 2^i ; is equivalent to -> ans = ans + (1<< i) ;

/* we need to handle two corner cases

1. there can be chances that our quotient goes overflow , and this will happen only in cases when
you dividend = INT_MAX or INT_MIN and divisor = 1 or -1
so after seeing the range of dividend mentioned in ques, according deal these cases as instructed
in the ques

2. for safety purposes store your ans , dividend and divisor in long long int ,and also figure out
the sign of your ans , whether overall ans will be +ve or -ve

*/
int divide(int a, int b)
{

if( a == INT_MIN && b == -1){return INT_MAX;} // dealing with overflow corner cases
if( a == INT_MIN && b == 1){return INT_MIN;}
if( a == INT_MAX && b == 1){return INT_MAX;}
if( a == INT_MAX && b == -1){return -INT_MAX;}

long long int dividend = abs(a);


long long int divisor = abs(b);
long long int ans = 0;

while(dividend >= divisor)


{
int i = 0;

while( (divisor<<i) <= dividend) {i++;}


i--;

ans = ans + (1<<i);


dividend = dividend - (divisor<<i);

int sign = 1;
if( (a < 0 && b > 0) || ( a > 0 && b < 0)){sign = -1;} // if any of a or b is -ve , overall ans
will be -ve
if( sign == 1) return ans;
else return -ans;

12) find square without using *, /


Recursive code + using bit manipulation ,

In recursion , our aim is to break our i/p into smaller i/p until we reach the base case,

Here also with the help of this recursive relation square(n) = 4*square(n/2) ,(for n = even) we are breaking
our i/p into smaller i/p.So ,now instead of finding square of n ,our problem reduces to
finding square of n/2 we keep going like this until we hit the base case when n == 0

int square(int n)
{

if (n == 0) return 0; // Base case of recursive function call

n = abs(n); // Handle negative number

int x = n >> 1; // find x = n / 2

if ( (n & 1) == 1 ) // If n is odd
return ((square(x) << 2) + (x << 2) + 1);
else // If n is even
return (square(x) << 2);
}

You might also like