Start learning free

LESSON 288 OF 410 · BACKTRACKING

Generate Parentheses in Java: Backtracking with Pruning

Practice O(2^n) Read the full explanation

Given n pairs of parentheses, write a function to generate all combinations of well-formed parentheses.

A parentheses string is well-formed if and only if:

  • It is the empty string, or

  • It can be written as AB (A concatenated with B), where A and B are well-formed, or

  • It can be written as (A), where A is well-formed.


Example 1:

Input: n = 3
Output: ["((()))","(()())","(())()","()(())","()()()"]

Example 2:

Input: n = 1
Output: ["()"]

Note: 1 <= n <= 8


STEP 1 OF 10
    Ctrl + Enter to check

    JAVA · BACKTRACKING

    Generate Parentheses in Java, explained

    Problem overview

    Given n pairs of parentheses, generateParentheses(int n) returns every string of 2 * n characters in which the parentheses are well-formed: each ( is closed by a later ), and no ) appears without an open ( to match. The strings may be in any order in principle, but the lesson's examples list them with ( tried before ).

    • For n = 1 the only answer is ["()"].
    • For n = 2 the answers are (()) and ()().
    • For n = 3 there are five: ((())), (()()), (())(), ()(()) and ()()().

    The input is small, 1 <= n <= 8, because the number of answers grows quickly: 1, 2, 5, 14, 42, 132, 429 and 1430 for n = 1 through 8. These are the Catalan numbers.

    Approach: build the string one character at a time

    A string of length 2n has 2^(2n) possible combinations of the two characters, and most are invalid. Generating all of them and filtering would waste almost all the work. Backtracking builds each string character by character and abandons a partial string as soon as it can no longer be completed into a valid one.

    Two counters track the partial string: open, the number of ( so far, and closed, the number of ). A prefix is only useful if it passes two tests:

    • Never more closes than opens. If closed exceeds open at any point, some ) has no partner, and adding characters can never fix that.
    • Not too many opens for the space left. Each extra open parenthesis needs a close later. If open - closed is larger than the number of positions still to fill, there is no room to close them all.

    Because both tests are checked on every prefix, any string that reaches full length has equal numbers of each character and never dips below zero. Together that is the definition of well-formed, so no extra validation is needed at the end.

    Step by step

    1. Keep the results in a field. A field List<String> solutions lets the recursive helper add answers without passing the list through every call.
    2. Reset it on each call. At the start of generateParentheses, assign a new ArrayList so results from an earlier call do not leak into this one.
    3. Start the recursion. Call generate("", 2 * n, 0, 0): an empty string, the target length, and zero opens and closes. Inside the helper the parameter named n is the full length, twice the number of pairs.
    4. Return solutions once the recursion has filled it.
    5. Write the helper void generate(String current, int n, int open, int closed).
    6. First prune: if open < closed, return.
    7. Second prune: if open exceeds closed by more than the positions left, return. The positions left are n - current.length().
    8. Base case: if current.length() == n, the string is complete. Add it to solutions and return.
    9. Branch twice: recurse with current + '(' and open + 1, then with current + ')' and closed + 1.

    The solution in Java

    class Solution {
        void generate(String current, int n, int open, int closed) {
            if (open < closed) {
                return;
            }
            if (open > closed + (n - current.length() + 1)) {
                return;
            }
            if (current.length() == n) {
                solutions.add(current);
                return;
            }
            generate(current + '(', n, open + 1, closed);
            generate(current + ')', n, open, closed + 1);
        }
        List<String> solutions;
        public List<String> generateParentheses(int n) {
            solutions = new ArrayList<>();
            generate("", 2 * n, 0, 0);
            return solutions;
        }
    }
    

    The code uses List and ArrayList, which live in java.util, so a standalone program needs import java.util.*; at the top.

    Tracing n = 2

    The helper receives 4 as its length. From the empty string, the call for ")" returns at once because open < closed, and the call for "(" continues. From "(" the helper tries "((" and "()". The string "(((", one step below "((", is pruned: it has three opens and no closes, a surplus of 3 with only one position left. "(()" survives and can only grow into "(())", because "(()(" is pruned in turn. From "()" the only survivor is "()()", since "())" has more closes than opens. The two results are (()) and ()(). Because ( is always tried before ), the answers come out in that order.

    Java details

    • Strings are immutable. current + '(' creates a new string and leaves current unchanged. That is why no "undo" step is needed after the first recursive call: the second call still sees the original prefix. With a StringBuilder you would have to remove the last character after each call.
    • Adding a char to a String. current + '(' appends the character. This is fine because current is a String; with two char values, '(' + ')' would add their numeric codes instead.
    • The extra + 1 in the second prune. It never changes the outcome. The surplus open - closed and the number of positions left always have the same parity, so the surplus can't be exactly one more than the positions left.
    • The field is shared. Because solutions belongs to the object, calling generateParentheses from two threads on the same Solution would mix their results. Resetting it at the start of each call handles the sequential case.

    Complexity

    The number of results is the Catalan number C(n), roughly 4^n divided by n^1.5 for large n. The pruning is exact: every prefix that survives can still be completed, so the recursion never explores a dead end deeper than one call. The work is therefore proportional to the number of surviving prefixes, each of which also costs up to O(n) to build, because a string concatenation copies the characters. The total time grows like C(n) times a small polynomial in n, and the output alone takes 2n * C(n) characters. The recursion goes at most 2n levels deep, so the space is O(n) not counting the output.

    Edge cases and common mistakes

    • Counting positions with the wrong n. Mixing up the number of pairs and the string length is the classic error. In the helper, n is the length, so the base case is current.length() == n, not 2 * n.
    • Forgetting to reset the field. Without solutions = new ArrayList<>(), a second call to the method returns the first call's results as well.
    • Checking validity only at the end. That also works but visits every one of the 2^(2n) strings; pruning is what makes the search fast.
    • Printing the result. To print each combination on its own line, loop over the list: for (String s : generateParentheses(3)) System.out.println(s);.
    Keep going with JavaAbout 340 step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free