Python 3.11+
The examples use standard Python containers and syntax available in the supported browser runtime; Binary Search Tree itself is taught as an interview abstraction.
Verify in Python docs(opens in a new tab)Binary tree maintaining an ordering invariant for search, insertion, and range traversal. This guide turns that contract into deliberate Python operations, interview invariants, and a runnable practice loop.
Recognize it when
Consider Binary Search Tree when the prompt's constraints and required operations match this shape: Binary tree maintaining an ordering invariant for search, insertion, and range traversal.

Sign in to save your code and progress across devices. The lesson and problem statement remain public.
A binary search tree adds order to binary-tree shape. Under a strict no-duplicates policy, every value in a node’s left subtree is smaller and every value in its right subtree is larger. This is a global ancestor constraint, not merely a comparison with each immediate child.
Each descent narrows an allowed interval. Going left replaces the upper bound with the current value; going right replaces the lower bound. A descendant must satisfy every bound inherited from all ancestors. Inorder traversal visits a valid BST in sorted order, but sorted output alone does not identify the original shape.
Search and insertion compare once per visited level, discarding the impossible subtree. Their cost is O(h), where h is tree height: O(log n) when balanced and O(n) for a chain created by sorted insertions. Python does not provide a built-in mutable BST; the bisect module(opens in a new tab) serves sorted lists and has different insertion costs.
def bst_operations(values, queries):
root = None
for value in values:
if root is None:
root = [value, None, None]
continue
node = root
while True:
if value == node[0]:
break
side = 1 if value < node[0] else 2
if node[side] is None:
node[side] = [value, None, None]
break
node = node[side]
ordered = []
def visit(node):
if node is not None:
visit(node[1])
ordered.append(node[0])
visit(node[2])
visit(root)
found = []
for query in queries:
node = root
while node is not None and node[0] != query:
node = node[1] if query < node[0] else node[2]
found.append(node is not None)
return [ordered, found]Implement bst_operations(values, queries). Insert unique values, then return inorder values and directed-search results.
Loading interactive editor.If it does not appear, the reference and starter code remain readable.Reload editor
Checking left < node < right for immediate children misses a value that violates a higher
ancestor. Pass lower and upper bounds through recursion. Decide duplicate policy explicitly; the
strict contract uses open bounds, so equality is invalid anywhere.
def is_valid_bst(level_order):
def valid(index, lower, upper):
if index >= len(level_order) or level_order[index] is None:
return True
value = level_order[index]
if not lower < value < upper:
return False
return valid(2 * index + 1, lower, value) and valid(2 * index + 2, value, upper)
return valid(0, float('-inf'), float('inf'))Implement is_valid_bst(level_order) for a complete-index array. Require strict ordering against all ancestor lower and upper bounds.
Loading interactive editor.If it does not appear, the reference and starter code remain readable.Reload editor
Use a BST when the input is already a tree or dynamic ordered operations are central. Use binary search on a sorted list for compact static data and frequent reads; insertion into the middle still costs O(n). Use a hash map for exact-key lookup without range or order queries, and a heap when only the current extreme matters.
State the duplicate rule and the interval owned by each recursive call. For search, name the subtree discarded by each comparison. Give complexity in terms of height first, then translate to balanced and worst-case bounds. Do not call every binary tree a BST.
Build and Query a Binary Search Tree(opens in a new tab) practices directed descent. Validate a Binary Search Tree(opens in a new tab) tests the global lower/upper-bound invariant.
Python 3.11+
The examples use standard Python containers and syntax available in the supported browser runtime; Binary Search Tree itself is taught as an interview abstraction.
Verify in Python docs(opens in a new tab)Interview bounds depend on the stated representation, input model, and real Python operations.
| Operation | Average | Worst | Interview note |
|---|---|---|---|
| Core Binary Search Tree workflow | O((n + q)h) | O((n + q)h) | Build a small explicit BST, traverse it in order, and direct each query left or right by comparison. |
O(n + h) for the demonstrated Binary Search Tree workflow.
These are the mistakes most likely to survive a happy-path example and fail a boundary case.
Placeholder code cannot preserve the Binary Search Tree invariant or satisfy the public contract.
Prevent it: Implement the smallest complete transition and run the public tests before adding optimizations.
Avoid
def bst_operations(values, queries):
passUse instead
def bst_operations(values, queries):
root = None
for value in values:
if root is None:
root = [value, None, None]
continue
node = root
while True:
if value == node[0]:
break
side = 1 if value < node[0] else 2
if node[side] is None:
node[side] = [value, None, None]
break
node = node[side]
ordered = []
def visit(node):
if node is not None:
visit(node[1])
ordered.append(node[0])
visit(node[2])
visit(root)
found = []
for query in queries:
node = root
while node is not None and node[0] != query:
node = node[1] if query < node[0] else node[2]
found.append(node is not None)
return [ordered, found]Where you will hit this: Build and Query a Binary Search Tree(opens in a new tab)
Always sends equality right, so duplicate input values appear more than once in the traversal.
Prevent it: Keep this invariant visible while editing: State the precise Binary Search Tree invariant before coding and preserve it after every update, traversal step, or recursive return.
Avoid
def bst_operations(values, queries):
return [sorted(values), [query in values for query in queries]]Use instead
def bst_operations(values, queries):
root = None
for value in values:
if root is None:
root = [value, None, None]
continue
node = root
while True:
if value == node[0]:
break
side = 1 if value < node[0] else 2
if node[side] is None:
node[side] = [value, None, None]
break
node = node[side]
ordered = []
def visit(node):
if node is not None:
visit(node[1])
ordered.append(node[0])
visit(node[2])
visit(root)
found = []
for query in queries:
node = root
while node is not None and node[0] != query:
node = node[1] if query < node[0] else node[2]
found.append(node is not None)
return [ordered, found]Where you will hit this: Build and Query a Binary Search Tree(opens in a new tab)
Handle empty state, duplicate values, aliasing, and serialization boundaries explicitly in tests.
Prevent it: Account for every slice, copy, sort, membership test, and container mutation before claiming O((n + q)h).
Avoid
def bst_operations(values, queries):
return [sorted(values), [query in values for query in queries]]Use instead
def bst_operations(values, queries):
root = None
for value in values:
if root is None:
root = [value, None, None]
continue
node = root
while True:
if value == node[0]:
break
side = 1 if value < node[0] else 2
if node[side] is None:
node[side] = [value, None, None]
break
node = node[side]
ordered = []
def visit(node):
if node is not None:
visit(node[1])
ordered.append(node[0])
visit(node[2])
visit(root)
found = []
for query in queries:
node = root
while node is not None and node[0] != query:
node = node[1] if query < node[0] else node[2]
found.append(node is not None)
return [ordered, found]Where you will hit this: Validate a Binary Search Tree(opens in a new tab)
Official Python documentation supports language behavior. Canonical problem pages provide additional practice context.
python-docs · checked 2026-07-27