Skip to content
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

Refactor: Combination #5225

Closed
wants to merge 13 commits into from
Closed
70 changes: 33 additions & 37 deletions src/main/java/com/thealgorithms/backtracking/Combination.java
Original file line number Diff line number Diff line change
@@ -1,62 +1,58 @@
package com.thealgorithms.backtracking;

import java.util.Arrays;
import java.util.LinkedList;
import java.util.ArrayList;
import java.util.List;
import java.util.TreeSet;

/**
* Finds all permutations of given array
* @author Alan Piao (<a href="https://github.com/cpiao3">git-Alan Piao</a>)
* Finds all combinations of a given array of unique elements.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not verified anywhere. Maybe the input type should be Set<T>? Or maybe this statement is not needed. I think what we are aiming for it to mimic the itertools.combinations from python.

*/
public final class Combination {
private Combination() {
}

private static int length;

/**
* Find all combinations of given array using backtracking
* @param arr the array.
* @param n length of combination
* @param <T> the type of elements in the array.
* @return a list of all combinations of length n. If n == 0, return null.
* Finds all combinations of a given array of length n.
* If n is 0, an empty list is returned.
*
* @param data the array of elements.
* @param combinationSize the desired size of combinations.
* @param <T> the type of elements in the array (assumed to be unique).
* @return a list of all combinations of size `combinationSize`.
* @throws IllegalArgumentException if combinationSize is negative.
*/
public static <T> List<TreeSet<T>> combination(T[] arr, int n) {
if (n == 0) {
public static <T> List<TreeSet<T>> combination(T[] data, int combinationSize) {
if (combinationSize < 0) {
throw new IllegalArgumentException("Combination size cannot be negative.");
} else if (combinationSize == 0) {
return null;
}
Comment on lines +27 to 29
Copy link
Member

@vil02 vil02 Jul 4, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not see a reason to return null here. You can just remove this branch, so the result would be Arrays.asList(new TreeSet<Integer>()).

The problem is ArrayCombination. The method combination will need an update. I force merged #5181, so this null problem will no longer occur.

length = n;
T[] array = arr.clone();
Arrays.sort(array);
List<TreeSet<T>> result = new LinkedList<>();
backtracking(array, 0, new TreeSet<T>(), result);
return result;

List<TreeSet<T>> combinations = new ArrayList<>();
findCombinationsRecursive(data, 0, new ArrayList<>(), combinations, combinationSize);
return combinations;
}

/**
* Backtrack all possible combinations of a given array
* @param arr the array.
* @param index the starting index.
* @param currSet set that tracks current combination
* @param result the list contains all combination.
* Recursive helper function to find combinations using backtracking.
*
* @param data the array of elements.
* @param startIndex the starting index for the current combination.
* @param currentCombination the current combination being built.
* @param combinations the list to store all found combinations.
* @param combinationSize the desired size of combinations.
* @param <T> the type of elements in the array.
*/
private static <T> void backtracking(T[] arr, int index, TreeSet<T> currSet, List<TreeSet<T>> result) {
if (index + length - currSet.size() > arr.length) {
private static <T> void findCombinationsRecursive(T[] data, int startIndex, List<T> currentCombination, List<TreeSet<T>> combinations, int combinationSize) {
if (currentCombination.size() == combinationSize) {
combinations.add(new TreeSet<>(currentCombination)); // Deep copy to avoid modification
return;
}
if (length - 1 == currSet.size()) {
for (int i = index; i < arr.length; i++) {
currSet.add(arr[i]);
result.add((TreeSet<T>) currSet.clone());
currSet.remove(arr[i]);
}
}
for (int i = index; i < arr.length; i++) {
currSet.add(arr[i]);
backtracking(arr, i + 1, currSet, result);
currSet.remove(arr[i]);

for (int i = startIndex; i < data.length; i++) {
currentCombination.add(data[i]);
findCombinationsRecursive(data, i + 1, currentCombination, combinations, combinationSize);
currentCombination.removeLast();
}
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please:

  • add a test case with data being an empty array and combinationSize being positive,
  • add a test case with data being an empty array and combinationSize being zero,
  • add a test case with data being [1, 2, 3] and combinationSize being 2,
  • add a test case with data being an array of strings.

Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.thealgorithms.backtracking;

import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;

import java.util.List;
samuelfac marked this conversation as resolved.
Show resolved Hide resolved
import java.util.TreeSet;
Expand All @@ -11,21 +12,23 @@ public class CombinationTest {
@Test
void testNoElement() {
List<TreeSet<Integer>> result = Combination.combination(new Integer[] {1, 2}, 0);
assertTrue(result == null);
assertNull(result);
}

@Test
void testLengthOne() {
List<TreeSet<Integer>> result = Combination.combination(new Integer[] {1, 2}, 1);
assertTrue(result.get(0).iterator().next() == 1);
assertTrue(result.get(1).iterator().next() == 2);
assert result != null;
assertEquals(1, (int) result.get(0).getFirst());
assertEquals(2, (int) result.get(1).getFirst());
samuelfac marked this conversation as resolved.
Show resolved Hide resolved
}

@Test
void testLengthTwo() {
List<TreeSet<Integer>> result = Combination.combination(new Integer[] {1, 2}, 2);
Integer[] arr = result.get(0).toArray(new Integer[2]);
assertTrue(arr[0] == 1);
assertTrue(arr[1] == 2);
assert result != null;
Integer[] arr = result.getFirst().toArray(new Integer[2]);
assertEquals(1, (int) arr[0]);
assertEquals(2, (int) arr[1]);
samuelfac marked this conversation as resolved.
Show resolved Hide resolved
}
}