Shuffle an Array
MedPrompt
Approaches
3 approaches with complexity analysis and trade-offs.
This approach involves generating every possible permutation of the input array beforehand. When a shuffle is requested, one of these pre-computed permutations is chosen at random.
Algorithm
-
- In the constructor, store a copy of the original array.
-
- On the first call to
shuffle(), generate alln!permutations of the array using a recursive backtracking algorithm and store them in a list.
- On the first call to
-
- For subsequent
shuffle()calls, pick a random index from0ton!-1and return the permutation at that index.
- For subsequent
-
- The
reset()method returns the stored original array.
- The
Walkthrough
In this method, we first generate all unique permutations of the original array. This is a one-time operation, typically done during the initialization of the object or on the first call to shuffle(). A standard backtracking algorithm can be used to generate these permutations.
Once we have a list containing all n! permutations, the shuffle() method becomes very simple: it just needs to pick a random index from this list and return the corresponding permutation. The reset() method simply returns the original, unmodified array.
While conceptually straightforward, this approach is highly impractical due to its factorial time and space complexity. It's only feasible for very small arrays.
import java.util.ArrayList;import java.util.List;import java.util.Random; class Solution { private int[] original; private List<int[]> allPermutations; private Random rand; public Solution(int[] nums) { this.original = nums.clone(); this.rand = new Random(); this.allPermutations = new ArrayList<>(); // Pre-computation is too slow to do here, so we do it on the first shuffle call. } // Helper to generate all permutations using backtracking private void generatePermutations(List<Integer> current, boolean[] used, int[] nums) { if (current.size() == nums.length) { allPermutations.add(current.stream().mapToInt(i -> i).toArray()); return; } for (int i = 0; i < nums.length; i++) { if (!used[i]) { used[i] = true; current.add(nums[i]); generatePermutations(current, used, nums); current.remove(current.size() - 1); used[i] = false; } } } public int[] reset() { return original; } public int[] shuffle() { if (allPermutations.isEmpty()) { // One-time expensive setup generatePermutations(new ArrayList<>(), new boolean[original.length], original); } int randomIndex = rand.nextInt(allPermutations.size()); return allPermutations.get(randomIndex); }}Complexity
Time
O(n * n!) for the one-time pre-computation to generate all permutations. Each subsequent call to `shuffle()` is O(1) (or O(n) to copy the array). The `reset()` is O(1) or O(n). The dominant factor is the pre-computation.
Space
O(n * n!) to store all `n!` permutations, each of length `n`. This is prohibitively large for most inputs.
Trade-offs
Pros
Guarantees a uniform distribution if the random index is chosen uniformly.
Once pre-computation is done, subsequent shuffles are very fast.
Cons
Extremely high time complexity for pre-computation.
Extremely high space complexity.
Impractical for arrays with more than a few elements (e.g., n > 10).
Solutions
Solution
class Solution {private int[] nums;private int[] original;private Random rand;public Solution(int[] nums) { this.nums = nums; this.original = Arrays.copyOf(nums, nums.length); this.rand = new Random(); }public int[] reset() { nums = Arrays.copyOf(original, original.length); return nums; }public int[] shuffle() { for (int i = 0; i < nums.length; ++i) { swap(i, i + rand.nextInt(nums.length - i)); } return nums; }private void swap(int i, int j) { int t = nums[i]; nums[i] = nums[j]; nums[j] = t; }} /** * Your Solution object will be instantiated and called as such: * Solution obj = new Solution(nums); * int[] param_1 = obj.reset(); * int[] param_2 = obj.shuffle(); */Video walkthrough
Newsletter
One sharp idea, every week
System design and interview prep — short enough to finish.
No spam. Unsubscribe anytime.
Practice
Same difficulty — related problems to reinforce the pattern.