Intuition
Balance demands the middle element as root. Finding the middle of a list repeatedly costs O(n log n) — instead, build inorder: recurse on sizes, consume list nodes left-to-right exactly when the inorder traversal would visit them. The list pointer advances once per node, total O(n).
Build the tree in inorder while walking the list forward. Since inorder traversal of a BST visits values in sorted order — exactly the list's order — the node is created at the moment between the two recursive calls, and the list pointer advances once per node.
Approach
Sizes first
Count the list length n. build(l, r) constructs the subtree covering positions l..r without knowing values in advance.
Inorder consumption
Build the left subtree, THEN take the current list node as root (advance the pointer), then build the right — values arrive in exactly the right order.
Balanced by construction
Midpoint split of sizes guarantees height ⌈log n⌉.
Solution & live demo
Common pitfalls
Finding the middle node for every subtree
slow, fast = head, head # find middle, recurse on both halves
left = build(l, mid - 1) node = TreeNode(self.cur.val) self.cur = self.cur.next
Repeated middle-finding costs O(n log n) because each level rescans. Building in inorder touches each node once — O(n) — since the list order already is the inorder sequence.
Creating the node before recursing left
node = TreeNode(self.cur.val) self.cur = self.cur.next node.left = build(l, mid - 1)
left = build(l, mid - 1) node = TreeNode(self.cur.val)
That consumes the list in preorder, so the smallest values land at internal nodes rather than the left spine and the result isn't a BST. The node must be taken at the inorder moment — after the left subtree is complete.
Converting to an array first
vals = [] while head: vals.append(head.val); head = head.next
n, cur = 0, head while cur: n += 1; cur = cur.next
Works, but allocates O(n) extra space when only the count is needed. The list itself is consumed in order by the inorder build, so the values never need copying.
Edge cases
n=0 → None.
Either middle works; mid = (l+r)//2 picks consistently.