Construct all possible BSTs for keys 1 to N
Last Updated :
29 Sep, 2024
Given an integer n, the task is to generate all possible Binary Search Trees (BSTs) that can be constructed using keys from 1 to n and print their preorder traversal.
Examples:
Input: n = 2
Output:
1 2
2 1
Input: n = 3
Output:
1 2 3
1 3 2
2 1 3
3 1 2
3 2 1
How many structurally unique BSTs can be formed with keys from 1 to n?
- The key observation is that for any i-th number chosen as the root, all numbers from 1 to i-1 must be in the left subtree (since they are smaller than the root), and all numbers from i+1 to n must be in the right subtree (since they are larger than the root).
- If the numbers from 1 to i-1 can form x different trees and the numbers from i+1 to n can form y different trees, then for the i-th number as root, we can have x * y total unique BSTs.
- Since we can choose any number from 1 to n as the root, we iterate over each possible root (from 1 to n) and calculate the total number of unique BSTs by summing up the product of the number of left and right subtrees for each root.
- Upon observation, the number of structurally unique BSTs for n keys is equivalent to the n-th Catalan number, which can be computed using known formulas or dynamic programming.
Approach:
The idea is to construct all possible BSTs by considering each number from 1 to n as the root and recursively constructing the left and right subtrees. For each root i, all numbers from 1 to i-1 will form the left subtree, and numbers from i+1 to n will form the right subtree. We recursively generate all possible BSTs for the left and right subtrees and then combine each left subtree with each right subtree for the current root.
Follow the steps below to solve the problem:
- Initialize a array of BSTs as empty.
- For every number i from 1 to n, do the following:
- Create a new node with key i, let this node be 'node'.
- Recursively construct a list of all possible left subtrees from numbers 1 to i-1.
- Recursively construct a list of all possible right subtrees from numbers i+1 to n.
- Iterate over all possible left subtrees:
- For each current left subtree, iterate over all possible right subtrees.
- Attach the current left and right subtrees to 'node'.
- Add the node (with attached subtrees) to the list of BSTs.
Below is the implementation of the above approach:
C++
// C++ program to find all binary trees
// from 1 to n
#include <bits/stdc++.h>
using namespace std;
class Node {
public:
int data;
Node* left;
Node* right;
Node(int x) {
data = x;
left = right = nullptr;
}
};
// Function to construct all possible binary trees
vector<Node*> getTrees(int start, int end) {
vector<Node*> trees;
// Base case: If start index is greater than end,
// return empty tree (nullptr)
if (start > end) {
trees.push_back(nullptr);
return trees;
}
// Iterate through all values in the array
// and construct left and right subtrees
for (int i = start; i <= end; ++i) {
// Generate all left subtrees
vector<Node*> leftTrees = getTrees(start, i - 1);
// Generate all right subtrees
vector<Node*> rightTrees = getTrees(i + 1, end);
// Combine each left and right subtree with
// the current root
for (Node* left : leftTrees) {
for (Node* right : rightTrees) {
// Make i as root
Node* root = new Node(i);
root->left = left;
root->right = right;
// Add the constructed tree to the list of trees
trees.push_back(root);
}
}
}
return trees;
}
void preorder(Node* root) {
if (root != nullptr) {
cout << root->data << " ";
preorder(root->left);
preorder(root->right);
}
}
int main() {
vector<Node*> trees = getTrees(1, 3);
for (int i = 0; i < trees.size(); ++i) {
preorder(trees[i]);
cout << "\n";
}
return 0;
}
Java
// Java program to find all binary
// trees from 1 to n
import java.util.ArrayList;
import java.util.List;
class Node {
int data;
Node left, right;
Node(int data) {
this.data = data;
left = right = null;
}
}
class GfG {
// Function to construct all possible binary trees
static List<Node> getTrees(int start, int end) {
List<Node> trees = new ArrayList<>();
// Base case: If start index is greater than end,
// return empty tree (null)
if (start > end) {
trees.add(null);
return trees;
}
// Iterate through all values in the array and
// construct left and right subtrees
for (int i = start; i <= end; i++) {
// Generate all left subtrees
List<Node> leftTrees = getTrees(start, i - 1);
// Generate all right subtrees
List<Node> rightTrees = getTrees(i + 1, end);
// Combine each left and right
// subtree with the current root
for (Node left : leftTrees) {
for (Node right : rightTrees) {
// Make i as root
Node root = new Node(i);
root.left = left;
root.right = right;
// Add the constructed tree to the
// list of trees
trees.add(root);
}
}
}
return trees;
}
static void preorder(Node root) {
if (root != null) {
System.out.print(root.data + " ");
preorder(root.left);
preorder(root.right);
}
}
public static void main(String[] args) {
List<Node> trees = getTrees(1, 3);
for (Node tree : trees) {
preorder(tree);
System.out.println();
}
}
}
Python
# Python program to find all binary
# trees from 1 to n
class Node:
def __init__(self, data):
self.data = data
self.left = None
self.right = None
# Function to construct all possible
# binary trees
def get_trees(start, end):
trees = []
# Base case: If start index is greater than end,
# return empty tree (None)
if start > end:
trees.append(None)
return trees
# Iterate through all values in the array and
# construct left and right subtrees
for i in range(start, end + 1):
# Generate all left subtrees
left_trees = get_trees(start, i - 1)
# Generate all right subtrees
right_trees = get_trees(i + 1, end)
# Combine each left and right subtree
# with the current root
for left in left_trees:
for right in right_trees:
# Make i as root
root = Node(i)
root.left = left
root.right = right
# Add the constructed tree to
# the list of trees
trees.append(root)
return trees
def preorder(root):
if root is not None:
print(root.data, end=" ")
preorder(root.left)
preorder(root.right)
if __name__ == "__main__":
trees = get_trees(1, 3)
for tree in trees:
preorder(tree)
print()
C#
// C# program to find all binary trees
// from 1 to n
using System;
using System.Collections.Generic;
class Node {
public int data;
public Node left, right;
public Node(int data) {
this.data = data;
this.left = this.right = null;
}
}
class GfG {
// Function to construct all possible binary trees
static List<Node> GetTrees(int start, int end) {
List<Node> trees = new List<Node>();
// Base case: If start index is greater than end,
// return empty tree (null)
if (start > end) {
trees.Add(null);
return trees;
}
// Iterate through all values in the array and
// construct left and right subtrees
for (int i = start; i <= end; i++) {
// Generate all left subtrees
List<Node> leftTrees = GetTrees(start, i - 1);
// Generate all right subtrees
List<Node> rightTrees = GetTrees(i + 1, end);
// Combine each left and right subtree
// with the current root
foreach (Node left in leftTrees) {
foreach (Node right in rightTrees) {
// Make i as root
Node root = new Node(i);
root.left = left;
root.right = right;
// Add the constructed tree to
// the list of trees
trees.Add(root);
}
}
}
return trees;
}
static void Preorder(Node root) {
if (root != null) {
Console.Write(root.data + " ");
Preorder(root.left);
Preorder(root.right);
}
}
static void Main(string[] args) {
List<Node> trees = GetTrees(1, 3);
foreach (Node tree in trees) {
Preorder(tree);
Console.WriteLine();
}
}
}
JavaScript
// JavaScript program to find all binary
// trees from 1 to n
class Node {
constructor(data) {
this.data = data;
this.left = null;
this.right = null;
}
}
// Function to construct all possible binary trees
function getTrees(start, end) {
const trees = [];
// Base case: If start index is greater than end,
// return empty tree (null)
if (start > end) {
trees.push(null);
return trees;
}
// Iterate through all values in the array
// and construct left and right subtrees
for (let i = start; i <= end; i++) {
// Generate all left subtrees
const leftTrees = getTrees(start, i - 1);
// Generate all right subtrees
const rightTrees = getTrees(i + 1, end);
// Combine each left and right subtree
// with the current root
leftTrees.forEach(left => {
rightTrees.forEach(right => {
// Make i as root
const root = new Node(i);
root.left = left;
root.right = right;
// Add the constructed tree to
// the list of trees
trees.push(root);
});
});
}
return trees;
}
function preorder(root) {
if (root !== null) {
process.stdout.write(root.data + " ");
preorder(root.left);
preorder(root.right);
}
}
const trees = getTrees(1, 3);
trees.forEach(tree => {
preorder(tree);
console.log();
});
Output1 2 3
1 3 2
2 1 3
3 1 2
3 2 1
Time Complexity: O(Cn​ * n), where Cn​ is the Catalan number
Auxiliary Space: O(Cn​ * n), for storing all possible trees and the space used by the recursion stack.
Related Articles
Similar Reads
Binary Search Tree A Binary Search Tree (or BST) is a data structure used in computer science for organizing and storing data in a sorted manner. Each node in a Binary Search Tree has at most two children, a left child and a right child, with the left child containing values less than the parent node and the right chi
3 min read
Introduction to Binary Search Tree Binary Search Tree is a data structure used in computer science for organizing and storing data in a sorted manner. Binary search tree follows all properties of binary tree and for every nodes, its left subtree contains values less than the node and the right subtree contains values greater than the
3 min read
Applications of BST Binary Search Tree (BST) is a data structure that is commonly used to implement efficient searching, insertion, and deletion operations along with maintaining sorted sequence of data. Please remember the following properties of BSTs before moving forward.The left subtree of a node contains only node
3 min read
Applications, Advantages and Disadvantages of Binary Search Tree A Binary Search Tree (BST) is a data structure used to storing data in a sorted manner. Each node in a Binary Search Tree has at most two children, a left child and a right child, with the left child containing values less than the parent node and the right child containing values greater than the p
2 min read
Insertion in Binary Search Tree (BST) Given a BST, the task is to insert a new node in this BST.Example: How to Insert a value in a Binary Search Tree:A new key is always inserted at the leaf by maintaining the property of the binary search tree. We start searching for a key from the root until we hit a leaf node. Once a leaf node is fo
15 min read
Searching in Binary Search Tree (BST) Given a BST, the task is to search a node in this BST. For searching a value in BST, consider it as a sorted array. Now we can easily perform search operation in BST using Binary Search Algorithm. Input: Root of the below BST Output: TrueExplanation: 8 is present in the BST as right child of rootInp
7 min read
Deletion in Binary Search Tree (BST) Given a BST, the task is to delete a node in this BST, which can be broken down into 3 scenarios:Case 1. Delete a Leaf Node in BSTDeletion in BSTCase 2. Delete a Node with Single Child in BSTDeleting a single child node is also simple in BST. Copy the child to the node and delete the node. Deletion
10 min read
Binary Search Tree (BST) Traversals â Inorder, Preorder, Post Order Given a Binary Search Tree, The task is to print the elements in inorder, preorder, and postorder traversal of the Binary Search Tree. Input: A Binary Search TreeOutput: Inorder Traversal: 10 20 30 100 150 200 300Preorder Traversal: 100 20 10 30 200 150 300Postorder Traversal: 10 30 20 150 300 200 1
10 min read
Balance a Binary Search Tree Given a BST (Binary Search Tree) that may be unbalanced, the task is to convert it into a balanced BST that has the minimum possible height.Examples: Input: Output: Explanation: The above unbalanced BST is converted to balanced with the minimum possible height.Input: Output: Explanation: The above u
10 min read
Self-Balancing Binary Search Trees Self-Balancing Binary Search Trees are height-balanced binary search trees that automatically keep the height as small as possible when insertion and deletion operations are performed on the tree. The height is typically maintained in order of logN so that all operations take O(logN) time on average
4 min read