Skip to content

refactor: NonRepeatingElement #5375

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Aug 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 45 additions & 60 deletions src/main/java/com/thealgorithms/maths/NonRepeatingElement.java
Original file line number Diff line number Diff line change
@@ -1,76 +1,61 @@
package com.thealgorithms.maths;

import java.util.Scanner;

/*
* Find the 2 elements which are non repeating in an array
/**
* Find the 2 elements which are non-repeating in an array
* Reason to use bitwise operator: It makes our program faster as we are operating on bits and not
* on actual numbers.
*
* Explanation of the code:
* Let us assume we have an array [1, 2, 1, 2, 3, 4]
* Property of XOR: num ^ num = 0.
* If we XOR all the elements of the array, we will be left with 3 ^ 4 as 1 ^ 1
* and 2 ^ 2 would give 0. Our task is to find num1 and num2 from the result of 3 ^ 4 = 7.
* We need to find the two's complement of 7 and find the rightmost set bit, i.e., (num & (-num)).
* Two's complement of 7 is 001, and hence res = 1. There can be 2 options when we Bitwise AND this res
* with all the elements in our array:
* 1. The result will be a non-zero number.
* 2. The result will be 0.
* In the first case, we will XOR our element with the first number (which is initially 0).
* In the second case, we will XOR our element with the second number (which is initially 0).
* This is how we will get non-repeating elements with the help of bitwise operators.
*/
public final class NonRepeatingElement {
private NonRepeatingElement() {
}

public static void main(String[] args) {
try (Scanner sc = new Scanner(System.in)) {
int i;
int res = 0;
System.out.println("Enter the number of elements in the array");
int n = sc.nextInt();
if ((n & 1) == 1) {
// Not allowing odd number of elements as we are expecting 2 non repeating
// numbers
System.out.println("Array should contain even number of elements");
return;
}
int[] arr = new int[n];
/**
* Finds the two non-repeating elements in the array.
*
* @param arr The input array containing exactly two non-repeating elements and all other elements repeating.
* @return An array containing the two non-repeating elements.
* @throws IllegalArgumentException if the input array length is odd.
*/
public static int[] findNonRepeatingElements(int[] arr) {
if (arr.length % 2 != 0) {
throw new IllegalArgumentException("Array should contain an even number of elements");
}

System.out.println("Enter " + n + " elements in the array. NOTE: Only 2 elements should not repeat");
for (i = 0; i < n; i++) {
arr[i] = sc.nextInt();
}
int xorResult = 0;

// Find XOR of the 2 non repeating elements
for (i = 0; i < n; i++) {
res ^= arr[i];
}

// Finding the rightmost set bit
res = res & (-res);
int num1 = 0;
int num2 = 0;
// Find XOR of all elements
for (int num : arr) {
xorResult ^= num;
}

for (i = 0; i < n; i++) {
if ((res & arr[i]) > 0) { // Case 1 explained below
num1 ^= arr[i];
} else {
num2 ^= arr[i]; // Case 2 explained below
}
// Find the rightmost set bit
int rightmostSetBit = xorResult & (-xorResult);
int num1 = 0;
int num2 = 0;

// Divide the elements into two groups and XOR them
for (int num : arr) {
if ((num & rightmostSetBit) != 0) {
num1 ^= num;
} else {
num2 ^= num;
}

System.out.println("The two non repeating elements are " + num1 + " and " + num2);
}

return new int[] {num1, num2};
}
/*
* Explanation of the code:
* let us assume we have an array [1,2,1,2,3,4]
* Property of XOR: num ^ num = 0.
* If we XOR all the elemnets of the array we will be left with 3 ^ 4 as 1 ^ 1
* and 2 ^ 2 would give
* 0. Our task is to find num1 and num2 from the result of 3 ^ 4 = 7. We need to
* find two's
* complement of 7 and find the rightmost set bit. i.e. (num & (-num)) Two's
* complement of 7 is 001
* and hence res = 1. There can be 2 options when we Bitise AND this res with
* all the elements in our
* array
* 1. Result will come non zero number
* 2. Result will be 0.
* In the first case we will XOR our element with the first number (which is
* initially 0)
* In the second case we will XOR our element with the second number(which is
* initially 0)
* This is how we will get non repeating elements with the help of bitwise
* operators.
*/
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.thealgorithms.maths;

import static org.junit.jupiter.api.Assertions.assertArrayEquals;

import java.util.stream.Stream;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;

public class NonRepeatingElementTest {

private record TestData(int[] input, int[] expected) {
}

private static Stream<TestData> provideTestCases() {
return Stream.of(new TestData(new int[] {1, 2, 1, 3, 2, 4}, new int[] {3, 4}), new TestData(new int[] {-1, -2, -1, -3, -2, -4}, new int[] {-3, -4}), new TestData(new int[] {-1, 2, 2, -3, -1, 4}, new int[] {-3, 4}));
}

@ParameterizedTest
@MethodSource("provideTestCases")
void testFindNonRepeatingElements(TestData testData) {
int[] result = NonRepeatingElement.findNonRepeatingElements(testData.input);
assertArrayEquals(testData.expected, result);
}

@Test
public void testFindNonRepeatingElementsWithLargeNumbers() {
assertArrayEquals(new int[] {200000, 400000}, NonRepeatingElement.findNonRepeatingElements(new int[] {100000, 200000, 100000, 300000, 400000, 300000}));
}
}