The problem. Given the root of a binary search tree and a value val that is not already in the tree, insert it and return the root of the resulting BST. Any valid BST is accepted, but the standard answer — and the one interviewers expect — adds the value as a new leaf.
root = [4, 2, 7, 1, 3], val = 5 -> [4, 2, 7, 1, 3, 5]
root = [40, 20, 60, 10, 30, 50, 70], val = 25 -> [40, 20, 60, 10, 30, 50, 70, null, null, 25]
root = [], val = 5 -> [5]Search for val as if it were there. The search ends by stepping off the tree at an empty child — and that empty child is exactly where val belongs: every comparison on the way agreed it fits there. Attaching a leaf never disturbs the rest of the tree.
Each call returns the root of its subtree after the insert. For an empty subtree that's the new node; otherwise it's the same node, with one child replaced by the result of the recursive call. This "return the subtree and re-attach it" shape comes back in delete, trim and many other BST problems.
The new node is created where the walk runs out; each call re-attaches its child on the way back.
function insertIntoBST(root, val) { if (root === null) return new TreeNode(val); // the empty spot: the new leaf if (val < root.val) root.left = insertIntoBST(root.left, val); else root.right = insertIntoBST(root.right, val); return root; // this subtree's root is unchanged}Time O(h), space O(h) for the call stack.
function insertIntoBST(root, val) { const fresh = new TreeNode(val); if (root === null) return fresh; let node = root; for (;;) { const side = val < node.val ? "left" : "right"; if (node[side] === null) { node[side] = fresh; // attach to the empty side return root; } node = node[side]; }}Time O(h), space O(1). Same path, no call stack.