├── .editorconfig ├── .gitignore ├── README.md └── src ├── CountDigits.java ├── DecimalToBinary.java ├── Solution.java └── operators ├── and ├── CheckEvenOrOdd.java ├── CountSetBits.java ├── CountingBitsII.java └── PowerOfTwo.java ├── leftshift ├── BitLength.java ├── BitPosition.java ├── CheckKthBitSetOrUnset.java ├── LeftShift.java └── Subsets.java ├── not └── SwitchSign.java ├── or └── NumberOfFlips.java ├── rightshift ├── BitPosition.java ├── CheckIfKthBitIsSetOrUnset.java └── RightShift.java └── xor ├── FindOddOccurringElement.java ├── HammingDistance.java ├── MissingNumber.java ├── OppositeSigns.java ├── SingleNumber.java └── SwapTwoNumbers.java /.editorconfig: -------------------------------------------------------------------------------- 1 | [*.java] 2 | indent_size = 2 3 | indent_style = space -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | out 2 | .idea 3 | 4 | bit-manipulation-java-solutions.iml 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Grokking Bit Manipulation For Coding Interviews 2 | 3 | Bit manipulation is a powerful technique to optimize your algorithmic and problem-solving skills. 4 | 5 | Bit Mask is one of the most important/critical topics when someone starts preparing for coding interviews for FAANG companies. 6 | 7 | To kick things off, you'll start by learning about the number system and how it's represented. Then you'll learn about the six bitwise operators: AND, OR, NOT, XOR, and bit shifting. You will get tons of hands-on experience working through practice problems to help sharpen your understanding. 8 | 9 | By completing this course, you will be able to solve problems faster with greater efficiency. 10 | 11 | 1. Master problem solving that involves bit manipulation. 12 | 2. Master the bit manipulation, which allows you to organize all inputs in binary representation at the memory levels. 13 | 3. Master how the bit-level operations are computed. Understand that bit-level operations are based on all the arithmetic operations built into all languages. 14 | 4. Solve problems commonly asked in coding interviews related to bit manipulation. 15 | 5. These bit tricks could help competitive programming in run algorithms, mainly in `O(1)` time. 16 | 17 | 18 | course link: [Grokking Bit Manipulation For Coding Interviews](https://www.educative.io/courses/bit-manipulation?aff=xjzd) 19 | 20 | ## What is Bit Manipulation? 21 | Bit manipulation is the act of algorithmically manipulating bits or other pieces of data shorter than a word. Bit manipulation is something that has constant time complexity. This tutorial explains the basics and why Bitwise operators are used in programming. 22 | 23 | Bit manipulation applies logical operations on a sequence of bits to achieve a required result. It is an act of algorithmically manipulating bits or other pieces of data shorter than a word. 24 | 25 | Computer programming tasks that require bit manipulation include: 26 | 27 | 1. Low-level device control 28 | 2. Error detection and correction algorithms 29 | 3. Data compression 30 | 4. Encryption algorithms 31 | 5. Optimization 32 | 33 | ## List of Bitwise operators 34 | 35 | | **Operator** | **Name of Operator** | **Usage** | 36 | | :---: | :---: | :---: | 37 | | `&` | Bitwise AND |Used to mask particular part of byte | 38 | | `\|` | Bitwise OR | | 39 | | `~` | One's complement/NOT | Used to turn a bit on/off | 40 | | `^` | Bitwise XOR | | 41 | | `<<` | Left Shift |Used to shift the bit to the left | 42 | | `>>` | Right Shift |Used to shift the bit to the right | 43 | 44 | For most other tasks, modern programming languages allow the programmer to work directly with abstractions instead of bits representing those abstractions. 45 | 46 | Bit manipulation can obviate or reduce the need to loop over a data structure and speed up coding as bit manipulations are processed in parallel. 47 | 48 | Throughout the course, we offer many examples to help you understand the patterns in solving bit manipulation algorithmic problems. 49 | 50 | The problems solved under these patterns use a varied set of algorithmic techniques that you will encounter day-to-day. 51 | 52 | We will start with a brief introduction to each topic before jumping into practice problems. Under each subject, the first problem will explain the underlying pattern in detail to build the concepts that can apply to the latest issues. The latest problems will focus on each problem's different constraints and how our algorithm needs to change to handle them. 53 | 54 | Let's start to understand the crucial concepts of Bitwise operators. 55 | 56 | ## Bit-level operations 57 | 1. Sometimes, it becomes mandatory to consider data at the bit level. 58 | 2. We have to operate on the individual data bit. During source code drafting, we must also turn on/off particular data bits. At that time, we must use a bitwise operator to make our task more manageable. 59 | 3. C/Java programming provides us with different bitwise operators for manipulating bits. 60 | 4. Bitwise operators operate on integers and characters but not on data types float or double. 61 | 5. Using Bitwise operators, we can easily manipulate individual bits. 62 | 6. C/Java programming supports six bitwise operators. 63 | -------------------------------------------------------------------------------- /src/CountDigits.java: -------------------------------------------------------------------------------- 1 | public class CountDigits { 2 | public static void main(String[] args) { 3 | int number = 125; 4 | System.out.println("Number of digits : " + countDigits(number)); // recommended approach 5 | System.out.println("Number of digits : " + logarithmicApproach(number)); 6 | System.out.println("Number of digits : " + recursiveApproach(number)); 7 | System.out.println("Number of digits : " + stringApproach(number)); 8 | } 9 | 10 | // Optimal approach: Division 11 | // Time: O(n), run time depends on the number of digits in n. In the worst case, it iterates 12 | // through all the digits until it becomes 0. 13 | // Space: O(1), space complexity is O(1) since no additional space is allocated. 14 | private static int countDigits(int n) { 15 | int count = 0; 16 | 17 | while (n > 0) { 18 | ++count; 19 | n /= 10; 20 | } 21 | 22 | return count; 23 | } 24 | 25 | // logarithmic approach 26 | private static int logarithmicApproach(int n) { 27 | return n != 0 ? ((int) Math.floor(Math.log10(n) + 1)) : -1; 28 | } 29 | 30 | // recursive approach 31 | private static int recursiveApproach(int n) { 32 | // base checks 33 | if (n == 0) { 34 | return 0; 35 | } 36 | 37 | return (1 + recursiveApproach(n / 10)); 38 | } 39 | 40 | // string conversion 41 | private static int stringApproach(int n) { 42 | // you can also use String.valueOf(number) to convert int to string. 43 | String num = Integer.toString(n); 44 | 45 | return num.length(); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/DecimalToBinary.java: -------------------------------------------------------------------------------- 1 | import java.util.Arrays; 2 | import java.util.Stack; 3 | 4 | public class DecimalToBinary { 5 | public static void main(String[] args) { 6 | int number = 125; 7 | System.out.println("Number of bits : " + bitShifting(number)); 8 | System.out.println("Number of bits : " + bitShiftingOptimal(number)); 9 | System.out.println("Number of bits : " + decimalToBinaryUsingStack(number)); 10 | } 11 | 12 | // bit shifting approach 13 | private static int bitShifting(int n) { 14 | int count = 0; 15 | while (n > 0) { 16 | ++count; 17 | n >>= 1; // this is equivalent to (n = n >> 1) OR (n = n/2); 18 | } 19 | return count; 20 | } 21 | 22 | // optimal solution - bit shifting 23 | private static int bitShiftingOptimal(int n) { 24 | int bitsCounter = 0; 25 | 26 | while ((1 << bitsCounter) <= n) { 27 | bitsCounter += 1; 28 | } 29 | 30 | return bitsCounter; 31 | } 32 | 33 | // using Stack approach 34 | private static String decimalToBinaryUsingStack(int n) { 35 | Stack stack = new Stack<>(); 36 | 37 | while (n > 0) { 38 | int remainder = n % 2; // remainder gives either 0 OR 1 39 | stack.push(remainder); // store the remainder value in stack 40 | n >>= 1; // this is equivalent to n = n/2; 41 | } 42 | 43 | // loggers 44 | Arrays.asList("while loop breaks only when \"number\" terminates to : " + n, " ") 45 | .forEach(System.out::println); 46 | 47 | int[] bits = new int[stack.size()]; 48 | int k = 0; 49 | 50 | while (!stack.isEmpty()) { 51 | // each bit is popped and stored in array of integers 52 | bits[k++] = stack.pop(); 53 | } 54 | 55 | return Arrays.toString(bits); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/Solution.java: -------------------------------------------------------------------------------- 1 | import java.util.ArrayList; 2 | import java.util.Arrays; 3 | import java.util.List; 4 | 5 | public class Solution { 6 | public static void main(String[] args) { 7 | List A = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5)); 8 | System.out.println(solve(A)); 9 | } 10 | 11 | // DO NOT MODIFY THE LIST. IT IS READ ONLY 12 | public static ArrayList solve(List A) { 13 | int i = 0; 14 | int j = A.size() - 1; 15 | 16 | while (i < j) { 17 | int temp = A.get(i); 18 | A.set(i, A.get(j)); 19 | A.set(j, temp); 20 | 21 | i++; 22 | j--; 23 | } 24 | 25 | return (ArrayList) A; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/operators/and/CheckEvenOrOdd.java: -------------------------------------------------------------------------------- 1 | package operators.and; 2 | 3 | import java.util.Arrays; 4 | 5 | public class CheckEvenOrOdd { 6 | public static void main(String[] args) { 7 | int firstNumber = 125; 8 | int secondNumber = 8; 9 | System.out.println("Number '" + firstNumber + "' is : " + checkEvenOdd(firstNumber)); 10 | System.out.println("Number '" + secondNumber + "' is : " + checkEvenOdd(secondNumber)); 11 | 12 | int[] nums = {1, 2, 3, 4, 5, 6, 7, 8, 9}; 13 | System.out.println(Arrays.toString(checkEvenOdd(nums))); 14 | } 15 | 16 | // Time: O(1) time 17 | // Space: O(1), no extra memory allocated 18 | private static String checkEvenOdd(int n) { 19 | return (n & 1) == 0 ? "Even" : "Odd"; 20 | } 21 | 22 | private static String[] checkEvenOdd(int[] nums) { 23 | String[] ans = new String[nums.length]; 24 | 25 | int k = 0; 26 | for (int n : nums) { 27 | ans[k++] = ((n & 1) == 1) ? "Odd" : "Even"; 28 | } 29 | return ans; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/operators/and/CountSetBits.java: -------------------------------------------------------------------------------- 1 | package operators.and; 2 | 3 | public class CountSetBits { 4 | public static void main(String[] args) { 5 | int number = 125; 6 | System.out.println("SetBit Count is : " + countSetBits(number)); 7 | System.out.println("SetBit Count is : " + countSetBits01(number)); 8 | System.out.println("SetBit Count is : " + countSetBits02(number)); 9 | System.out.println( 10 | "SetBit Count is : " + brainKernighanAlgorithm(number)); // optimal approach (recommended) 11 | System.out.println( 12 | "SetBit Count is : " + lookupTableApproach(number)); // optimal approach (recommended) 13 | } 14 | 15 | // Time: O(n), where n is the number of bits present in the data type, for int has 32bits, and 16 | // long has 64bits 17 | // Space: O(1), no extra memory allocated 18 | private static int countSetBits(int n) { 19 | int ans = 0; 20 | while (n > 0) { 21 | if (n % 2 != 0) { 22 | ans++; 23 | } 24 | n /= 2; 25 | } 26 | 27 | return ans; 28 | } 29 | 30 | // above solution can be further simplified into 31 | // Time: O(n), where n is the number of bits present in the data type, for int has 32bits, and 32 | // long has 64bits 33 | // Space: O(1), no extra memory allocated 34 | private static int countSetBits01(int n) { 35 | int count = 0; 36 | while (n > 0) { 37 | if ((n & 1) == 1) { 38 | count++; 39 | } 40 | n >>= 1; 41 | } 42 | return count; 43 | } 44 | 45 | // Above solutions can be further simplified into 46 | // Time: O(n), where n is the number of bits present in the data type, for int has 32bits, and 47 | // long has 64bits 48 | // Space: O(1), no extra memory allocated 49 | private static int countSetBits02(int n) { 50 | int count = 0; 51 | while (n > 0) { 52 | count += (n & 1); 53 | n >>= 1; 54 | } 55 | return count; 56 | } 57 | 58 | // Brian Kernighan’s algorithm (optimal and recommended approach) 59 | // Time: O(set-bit count)/ O(1), where setbit means number of `1` bits present in the given number 60 | // In the worst case, all 32bits can be `1` bits, so O(32) or O(64) time 61 | // Space: O(1), no extra memory allocated 62 | private static int brainKernighanAlgorithm(int n) { 63 | int count = 0; 64 | while (n > 0) { 65 | n &= (n - 1); 66 | count++; 67 | } 68 | return count; 69 | } 70 | 71 | // Lookup table approach (optimal approach, recommended) 72 | // Time: O(1), This requires an O(1) time solution to count the set bits in each of the 8-bit chunks. 73 | // Space: O(1), no extra memory allocated 74 | private static int lookupTableApproach(int n) { 75 | int[] table = new int[256]; 76 | table[0] = 0; 77 | 78 | for (int i = 1; i < 256; i++) { 79 | table[i] = (i & 1) + table[i >> 1]; // i >> 1 equals to i/2 80 | } 81 | 82 | int res = 0; 83 | for (int i = 0; i < 4; i++) { 84 | res += table[n & 0xff]; 85 | n >>= 8; 86 | } 87 | 88 | return res; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/operators/and/CountingBitsII.java: -------------------------------------------------------------------------------- 1 | package operators.and; 2 | 3 | import java.util.Arrays; 4 | 5 | public class CountingBitsII { 6 | public static void main(String[] args) { 7 | int number = 6; 8 | System.out.println("Result " + Arrays.toString(countingBits(number))); 9 | } 10 | 11 | // Time: O(n+1)*O(setbit count) or O(n+1)*O(1), The time taken is proportional to set bits in 12 | // binary representation and the outer loop which runs (n+1) times. 13 | // Space: O(1), no extra memory allocated 14 | private static int[] countingBits(int n) { 15 | int[] ans = new int[n + 1]; 16 | 17 | for (int i = 0; i <= n; i++) { 18 | ans[i] = helper(i); 19 | } 20 | 21 | return ans; 22 | } 23 | 24 | private static int helper(int n) { 25 | int count = 0; 26 | while (n > 0) { 27 | n &= (n - 1); 28 | count++; 29 | } 30 | return count; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/operators/and/PowerOfTwo.java: -------------------------------------------------------------------------------- 1 | package operators.and; 2 | 3 | public class PowerOfTwo { 4 | 5 | public static void main(String[] args) { 6 | System.out.println(isPowerOfTwo(6)); 7 | System.out.println(isPowerOfTwoOptimal(8)); // recommended approach 8 | } 9 | 10 | // Time: O(logn), we can do better using brian kernighan's algorithm 11 | // Space: O(1), no extra memory allocated 12 | private static boolean isPowerOfTwo(int n) { 13 | if (n == 0) { 14 | return false; 15 | } 16 | 17 | while (n != 1) { 18 | if (n % 2 != 0) { 19 | return false; 20 | } 21 | n >>= 1; 22 | } 23 | return true; 24 | } 25 | 26 | // optimal solution, recommended 27 | // Time: O(1), the run time depends on the number of 1-bits, worst-case time for int data type is 28 | // O(32), and O(64) for long 29 | // Space: O(1), no extra memory allocated 30 | private static boolean isPowerOfTwoOptimal(int n) { 31 | /* this works, but we can simplify this into single line as shown below 32 | if (n == 0) { 33 | return false; 34 | } 35 | return (n & (n - 1)) == 0; 36 | */ 37 | return n != 0 && (n & (n - 1)) == 0; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/operators/leftshift/BitLength.java: -------------------------------------------------------------------------------- 1 | package operators.leftshift; 2 | 3 | public class BitLength { 4 | public static void main(String[] args) { 5 | System.out.println(bitsLength(8)); 6 | System.out.println(bitsLength(2)); 7 | System.out.println(bitsLength(7)); 8 | } 9 | 10 | // Time: O(n), loop continues to run until it breaks 11 | // Space: O(1), no extra memory allocated 12 | private static int bitsLength(int number) { 13 | int bitsCounter = 0; 14 | 15 | while ((1 << bitsCounter) <= number) { 16 | bitsCounter += 1; 17 | } 18 | return bitsCounter; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/operators/leftshift/BitPosition.java: -------------------------------------------------------------------------------- 1 | package operators.leftshift; 2 | 3 | public class BitPosition { 4 | public static void main(String[] args) { 5 | System.out.println("First set-bit position for number: 18 is -> " + getFirstSetBitPos(18)); 6 | System.out.println("First set-bit position for number: 5 is -> " + getFirstSetBitPos(5)); 7 | System.out.println("First setb-it position for number: 32 is -> " + getFirstSetBitPos(32)); 8 | } 9 | 10 | // Time: O(1), always constant, as we are dealing with bit representation of decimals or ASCII. 11 | // They are represented in either 32/64 12 | // Space: O(1), no extra memory allocated 13 | private static int getFirstSetBitPos(int n) { 14 | if (n == 0) return 0; 15 | 16 | int k = 1; 17 | 18 | while (true) { 19 | if ((n & (1 << (k - 1))) == 0) { 20 | k++; 21 | } else { 22 | return k; 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/operators/leftshift/CheckKthBitSetOrUnset.java: -------------------------------------------------------------------------------- 1 | package operators.leftshift; 2 | 3 | public class CheckKthBitSetOrUnset { 4 | public static void main(String[] args) { 5 | System.out.println("First set-bit position for number: 18 is -> " + checkKthBitSet(18)); 6 | System.out.println( 7 | "First set-bit position for number: 5 is -> " + checkKthBitSet01(5, 3)); // optimal approach 8 | System.out.println( 9 | "First set-bit position for number: 32 is -> " 10 | + checkKthBitSet01(10, 2)); // optimal approach 11 | System.out.println( 12 | "First set-bit position for number: 32 is -> " 13 | + checkKthBitSet01(10, 1)); // optimal approach 14 | } 15 | 16 | // Time: O(1) 17 | // Space: O(1) 18 | private static int checkKthBitSet(int n) { 19 | if (n == 0) { 20 | return 0; 21 | } 22 | 23 | int k = 1; 24 | 25 | while (true) { 26 | if ((n & (1 << (k - 1))) == 0) { 27 | k++; 28 | } else { 29 | return k; 30 | } 31 | } 32 | } 33 | 34 | // Time: O(1), always constant, as we are dealing with bit representation of decimals or ASCII. 35 | // They are represented in either 32/64 36 | // Space: O(1), no extra memory allocated 37 | private static boolean checkKthBitSet01(int n, int k) { 38 | return (n & (1 << (k - 1))) != 0; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/operators/leftshift/LeftShift.java: -------------------------------------------------------------------------------- 1 | package operators.leftshift; 2 | 3 | public class LeftShift { 4 | public static void main(String[] args) { 5 | int number = 100; 6 | 7 | System.out.println(number + " shifted 1 position left, yields to " + helper(number, 1)); 8 | System.out.println(number + " shifted 2 positions left, yields to " + helper(number, 2)); 9 | System.out.println(number + " shifted 3 positions left, yields to " + helper(number, 3)); 10 | System.out.println(number + " shifted 4 positions left, yields to " + helper(number, 4)); 11 | } 12 | 13 | private static int helper(int number, int i) { 14 | return number << i; // multiplies `number` with 2^i times. 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/operators/leftshift/Subsets.java: -------------------------------------------------------------------------------- 1 | package operators.leftshift; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | public class Subsets { 7 | public static void main(String[] args) { 8 | int[] nums = {1, 2, 3}; 9 | System.out.println(subsets(nums)); 10 | } 11 | 12 | // Time: O(n * 2^n), the complexity is n times the power-set. 13 | // Space: O(2^n), storing `2^n` subset elements in an array. so the extra memory/space allocated 14 | // is directly proportional to the O(2^n). 15 | public static List> subsets(int[] nums) { 16 | List> result = new ArrayList<>(); 17 | int n = nums.length; 18 | int powSize = (int) Math.pow(2, n); 19 | 20 | for (int i = 0; i < powSize; i++) { 21 | List val = new ArrayList<>(); 22 | for (int j = 0; j < n; j++) { 23 | if ((i & (1 << j)) != 0) { 24 | val.add(nums[j]); 25 | } 26 | } 27 | result.add(val); 28 | } 29 | 30 | return result; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/operators/not/SwitchSign.java: -------------------------------------------------------------------------------- 1 | package operators.not; 2 | 3 | public class SwitchSign { 4 | public static void main(String[] args) { 5 | int number = 8; 6 | System.out.println(switchSign(number)); 7 | } 8 | 9 | // Time: O(1), We are not running a loop or scaling the inputs. The inputs never change. So the 10 | // operation takes a single unit of time, which is O(1) 11 | // Space: O(1), no extra memory allocated 12 | private static int switchSign(int number) { 13 | return ~number + 1; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/operators/or/NumberOfFlips.java: -------------------------------------------------------------------------------- 1 | package operators.or; 2 | 3 | public class NumberOfFlips { 4 | public static void main(String[] args) { 5 | int a = 2; 6 | int b = 6; 7 | int c = 5; 8 | System.out.println( 9 | "Min Flips required to make two numbers equal to third is : " + numberOfFlips(a, b, c)); 10 | System.out.println( 11 | "Min Flips required to make two numbers equal to third is : " 12 | + numberOfFlipsSimplified(a, b, c)); 13 | } 14 | 15 | private static int numberOfFlips(int a, int b, int c) { 16 | int ans = 0; 17 | for (int i = 0; i < 32; i++) { 18 | int bitC = ((c >> i) & 1); 19 | int bitA = ((a >> i) & 1); 20 | int bitB = ((b >> i) & 1); 21 | 22 | if ((bitA | bitB) != bitC) { 23 | if (bitC == 0) { 24 | if (bitA == 1 && bitB == 1) { 25 | ans += 2; 26 | } else { 27 | ans += 1; 28 | } 29 | } else { 30 | ans += 1; 31 | } 32 | } 33 | } 34 | return ans; 35 | } 36 | 37 | // above snippet/algorithm can be further simplified into 38 | // Time: O(logn), as we are comparing bit values in each integer 39 | // Space: O(1), no extra memory allocated 40 | private static int numberOfFlipsSimplified(int a, int b, int c) { 41 | int ans = 0; 42 | for (int i = 0; i < 32; i++) { 43 | int bitC = ((c >> i) & 1); 44 | int bitA = ((a >> i) & 1); 45 | int bitB = ((b >> i) & 1); 46 | 47 | if ((bitA | bitB) != bitC) { 48 | ans += (bitC == 0) ? (bitA == 1 && bitB == 1) ? 2 : 1 : 1; 49 | } 50 | } 51 | return ans; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/operators/rightshift/BitPosition.java: -------------------------------------------------------------------------------- 1 | package operators.rightshift; 2 | 3 | public class BitPosition { 4 | public static void main(String[] args) { 5 | System.out.println("First set-bit position for number: 18 is -> " + helper(18)); 6 | System.out.println("First set-bit position for number: 5 is -> " + helper(5)); 7 | System.out.println("First set-bit position for number: 32 is -> " + helper(32)); 8 | } 9 | 10 | // Time: O(1), always constant, as we are dealing with bit representation of decimals or ASCII. 11 | // They are represented in either 32/64 12 | // Space: O(1), no extra memory allocated 13 | private static int helper(int n) { 14 | if (n == 0) return 0; 15 | 16 | int k = 1; 17 | 18 | while (true) { 19 | if (((n >> (k - 1)) & 1) == 0) { 20 | k++; 21 | } else { 22 | return k; 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/operators/rightshift/CheckIfKthBitIsSetOrUnset.java: -------------------------------------------------------------------------------- 1 | package operators.rightshift; 2 | 3 | public class CheckIfKthBitIsSetOrUnset { 4 | public static void main(String[] args) { 5 | System.out.println("First set-bit position for number: 18 is -> " + checkIfKthSetOrUnset(18)); 6 | System.out.println("First set-bit position for number: 5 is -> " + checkIfKthSetOrUnset(5)); 7 | System.out.println("First set-bit position for number: 32 is -> " + checkIfKthSetOrUnset(32)); 8 | 9 | // optimal approaches 10 | System.out.println("n = 5, k = 3 : " + checkKthBitSet01(5, 3)); 11 | System.out.println("------------"); 12 | System.out.println("n = 10, k = 2 : " + checkKthBitSet01(10, 2)); 13 | System.out.println("------------"); 14 | System.out.println("n = 10, k = 1 : " + checkKthBitSet01(10, 1)); 15 | } 16 | 17 | // Time: O(1) 18 | // Space: O(1) 19 | private static int checkIfKthSetOrUnset(int n) { 20 | if (n == 0) { 21 | return 0; 22 | } 23 | 24 | int k = 1; 25 | 26 | while (true) { 27 | if (((n >> (k - 1)) & 1) == 0) { 28 | k++; 29 | } else { 30 | return k; 31 | } 32 | } 33 | } 34 | 35 | // Time: O(1), always constant, as we are dealing with bit representation of decimals or ASCII. 36 | // They are represented in either 32/64 37 | // Space: O(1), no extra memory allocated 38 | public static boolean checkKthBitSet01(int n, int k) { 39 | // return (n & (1 << (k - 1))) != 0; this is using left shift 40 | return ((n >> (k - 1)) & 1) == 1; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/operators/rightshift/RightShift.java: -------------------------------------------------------------------------------- 1 | package operators.rightshift; 2 | 3 | public class RightShift { 4 | public static void main(String[] args) { 5 | int number = 100; 6 | 7 | System.out.println(number + " shifted 1 position right, yields to " + helper(number, 1)); 8 | System.out.println(number + " shifted 2 positions right, yields to " + helper(number, 2)); 9 | System.out.println(number + " shifted 3 positions right, yields to " + helper(number, 3)); 10 | System.out.println(number + " shifted 4 positions right, yields to " + helper(number, 4)); 11 | } 12 | 13 | private static int helper(int number, int i) { 14 | return number >> i; // divides `number` with 2^i times. 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/operators/xor/FindOddOccurringElement.java: -------------------------------------------------------------------------------- 1 | package operators.xor; 2 | 3 | public class FindOddOccurringElement { 4 | public static void main(String[] args) { 5 | int result = helper(new int[] {4, 3, 3, 4, 4, 4, 5, 3, 5}); 6 | System.out.println("Odd occurring element is " + result); 7 | } 8 | 9 | // Time: O(n), where n is the number of elements in the array. 10 | // Space: O(1), no extra memory allocated 11 | private static int helper(int[] arr) { 12 | int res = 0; 13 | for (int value : arr) { 14 | res ^= value; 15 | } 16 | return res; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/operators/xor/HammingDistance.java: -------------------------------------------------------------------------------- 1 | package operators.xor; 2 | 3 | public class HammingDistance { 4 | public static void main(String[] args) { 5 | int a = 1; 6 | int b = 8; 7 | System.out.println("Hamming Distance between two integers is " + hammingDistance(a, b)); 8 | System.out.println( 9 | "Hamming Distance between two integers is " + hammingDistanceOptimal(a, b)); // recommended 10 | } 11 | 12 | public static int hammingDistance(int a, int b) { 13 | int xor = a ^ b; 14 | int distance = 0; 15 | 16 | while (xor != 0) { 17 | if (xor % 2 == 1) { 18 | distance += 1; 19 | } 20 | xor >>= 1; 21 | } 22 | 23 | return distance; 24 | } 25 | 26 | // recommended solution 27 | // Time: O(1), the input size of the integer is fixed, constant time complexity 28 | // Space: O(1), no extra memory allocated 29 | public static int hammingDistanceOptimal(int a, int b) { 30 | int xor = a ^ b; 31 | int distance = 0; 32 | 33 | while (xor != 0) { 34 | distance += 1; 35 | xor &= (xor - 1); // equals to `xor = xor & ( xor - 1);` 36 | } 37 | 38 | return distance; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/operators/xor/MissingNumber.java: -------------------------------------------------------------------------------- 1 | package operators.xor; 2 | 3 | import java.util.HashSet; 4 | 5 | public class MissingNumber { 6 | public static void main(String[] args) { 7 | int[] nums = {9, 6, 4, 2, 3, 5, 7, 0, 1}; 8 | System.out.println( 9 | "Missing element in the array is " + missingNumber(nums)); // hashtable approach 10 | System.out.println("Missing element in the array is " + missingNumber(nums)); // math approach 11 | System.out.println( 12 | "Missing element in the array is " + missingNumberOptimal(nums)); // optimal approach 13 | } 14 | 15 | // Hashtable approach 16 | // Time: O(n) for the loop, and O(1) for the hash table operation `add(...)` 17 | // Space: O(n), the space/memory required to store the elements of an array into hash_table 18 | // where n is the length of the array. 19 | private static int missingNumber(int[] nums) { 20 | HashSet set = new HashSet(); 21 | 22 | for (int num : nums) { 23 | set.add(num); 24 | } 25 | 26 | int n = nums.length + 1; 27 | 28 | for (int i = 0; i < n; i++) { 29 | if (!set.contains(i)) { 30 | return i; 31 | } 32 | } 33 | return -1; 34 | } 35 | 36 | // Math approach 37 | // Time: O(n), for the loop over n elements 38 | // Space: O(1), no extra memory allocated 39 | private static int missingNumberMathApproach(int[] nums) { 40 | int n = nums.length; 41 | int expectedSum = ((n * (n + 1)) / 2); 42 | 43 | int actualSum = 0; 44 | 45 | for (int num : nums) { 46 | actualSum += num; 47 | } 48 | 49 | return expectedSum - actualSum; 50 | } 51 | 52 | // Optimal approach 53 | // Time: O(n), where n is the number of elements present in the array. 54 | // Space: O(1), no extra memory allocated 55 | private static int missingNumberOptimal(int[] nums) { 56 | int n = nums.length + 1; 57 | int res = 0; 58 | 59 | for (int i = 0; i < n; i++) { 60 | res ^= i; 61 | } 62 | 63 | for (int value : nums) { 64 | res ^= value; 65 | } 66 | return res; 67 | } 68 | 69 | // above solution can be further optimized to below 70 | private static int missingNumberOptimal01(int[] nums) { 71 | int missing = nums.length; 72 | 73 | for (int i = 0; i < nums.length; i++) { 74 | missing ^= i ^ nums[i]; 75 | } 76 | 77 | return missing; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/operators/xor/OppositeSigns.java: -------------------------------------------------------------------------------- 1 | package operators.xor; 2 | 3 | public class OppositeSigns { 4 | 5 | public static void main(String[] args) { 6 | int x = 100, y = -1; 7 | System.out.println("For inputs " + x + ", " + y + " : " + oppositeSigns(x, y)); 8 | 9 | int z = 100, p = 501; 10 | System.out.println("For inputs " + z + ", " + p + " : " + oppositeSigns(z, p)); 11 | } 12 | 13 | // Time: O(1), We are not running a loop or scaling the inputs. The inputs never change. So, the 14 | // operation takes a single unit of time, which is O(1) 15 | // Space: O(1), no extra memory allocated 16 | private static String oppositeSigns(int x, int y) { 17 | return (x ^ y) < 0 ? "Signs are opposite" : "Signs are not opposite"; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/operators/xor/SingleNumber.java: -------------------------------------------------------------------------------- 1 | package operators.xor; 2 | 3 | import java.util.HashMap; 4 | import java.util.HashSet; 5 | 6 | public class SingleNumber { 7 | public static void main(String[] args) { 8 | int[] nums = {4, 1, 2, 9, 1, 4, 2}; 9 | System.out.println("Element appearing one time is " + singleNumber(nums)); 10 | System.out.println("Element appearing one time is " + singleNumberMathApproach(nums)); 11 | System.out.println( 12 | "Element appearing one time is " + singleNumberOptimal(nums)); // recommended approach 13 | } 14 | 15 | // Hashtable approach 16 | // Time: O(n) for the loop, and O(1) for the hash table operation `pop()` 17 | // Space: O(n), the space/memory required to store the elements of an array into hash_table 18 | // where n is the length of the array. 19 | private static int singleNumber(int[] nums) { 20 | HashMap lookup = new HashMap<>(); 21 | 22 | for (int i : nums) { 23 | lookup.put(i, lookup.getOrDefault(i, 0) + 1); 24 | } 25 | 26 | for (int i : nums) { 27 | if (lookup.get(i) == 1) { 28 | return i; 29 | } 30 | } 31 | 32 | return -1; 33 | } 34 | 35 | // Math approach 36 | // Time: O(n), where n is the number of elements present in the array. 37 | // Space: O(n), the space/memory required to store the elements of an array into hash_table 38 | private static int singleNumberMathApproach(int[] nums) { 39 | int sumOfUniqueElements = 0, totalSum = 0; 40 | HashSet set = new HashSet<>(); 41 | 42 | for (int num : nums) { 43 | if (!set.contains(num)) { 44 | set.add(num); 45 | sumOfUniqueElements += num; 46 | } 47 | totalSum += num; 48 | } 49 | return 2 * sumOfUniqueElements - totalSum; 50 | } 51 | 52 | // optimal approach 53 | // Time: O(n), where n is the number of elements present in the array 54 | // Space: O(1), no extra memory/space allocated. 55 | private static int singleNumberOptimal(int[] nums) { 56 | int xor = 0; 57 | for (int num : nums) { 58 | xor ^= num; 59 | } 60 | return xor; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/operators/xor/SwapTwoNumbers.java: -------------------------------------------------------------------------------- 1 | package operators.xor; 2 | 3 | public class SwapTwoNumbers { 4 | public static void main(String[] args) { 5 | int a = 10, b = 121; 6 | swapSigns(a, b); 7 | } 8 | 9 | private static void swapSigns(int a, int b) { 10 | a = a ^ b; 11 | b = b ^ a; 12 | a = a ^ b; 13 | 14 | System.out.println("Finally, after swapping; a = " + a + " , b = " + b); 15 | } 16 | } 17 | --------------------------------------------------------------------------------