Search code examples
arraysdata-structuresbit-manipulation

Didn't understand this code to find rightmost set bit


I was solving a problem to find the repeating and missing numbers in an array with 1 to n numbers using the bit manipulation's XOR approach.

The following was the solution I found online-

public int[] repeatedNumber(final int[] arr) {
        int xor1;
        int n=arr.length;
        int set_bit_no;
 
        int i;
        int x = 0;
        int y = 0;
 
        xor1 = arr[0];
 
        /* Get the xor of all array elements */
        for (i = 1; i < n; i++)
            xor1 = xor1 ^ arr[i];
 
        /* XOR the previous result with numbers from 1 to n*/
        for (i = 1; i <= n; i++)
            xor1 = xor1 ^ i;
 
        /* Get the rightmost set bit in set_bit_no */
        set_bit_no = xor1 & ~(xor1 - 1);
 
        for (i = 0; i < n; i++) {
            if ((arr[i] & set_bit_no) != 0)
                /* arr[i] belongs to first set */
                x = x ^ arr[i];
 
            else
                /* arr[i] belongs to second set*/
                y = y ^ arr[i];
        }
        for (i = 1; i <= n; i++) {
            if ((i & set_bit_no) != 0)
                /* i belongs to first set */
                x = x ^ i;
 
            else
                /* i belongs to second set*/
                y = y ^ i;
        }
        
        for(int num : arr){
            if(num==x)
                return new int[]{x, y};
        }
        return new int[]{y,x};    
    }

After cracking my head for hours, I finally understood everything EXCEPT one line-

set_bit_no = xor1 & ~(xor1 - 1);

Now this is supposed to calculate the rightmost set bit for the final value after XOR operations. Even after testing this in debug mode and looking up YouTube videos, I have a few questions-

  1. Does the indexing for these values start from 1? (The debugger showed set_bit_no=1 even though xor value was 5, i.e. 101)
  2. Why do we subtract 1 from the xor value? What is the intuition behind that?

Solution

  • Let's say we have, in binary:

    xor1:               0b00110111001000
    xor1-1:             0b00110111000111
    ~(xor1-1):          0b11001000111000
    xor1 & ~(xor1-1):   0b00000000001000
    

    The secret is that subtracting one changes the trailing 0 bits to 1, and then changes the 1 bit to the left of those to 0 via borrow.

    The bit manipulations after that isolate the single bit that changes from 1 to 0.

    The name of the set_bit_no variable is confusing, since it's not a bit number. It's a bit value.