Binary search tree Created 2026-10-13 Updated 2026-10-14
A binary search tree stores key-value entries in a binary tree, with distinct keys drawn from a total order. At every vertex, all keys in its left subtree must be smaller than its own key, and all keys in its right subtree must be larger. Thus the entries represent a partial function from keys to values: a key has at most one associated value and may be absent. [ref]
Lookup starts at the root of a tree. Compare the target with the current key: equality returns its value, a smaller target continues into the left subtree, and a larger target continues into the right subtree. Reaching an empty subtree reports absence. The ordering rule justifies discarding the other subtree at every step; this is the algorithm correctness argument for lookup. It applies to the entire subtree, even if that subtree contains many vertices. [ref]
In the seven-key example, search for . At , the comparison selects the right subtree; at , selects the left subtree; at , equality ends the search. The visited keys are , three key comparisons. The right-child chain stores exactly the same keys and satisfies the same binary search tree rule, but lookup of visits , five comparisons. Searching for absent key visits in the first binary search tree and all seven keys in the chain before reaching an empty right subtree.
Lookup of key in two binary search trees containing the same seven keys
. Orange highlights visited keys and edges; green marks the match. The balanced shape has height of a binary tree and requires three visits, while the chain has height of a binary tree and requires five visits for this target. Details.For a nonempty binary search tree of height of a binary tree , lookup inspects at most vertices. Under constant-cost key comparisons and child-reference access, its time complexity is in Big O notation, usually written for . With entries, , giving worst-case lookup. If the shape is kept balanced so that , lookup takes for . The empty and one-entry cases take constant work. The worst-case linear bound also appears in Sedgewick and Wayne's ordinary, unbalanced implementation. [ref]
https://raw.githubusercontent.com/kevin-wayne/algs4/master/src/main/java/edu/princeton/cs/algs4/BST.java. Sedgewick and Wayne, Algorithms: BST implementation, complexity contract and get method
In a shape with every level full, there are vertices, a geometric series. This explains how a small height of a binary tree can accommodate many entries. The binary search tree ordering rule alone does not guarantee this shape or logarithmic time complexity. Breadth-first search also gains no logarithmic key-lookup guarantee merely from having a binary tree: in the displayed balanced example, its left-to-right level order is , so it inspects six keys before finding , and an absent target requires all seven. Directed lookup uses the total order to choose one branch.
The ordering requirement includes every ancestor on the graph path from the root of a tree. Consider the smaller binary tree with root key , left child , right child , and right child under . Key must satisfy both , from its immediate parent in a rooted tree, and , from its position in the root's left subtree. Replacing by preserves all immediate parent-child comparisons, including , but violates . Lookup for would incorrectly go from to and then to an empty left subtree, overlooking the stored key.
The binary search tree ordering rule constrains an entire subtree
. The valid example has . The invalid example passes immediate parent-child comparisons, but key lies in the left subtree of key and violates its inherited upper bound. Details.An inorder traversal visits a left subtree, then its own vertex, then a right subtree. On the valid small example its output is ; on the invalid example it is , exposing the descent from to . Both seven-key binary search trees yield . The theorem is that an inorder traversal of a finite binary search tree with distinct keys lists every key once in strictly increasing total order. For a short proof outline, use mathematical induction on the number of vertices: each smaller subtree produces its own sorted list, and the full ordering rule places every left key before the current key and every right key after it. Concatenating these three parts preserves strict order. Sedgewick and Wayne's
keys method uses this left-current-right order. [ref]https://raw.githubusercontent.com/kevin-wayne/algs4/master/src/main/java/edu/princeton/cs/algs4/BST.java. Sedgewick and Wayne, Algorithms: BST keys method and inherited-bound isBST check
This combination is useful and attractive: one data structure supports directed lookup and ordered enumeration. An inorder traversal costs time complexity for entries, since it visits every vertex; balance accelerates lookup but cannot eliminate the work of outputting all keys.
// Destination: computer-science.bigb
Binary tree Created 2026-10-13 Updated 2026-10-14
A binary tree, in the data structure convention used here, is a rooted tree in which each vertex has zero, one or two children in a rooted tree, occupying distinct left and right positions. A single left child and a single right child give different binary trees. A vertex with no children in a rooted tree is called a leaf. An empty binary tree has no root of a tree and is also allowed as a storage case. [ref]
For example, give the root of a tree label , its left child label and its right child label . Attach as the left and right children in a rooted tree of , and as those of . This binary tree has seven vertices, six edges, and height of a binary tree : a longest downward graph path contains two edges. The binary search tree lookup illustration below shows this shape alongside the binary tree consisting only of successive right children , whose height of a binary tree is .
Left and right positions specify an ordered shape, not a numerical total order on stored labels. Swapping labels and in the first example still gives a binary tree, but destroys the binary search tree property. Nor must a binary tree be balanced: the right-child chain is just as valid. The same shape convention supports a binary heap, a Huffman code, or a binary search tree, each with its own additional rules. The useful idea is to describe two smaller subtrees at each vertex, making structural recursion natural.
For a precise recursive description, a finite binary tree is either empty or a root of a tree carrying a value together with an ordered pair of disjoint smaller binary trees. The entries of this ordered pair are its left and right subtrees, each of which may be empty. Thus two binary tree child slots exist even when fewer than two children in a rooted tree exist; an empty slot does not denote an extra vertex. This decomposition is the basis of structural recursion.[ref]
4.4 Symbol Tables. Introduction to Programming in Java, section 4.4, Binary search trees: recursive definition and node representation
Left and right binary tree child slots distinguish shapes even with just one child
. Solid circles are vertices; outlined squares mark empty binary tree child slots. The first two binary trees differ only in the occupied slot at . The last two have the same four labels but different height of a binary tree. Details.In the four-vertex mixed example, has left child and right child , while has only a right child . Count its vertices by structural recursion: the empty binary tree contributes zero and each nonempty binary tree contributes one plus the counts of its left and right subtrees. Thus contributes , and contributes . The same decomposition computes height of a binary tree, supports inorder traversal, and supplies short proofs by structural induction. It separates shape from the values stored in a data structure.
Ignoring values, let count finite binary tree shapes with vertices, retaining left and right slots and counting the empty shape once. Splitting at the root of a tree gives the recurrence relationFor each left subtree size , choose its shape and the right subtree shape independently. This yields the Catalan numbers: , , and . The two shapes at are exactly the left-only and right-only examples. Forgetting the slot distinction would change the counting problem.
Destructive binary search tree flattening 2026-10-31
Turn a binary search tree into a sorted doubly linked list by reusing each linked list node's left and right child fields as predecessor and successor fields. An inorder traversal determines the required order. A recursive implementation first saves both child references, flattens the disjoint child subtrees, and splices their endpoints around the root. Structural induction proves that it preserves every original entry exactly once and produces reciprocal links with null outside endpoints. An explicit stack implements the same traversal without recursive calls; each original right child must be saved before its field is overwritten. Both versions take linear time complexity and auxiliary space for original height . The old binary search tree representation is consumed, so outside aliases must not continue to interpret the changed links as child pointers.
Inorder traversal 2026-10-13
An inorder traversal of a binary tree uses recursion to visit the left subtree, output the current vertex, and visit the right subtree; an empty subtree outputs nothing. This is a depth-first traversal of a tree with output between the two recursive calls. It produces sorted keys for a binary search tree, but arbitrary labels in a binary tree need not appear in total order.
// Target: computer-science.bigb
Use the empty list for an empty subtree, and join the left result, the current label and the right result:
flatten :: STree a -> [a]
flatten = foldSTree (\left x right -> left ++ (x : right)) []Thus . This is an inorder traversal. By structural induction, each subtree produces its labels in order; the binary search tree invariant places every left label below and every right label above , proving that the whole result is increasing. The definition works for any binary-tree shape, although its result is sorted only when the ordering invariant holds. Repeated list concatenation can give quadratic cost on a left chain; the question asks for a tree fold definition, rather than an optimal traversal implementation.
Use pattern matching on the two data constructors, concatenating the left subtree's inorder traversal, the root label, and the right subtree's inorder traversal:Here
data Tree = Leaf | Fork Tree Int Tree
inorder :: Tree -> [Int]
inorder Leaf = []
inorder (Fork l x r) = inorder l ++ [x] ++ inorder rLeaf represents the empty binary tree, rather than a labelled leaf. The printed declaration uses type; an executable Haskell algebraic data type requires data, as above. By structural induction, the two equations return precisely the prescribed inorder traversal:Split the half-open array segment at its midpoint, using one entry for the root:The precondition is . Set . For the child sizes are and , differing by at most one. The same construction in both subtrees proves subtree-size balance at every node. Structural induction also gives the inorder traversal , followed by , followed by , hence precisely ; no sortedness assumption is needed.
PROCEDURE MakeTree(VAR u: ARRAY OF INTEGER;
a, b: INTEGER): Tree;
VAR m: INTEGER; t: Tree;
BEGIN
IF a = b THEN RETURN NIL END;
m := a + (b-a) DIV 2;
NEW(t);
t.label := u[m];
t.left := MakeTree(u, a, m);
t.right := MakeTree(u, m+1, b);
RETURN t
END MakeTree;Exactly records are allocated. Each nonempty recursive call makes two child calls, so there are calls including empty segments. Under constant-cost allocation and reference parameter passing,For positive this is proportional to ; the empty case takes constant time. The call stack has depth.
Use destructive inorder traversal by right rotations. A right tree rotation preserves inorder traversal but moves the first unvisited node closer to the root:The outer loop invariant states that the labels already passed to
PROCEDURE InorderDestructive(t: Tree; p: Visit);
VAR q: Tree;
BEGIN
WHILE t # NIL DO
WHILE t.left # NIL DO
q := t.left;
t.left := q.right;
q.right := t;
t := q
END;
p(t.label);
t := t.right
END
END InorderDestructive;p, followed by the inorder traversal of t, equal the original inorder traversal. The remaining nodes form a binary tree disjoint from the nodes already visited. The inner loop invariant preserves the remaining inorder traversal: if the old root is , its left child is , and the three unchanged subtrees are , then both shapes have order .At inner-loop exit,
t.left = NIL, so the root is the first remaining node. Visiting it and replacing t by t.right preserves the outer loop invariant. The procedure changes child pointers and makes the old root reference unusable as the original binary tree. Assume p neither changes the binary tree nor depends on its old shape.Replace suspended recursive calls by an explicit stack for an inorder traversal. Save each original right child before overwriting that link:The method belongs to the concrete class owning
def flattenIterative(): (Tree, Tree) = {
val pending = new scala.collection.mutable.Stack[Tree]
var current = root
var start: Tree = null
var end: Tree = null
while (current != null || pending.nonEmpty) {
while (current != null) {
pending.push(current)
current = current.left
}
val t = pending.pop()
val nextSubtree = t.right
t.left = end
if (end == null) start = t else end.right = t
t.right = null
end = t
current = nextSubtree
}
root = null
(start, end)
}root. Clearing root records that ownership has transferred to the returned doubly linked list; callers retain both returned endpoints. The inner loop invariant says that the current subtree lies before all its pending ancestors in inorder traversal; pushing and descending left preserves this fact. Its loop variant is the number of entries along the remaining left-child path, a finite decreasing quantity.The outer loop invariant says that
start through end is precisely the already emitted sorted prefix, with reciprocal links and null outside endpoints. current and the pending ancestors describe exactly the remaining traversal in order; original links that are still needed have been retained. A pending ancestor has not yet had its right child overwritten. The next popped entry is therefore the next sorted entry. Saving nextSubtree preserves its future work before assigning t.right = null; linking it to end extends the prefix by exactly one entry. The number of original entries not yet emitted is an outer loop variant, since each full outer iteration emits one. At exit nothing remains, proving the same preservation and ordering postconditions as the recursive version.Each entry is pushed and popped once, giving time complexity and auxiliary space. No new
Tree entries are allocated. The historical mutable stack interface provides the push and pop operations used here; its later deprecation does not change their meaning.[ref] Preorder traversal Created 2026-10-25 Updated 2026-10-31
Visit a rooted tree's root before recursively visiting its child subtrees in their specified order. For a binary tree, the order is root, left subtree, right subtree. Unlike inorder traversal, this interpretation stores an internal node's label before either child's labels.
Scala 2.12 mutable Stack API documentation 2026-10-31
Official historical API documentation for the mutable stack: the empty constructor,
push, pop and nonEmpty support an explicit inorder traversal stack. The API's deprecation notices concern later collection choices; they do not remove these documented operations from the cited version. Splaying 2026-10-31
In ordinary bottom-up splaying, locate the selected node by a binary search tree search and repeatedly move it toward the root. If its parent is the root, perform a zig step. Otherwise use a zig-zig step when the node and its parent occupy the same left/right directions, or a zig-zag step when their directions differ. Each tree rotation preserves the inorder traversal and hence the represented key order. The process ends with the selected key at the root. Unsuccessful access policies can instead move the last visited node to the root; arguments about successful calls should say the requested key exists.



