Skip to content

Commit 948ada9

Browse files
williamfisetclaude
andcommitted
Refactor PowerSet: return lists, add overflow guard, add tests
Replace print-based void methods with generic List-returning methods. Use incremental backtracking list instead of boolean[] used array. Add overflow guard for n > 30 in the binary method. Update README complexity from O(2^n) to O(n · 2^n). Add 9 JUnit 5 tests. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 6068e10 commit 948ada9

File tree

5 files changed

+145
-74
lines changed

5 files changed

+145
-74
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,7 @@ $ java -cp classes com.williamfiset.algorithms.search.BinarySearch
258258

259259
- [Bit manipulations](src/main/java/com/williamfiset/algorithms/other/BitManipulations.java) **- O(1)**
260260
- [List permutations](src/main/java/com/williamfiset/algorithms/other/Permutations.java) **- O(n!)**
261-
- [:movie_camera:](https://www.youtube.com/watch?v=RnlHPR0lyOE) [Power set (set of all subsets)](src/main/java/com/williamfiset/algorithms/other/PowerSet.java) **- O(2<sup>n</sup>)**
261+
- [:movie_camera:](https://www.youtube.com/watch?v=RnlHPR0lyOE) [Power set (set of all subsets)](src/main/java/com/williamfiset/algorithms/other/PowerSet.java) **- O(n · 2<sup>n</sup>)**
262262
- [Set combinations](src/main/java/com/williamfiset/algorithms/other/Combinations.java) **- O(n choose r)**
263263
- [Set combinations with repetition](src/main/java/com/williamfiset/algorithms/other/CombinationsWithRepetition.java) **- O((n+r-1) choose r)**
264264
- [Sliding Window Minimum/Maximum](src/main/java/com/williamfiset/algorithms/other/SlidingWindowMaximum.java) **- O(1)**

src/main/java/com/williamfiset/algorithms/other/BUILD

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,6 @@ java_binary(
3535
runtime_deps = [":other"],
3636
)
3737

38-
# bazel run //src/main/java/com/williamfiset/algorithms/other:PowerSet
39-
java_binary(
40-
name = "PowerSet",
41-
main_class = "com.williamfiset.algorithms.other.PowerSet",
42-
runtime_deps = [":other"],
43-
)
44-
4538
# bazel run //src/main/java/com/williamfiset/algorithms/other:SquareRootDecomposition
4639
java_binary(
4740
name = "SquareRootDecomposition",
Lines changed: 45 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1,84 +1,63 @@
11
/**
2-
* This code snippet shows how to generate the powerset of a set which is the set of all subsets of
3-
* a set. There are two common ways of doing this which are to use the binary representation of
4-
* numbers on a computer or to do it recursively. Both methods are shown here, pick your flavor!
2+
* Generates the power set of a set, which is the set of all subsets.
53
*
6-
* <p>Time Complexity: O( 2^n )
4+
* Two approaches are provided: an iterative method using binary representation of numbers, and a
5+
* recursive backtracking method. Both produce the same result.
6+
*
7+
* Time Complexity: O(n * 2^n)
78
*
89
* @author William Fiset, william.alexandre.fiset@gmail.com
910
*/
1011
package com.williamfiset.algorithms.other;
1112

12-
public class PowerSet {
13-
14-
// Use the fact that numbers represented in binary can be
15-
// used to generate all the subsets of an array
16-
static void powerSetUsingBinary(int[] set) {
13+
import java.util.*;
1714

18-
final int N = set.length;
19-
final int MAX_VAL = 1 << N;
15+
public class PowerSet {
2016

21-
for (int subset = 0; subset < MAX_VAL; subset++) {
22-
System.out.print("{ ");
23-
for (int i = 0; i < N; i++) {
24-
int mask = 1 << i;
25-
if ((subset & mask) == mask) System.out.print(set[i] + " ");
17+
/**
18+
* Generates the power set using binary representation. Each integer from 0 to 2^n - 1 represents
19+
* a subset, where bit i indicates whether element i is included.
20+
*/
21+
public static <T> List<List<T>> powerSetBinary(List<T> set) {
22+
int n = set.size();
23+
if (n > 30)
24+
throw new IllegalArgumentException("Set too large: n=" + n + " (max 30)");
25+
int total = 1 << n;
26+
List<List<T>> result = new ArrayList<>(total);
27+
28+
for (int mask = 0; mask < total; mask++) {
29+
List<T> subset = new ArrayList<>();
30+
for (int i = 0; i < n; i++) {
31+
if ((mask & (1 << i)) != 0)
32+
subset.add(set.get(i));
2633
}
27-
System.out.println("}");
34+
result.add(subset);
2835
}
36+
return result;
2937
}
3038

31-
// Recursively generate the powerset (set of all subsets) of an array by maintaining
32-
// a boolean array used to indicate which element have been selected
33-
static void powerSetRecursive(int at, int[] set, boolean[] used) {
34-
35-
if (at == set.length) {
36-
37-
// Print found subset!
38-
System.out.print("{ ");
39-
for (int i = 0; i < set.length; i++) if (used[i]) System.out.print(set[i] + " ");
40-
System.out.println("}");
41-
42-
} else {
43-
44-
// Include this element
45-
used[at] = true;
46-
powerSetRecursive(at + 1, set, used);
47-
48-
// Backtrack and don't include this element
49-
used[at] = false;
50-
powerSetRecursive(at + 1, set, used);
51-
}
39+
/**
40+
* Generates the power set using recursive backtracking. At each element, branches into including
41+
* or excluding it.
42+
*/
43+
public static <T> List<List<T>> powerSetRecursive(List<T> set) {
44+
List<List<T>> result = new ArrayList<>();
45+
recurse(0, set, new ArrayList<>(), result);
46+
return result;
5247
}
5348

54-
public static void main(String[] args) {
55-
56-
// Example usage:
57-
int[] set = {1, 2, 3};
58-
59-
powerSetUsingBinary(set);
60-
// prints:
61-
// { }
62-
// { 1 }
63-
// { 2 }
64-
// { 1 2 }
65-
// { 3 }
66-
// { 1 3 }
67-
// { 2 3 }
68-
// { 1 2 3 }
69-
70-
System.out.println();
71-
72-
powerSetRecursive(0, set, new boolean[set.length]);
73-
// prints:
74-
// { 1 2 3 }
75-
// { 1 2 }
76-
// { 1 3 }
77-
// { 1 }
78-
// { 2 3 }
79-
// { 2 }
80-
// { 3 }
81-
// { }
49+
private static <T> void recurse(int at, List<T> set, List<T> current, List<List<T>> result) {
50+
if (at == set.size()) {
51+
// Snapshot the current subset — must copy since 'current' is mutated during backtracking
52+
result.add(new ArrayList<>(current));
53+
return;
54+
}
55+
// Include set[at] and explore all subsets of the remaining elements
56+
current.add(set.get(at));
57+
recurse(at + 1, set, current, result);
8258

59+
// Backtrack: undo the inclusion of set[at], then explore without it
60+
current.remove(current.size() - 1);
61+
recurse(at + 1, set, current, result);
8362
}
8463
}

src/test/java/com/williamfiset/algorithms/other/BUILD

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,3 +46,14 @@ java_test(
4646
runtime_deps = JUNIT5_RUNTIME_DEPS,
4747
deps = TEST_DEPS,
4848
)
49+
50+
# bazel test //src/test/java/com/williamfiset/algorithms/other:PowerSetTest
51+
java_test(
52+
name = "PowerSetTest",
53+
srcs = ["PowerSetTest.java"],
54+
main_class = "org.junit.platform.console.ConsoleLauncher",
55+
use_testrunner = False,
56+
args = ["--select-class=com.williamfiset.algorithms.other.PowerSetTest"],
57+
runtime_deps = JUNIT5_RUNTIME_DEPS,
58+
deps = TEST_DEPS,
59+
)
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
package com.williamfiset.algorithms.other;
2+
3+
import static com.google.common.truth.Truth.assertThat;
4+
5+
import java.util.*;
6+
import java.util.stream.Collectors;
7+
import org.junit.jupiter.api.*;
8+
9+
public class PowerSetTest {
10+
11+
/** Converts a power set to a set of sorted string representations for easy comparison. */
12+
private static <T extends Comparable<T>> Set<String> normalize(List<List<T>> powerSet) {
13+
return powerSet.stream()
14+
.map(subset -> {
15+
List<T> sorted = new ArrayList<>(subset);
16+
Collections.sort(sorted);
17+
return sorted.toString();
18+
})
19+
.collect(Collectors.toSet());
20+
}
21+
22+
@Test
23+
public void emptySet_binary() {
24+
List<List<Integer>> result = PowerSet.powerSetBinary(List.of());
25+
assertThat(result).hasSize(1);
26+
assertThat(result.get(0)).isEmpty();
27+
}
28+
29+
@Test
30+
public void emptySet_recursive() {
31+
List<List<Integer>> result = PowerSet.powerSetRecursive(List.of());
32+
assertThat(result).hasSize(1);
33+
assertThat(result.get(0)).isEmpty();
34+
}
35+
36+
@Test
37+
public void singleElement_binary() {
38+
List<List<Integer>> result = PowerSet.powerSetBinary(List.of(42));
39+
assertThat(normalize(result)).containsExactly("[]", "[42]");
40+
}
41+
42+
@Test
43+
public void singleElement_recursive() {
44+
List<List<Integer>> result = PowerSet.powerSetRecursive(List.of(42));
45+
assertThat(normalize(result)).containsExactly("[]", "[42]");
46+
}
47+
48+
@Test
49+
public void threeElements_binary() {
50+
List<List<Integer>> result = PowerSet.powerSetBinary(List.of(1, 2, 3));
51+
assertThat(result).hasSize(8);
52+
assertThat(normalize(result))
53+
.containsExactly("[]", "[1]", "[2]", "[3]", "[1, 2]", "[1, 3]", "[2, 3]", "[1, 2, 3]");
54+
}
55+
56+
@Test
57+
public void threeElements_recursive() {
58+
List<List<Integer>> result = PowerSet.powerSetRecursive(List.of(1, 2, 3));
59+
assertThat(result).hasSize(8);
60+
assertThat(normalize(result))
61+
.containsExactly("[]", "[1]", "[2]", "[3]", "[1, 2]", "[1, 3]", "[2, 3]", "[1, 2, 3]");
62+
}
63+
64+
@Test
65+
public void bothMethodsProduceSameSubsets() {
66+
List<Integer> set = List.of(5, 10, 15, 20);
67+
Set<String> binary = normalize(PowerSet.powerSetBinary(set));
68+
Set<String> recursive = normalize(PowerSet.powerSetRecursive(set));
69+
assertThat(binary).isEqualTo(recursive);
70+
}
71+
72+
@Test
73+
public void correctSize() {
74+
for (int n = 0; n <= 5; n++) {
75+
List<Integer> set = new ArrayList<>();
76+
for (int i = 0; i < n; i++)
77+
set.add(i);
78+
assertThat(PowerSet.powerSetBinary(set)).hasSize(1 << n);
79+
assertThat(PowerSet.powerSetRecursive(set)).hasSize(1 << n);
80+
}
81+
}
82+
83+
@Test
84+
public void worksWithStrings() {
85+
List<List<String>> result = PowerSet.powerSetBinary(List.of("a", "b"));
86+
assertThat(normalize(result)).containsExactly("[]", "[a]", "[b]", "[a, b]");
87+
}
88+
}

0 commit comments

Comments
 (0)