Given the root of a binary search tree, return a balanced binary search tree with the same node values. If there is more than one answer, return any of them.
A binary search tree is balanced if the depth of the two subtrees of every node never differs by more than 1.
Example 1:
Input: root = [1,null,2,null,3,null,4,null,null] Output: [2,1,3,null,null,null,4] Explanation: This is not the only correct answer, [3,1,4,null,2] is also correct.
Example 2:
Input: root = [2,1,3] Output: [2,1,3]
Constraints:
[1, 104].1 <= Node.val <= 105When you get asked this question in a real-life environment, it will often be ambiguous (especially at FAANG). Make sure to ask these questions in that case:
The brute force method for balancing a binary search tree involves exploring every possible tree structure. We essentially try out every arrangement of the tree's elements. We then check if each arrangement is both a valid binary search tree and if it's balanced.
Here's how the algorithm would work step-by-step:
class Node:
def __init__(self, value):
self.value = value
self.left = None
self.right = None
def generate_trees(values):
if not values:
return [None]
trees = []
for i in range(len(values)):
root_value = values[i]
left_values = values[:i]
right_values = values[i+1:]
left_subtrees = generate_trees(left_values)
right_subtrees = generate_trees(right_values)
for left_subtree in left_subtrees:
for right_subtree in right_subtrees:
root = Node(root_value)
root.left = left_subtree
root.right = right_subtree
trees.append(root)
return trees
def is_bst(root):
def is_bst_helper(node, min_value, max_value):
if not node:
return True
if (min_value is not None and node.value <= min_value) or \
(max_value is not None and node.value >= max_value):
return False
return (is_bst_helper(node.left, min_value, node.value) and \
is_bst_helper(node.right, node.value, max_value))
return is_bst_helper(root, None, None)
def get_height(root):
if not root:
return 0
return 1 + max(get_height(root.left), get_height(root.right))
def is_balanced(root):
if not root:
return True
left_height = get_height(root.left)
right_height = get_height(root.right)
# Check if heights of left and right subtrees differ by more than 1
if abs(left_height - right_height) > 1:
return False
return is_balanced(root.left) and is_balanced(root.right)
def balance_bst_brute_force(root):
if not root:
return None
# Extract all node values from the tree
node_values = []
def inorder_traversal(node):
if node:
inorder_traversal(node.left)
node_values.append(node.value)
inorder_traversal(node.right)
inorder_traversal(root)
# Generate all possible BSTs from the values
possible_trees = generate_trees(node_values)
best_tree = None
best_balance_factor = float('inf')
for tree in possible_trees:
# Check if the tree is a valid BST
if is_bst(tree):
# Check if the tree is balanced
if is_balanced(tree):
left_height = get_height(tree.left)
right_height = get_height(tree.right)
balance_factor = abs(left_height - right_height)
# Find a more balanced BST
if balance_factor < best_balance_factor:
best_balance_factor = balance_factor
best_tree = tree
# If no valid BST found, return original root
if best_tree is None:
return root
return best_treeThe best way to balance a binary search tree is to first extract all the elements in a sorted order. Then, using this sorted list, reconstruct a perfectly balanced tree, ensuring optimal height and distribution of elements.
Here's how the algorithm would work step-by-step:
class TreeNode:
def __init__(self, value=0, left=None, right=None):
self.value = value
self.left = left
self.right = right
def balance_bst(root):
def inorder_traversal(node, sorted_list):
if not node:
return
inorder_traversal(node.left, sorted_list)
sorted_list.append(node.value)
inorder_traversal(node.right, sorted_list)
sorted_nodes = []
inorder_traversal(root, sorted_nodes)
# Build balanced BST from sorted list.
def build_balanced_tree(sorted_list):
if not sorted_list:
return None
middle_index = len(sorted_list) // 2
# Root is middle element to ensure balance.
root_node = TreeNode(sorted_list[middle_index])
root_node.left = build_balanced_tree(sorted_list[:middle_index])
# Right subtree contains elements greater than root
root_node.right = build_balanced_tree(sorted_list[middle_index + 1:])
return root_node
return build_balanced_tree(sorted_nodes)| Case | How to Handle |
|---|---|
| Null or Empty Tree | Return null immediately as there's nothing to balance. |
| Single Node Tree | Return the original node as it's already balanced. |
| Tree with only left children (skewed left) | The balancing algorithm should correctly re-arrange the nodes to reduce height. |
| Tree with only right children (skewed right) | The balancing algorithm must rearrange nodes to distribute them evenly and balance the tree. |
| Tree with duplicate values | Ensure balancing algorithm (e.g., AVL/Red-Black) handles duplicate keys correctly without violating BST properties, potentially by adjusting insertion logic. |
| Very large tree (approaching memory limits) | Consider iterative inorder traversal to avoid stack overflow, and verify algorithm scales efficiently in terms of both time and memory. |
| Tree with negative and positive values | The balancing algorithm should handle both positive and negative values correctly maintaining the BST property during balancing. |
| Integer overflow during value calculations (if applicable for balancing criteria) | Use appropriate data types or checks to prevent integer overflows when calculating heights or differences. |