A BST can be walked in the same four orders as any binary tree — but in a BST each order means something. The most important: an in-order walk (left subtree, node, right subtree) visits the values in sorted order, because everything on the left is smaller and everything on the right is bigger. Half of all BST interview questions are this one fact in disguise.
function inorder(root) { const out = []; const walk = (node) => { if (node === null) return; walk(node.left); // everything smaller first out.push(node.val); // then the node walk(node.right); // then everything bigger }; walk(root); return out;}Time O(n), space O(h) for the call stack.
The recursion's call stack can be made explicit: go left as far as possible, pushing every node on the way; pop one, visit it, then do the same from its right child. The stack always holds the nodes still waiting for their turn — at most one per level. This version can also be paused after any value, which is exactly what the BST Iterator problem needs.
Push the whole left spine, pop and visit, then continue from the right child.
function inorder(root) { const out = []; const stack = []; let node = root; while (node !== null || stack.length > 0) { while (node !== null) { // go left as far as possible, stack.push(node); // remembering each node on the way node = node.left; } node = stack.pop(); // the smallest value not visited yet out.push(node.val); node = node.right; // then everything just bigger than it } return out;}Time O(n), space O(h). Every node is pushed once and popped once.
Morris traversal visits in-order with O(1) extra memory by borrowing the tree's own empty pointers. Before going left from a node, find its predecessor (the rightmost node of its left subtree) and point that node's empty right at the current node — a temporary thread back up. When the walk later reaches the thread, it knows the left side is done: it removes the thread, visits the node and goes right. The tree ends up exactly as it started.
function inorder(root) { const out = []; let node = root; while (node !== null) { if (node.left === null) { out.push(node.val); // nothing smaller left: visit, go right node = node.right; continue; } let pred = node.left; // the rightmost node of the left subtree while (pred.right !== null && pred.right !== node) pred = pred.right; if (pred.right === null) { pred.right = node; // thread back to here, then do the left side node = node.left; } else { pred.right = null; // the left side is done: remove the thread, out.push(node.val); // visit this node, go right node = node.right; } } return out;}Time O(n), space O(1). Each edge is walked at most a few times while finding predecessors, so it's still linear. It temporarily changes the tree, so it isn't safe while another thread reads it.
These are written exactly as for any binary tree — the Binary Tree course walks through each with a simulator. In short:
pre-order: visit(node); walk(node.left); walk(node.right)
post-order: walk(node.left); walk(node.right); visit(node)
level order: a queue: pop a node, visit it, push its children