Tips implementing permutation algorithm in Java
Data Structures & Algorithms practice on Codemia
Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.
Introduction
Permutation generation is a classic interview topic, but the real implementation details matter once you use it in production code or serious algorithm exercises. A good Java solution needs a correct backtracking invariant first, then sensible choices about duplicate handling, memory usage, and stopping conditions.
Start with a Correct Backtracking Baseline
The standard in-place swap algorithm is a strong starting point because it is compact and avoids creating a new array at every recursion level. The key invariant is simple: positions before start are already fixed, and positions from start onward are still available.
The second swap call is not optional. It restores the array so the next branch starts from a clean state. If you forget that step, later permutations are built on corrupted data and the output quickly becomes wrong.
Handle Duplicate Input Explicitly
If the input can contain repeated values, the naive swap algorithm emits duplicate permutations. The usual fix is to track which values have already been placed at the current recursion depth.
This depth-local set is much cheaper than generating every permutation and removing duplicates later.
Decide Whether to Collect or Stream Results
For small inputs, returning a List<List<Integer>> is fine. For larger search spaces, collecting everything in memory is usually the wrong design because permutation counts grow factorially. Even 10! is already 3,628,800 permutations.
If you only need to inspect or validate permutations as they are produced, stream them to a visitor instead of storing them all:
Cloning before calling the consumer is important here because the source array is mutated after each callback.
Prune Early When the Problem Allows It
In real search problems, you often do not need every permutation. You may only care about permutations that satisfy a prefix condition, such as a partial sum limit or a forbidden adjacency rule. In those cases, pruning early usually gives more benefit than low-level micro-optimizations.
For example, if no valid solution can start with duplicate neighboring values, reject that branch immediately instead of waiting until the full permutation is built.
This is also where API design matters. A generator that can stop early is more useful than one that always materializes the full result set.
Consider Lexicographic Generation When Order Matters
Sometimes you need permutations in deterministic sorted order, or you want to resume from the current permutation. In those cases, the iterative nextPermutation algorithm can be a better fit than recursive backtracking.
This method only works as intended when you start from sorted input, but it is excellent when ordering guarantees matter.
Common Pitfalls
The classic bug is forgetting to swap values back after the recursive call. Another common issue is ignoring duplicate input and then wondering why the output contains repeated permutations. Developers also underestimate factorial growth and return giant lists from utility methods that should have been streaming or short-circuiting. Finally, callback-based designs sometimes forget to copy the current permutation before handing it to callers, which means the caller sees a mutated array later.
Summary
- Start with a simple in-place backtracking algorithm and get the state restoration right.
- Guard against duplicate values when the input is not guaranteed to be unique.
- Stream permutations instead of collecting them all when the search space is large.
- Add pruning logic when the target problem has prefix constraints.
- Use lexicographic generation if you need stable ordering or resume behavior.
Related reading
- To make a distance matrix or to repeatedly calculate distance
- To print the boundary of Binary Tree
- Toilet Seat Algorithm
- Topological sort based on a comparator rather than a graph
- TMP how to generalize a Cartesian Product of Vectors?
- Topological sort of cyclic graph with minimum number of violated edges
- .toArraynew MyClass0 or .toArraynew MyClassmyList.size?
- Tomcat How to find out running Tomcat version?

DSA Fundamentals
Master algorithmic patterns and data structures through hands-on LeetCode-style problems - from arrays and hashing to dynamic programming and advanced graphs.
View the courseTrack what you have practised
A free account saves your progress, solutions and study plan across every problem on Codemia.
Data Structures & Algorithms practice on Codemia
Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.