· By John Kavanagh

Generate Parentheses in TypeScript: A Clean Backtracking Walkthrough

Abstract image used to represent Generate Parentheses in TypeScript
Image by Priscilla Du Preez.

The 'Generate Parentheses' problem is a classic computer science challenge you might encounter during a coding interview or otherwise on the LeetCodetype coding challenge websites. This problem tests not just your algorithmic skills but also your understanding of recursion and backtracking.

The objective is to generate all combinations of wellformed parentheses for a given number n. A wellformed parentheses string is defined as one that opens and closes correctly. Understanding how to tackle this problem efficiently is useful for web developers who often face tasks that involve string manipulation and recursion, and it also links nicely back to a previous article I wrote about validating matched parentheses.


Problem Statement

Given n pairs of parentheses, write a function to generate all combinations of wellformed parentheses.

Some Examples:

Input: n = 1
Output: ["()"]
With only one pair of parentheses, the only possible wellformed combination is ().

Input: n = 2
Output: ["(())", "()()"]
With two pairs of parentheses, you can either nest one pair within the other (()) or place them sidebyside ()().

Input: n = 3
Output: ["((()))", "(()())", "(())()", "()(())", "()()()"]
With three pairs, the combinations start to grow in number. You can nest them in various ways or combine nested and sidebyside arrangements.

Input: n = 0
Output: [""]
At the other end of the spectrum, with zero pairs, the only valid combination is an empty string.


Solution Using Backtracking

Backtracking is a general algorithmic technique that incrementally explores all possible options to solve a problem and then reverts (backtracks) when it determines that a particular pathway is not leading to a solution. Essentially, backtracking involves making a series of choices, each leading to a new set of choices, until a solution is found or all options have been explored.

This type of technique is often used for solving constraint satisfaction problems, where you have to find a set of variables that satisfy specific constraints (for example, the NQueens problem or Sudoku).

In the case of generating all wellformed parentheses, we can use backtracking to systematically build strings and then backtrack when the current string doesn't lead to a wellformed parentheses pair (). This allows us to explore all possible combinations of parentheses whilst minimising unnecessary calculations.

In Code

Using TypeScript (or JavaScript), it is relatively straightforward to put together a solution that will run on the front end:

const generateParenthesis = (n: number): string[] => {
  const res: string[] = [];

  const backtrack = (s: string, left: number, right: number) => {
    if (s.length === 2 * n) {
      res.push(s);
      return;
    }

    if (left < n) backtrack(`${s}(`, left + 1, right);
    if (right < left) backtrack(`${s})`, left, right + 1);
  };

  backtrack('', 0, 0);

  return res;
};

How It Works

The idea here is to build each valid combination of parentheses by making a sequence of choices.

  1. Base Case

    : If the constructed string has reached its maximum possible length (which is 2 * n since each pair of parentheses contributes two characters), then we add it to our list of solutions.
  2. Recursive Case

    : At each step, we have two choices: add an opening parenthesis ( or a closing parenthesis ). However, these choices are constrained:
    • We can add an opening parenthesis if we haven't used all opening parentheses yet (left < n).
    • We can add a closing parenthesis if the number of opening parentheses used so far is greater than the number of closing parentheses (right < left).

The code achieves this using two functions:

  • generateParenthesis(n) creates the empty result array res, defines the nested backtrack function, and starts it with backtrack('', 0, 0). That call supplies the initial empty string directly. The function returns res after the search finishes.
  • backtrack(s, left, right) receives the string built so far and the counts of opening and closing parentheses used. It closes over n and res from generateParenthesis, rather than receiving them as arguments.
    • It starts by evaluating the base case: if the length of the current string is equal to twice the number of pairs (2 * n), this indicates that a valid sequence of parentheses has been constructed. This valid sequence is then added to the res array.
    • It then evaluates whether it can add either an opening or closing parenthesis whilst maintaining a valid sequence. If adding one of these options keeps the sequence valid, the function chooses that option and then recursively calls itself to continue building the sequence.

Wrapping Up

The 'Generate Parentheses' problem is a standard exercise for practising recursion and backtracking. This problem's complexity lies in ensuring that all generated parentheses combinations are valid. The TypeScript solution I've written and shared here does just that, hopefully in an efficient manner.


Need a senior engineer involved?

I can work directly in the codebase, review the architecture, or support the team through delivery when the work needs more than extra hands.