INVARIANT · BINARY TREES · CONSTRUCTION & TREE DP
01
00/07
01 / COVER STEP 13 · BINARY TREES
INVARIANT · STEP 13 · DECK 3 OF 3
BUILD THE TREE

The finale. First you exploit structure, counting a complete tree's nodes in O(log²n) and flattening a tree into a list in O(1) space. Then you run traversals backwards: reconstruct the exact tree from its preorder+inorder (or inorder+postorder), and serialize it to a string and back. You'll meet Morris traversal, which walks a tree in O(1) space by temporarily threading it. And you'll close on tree DP (House Robber III and Binary Tree Cameras) where each node returns a small tuple of sub-answers and the whole tree folds up in one postorder.

7Problems
3Families
8Units
8Live walks
← → ↑ ↓  or  W A S D  navigate SPACE next   DOUBLE-CLICK to advance I index   G goto problem   P predict H hide solutions   T close video   F fullscreen
INVARIANT · BINARY TREES · CONSTRUCTION & TREE DP · DECK 3 OF 3
02 / START HERE READ THIS ONE FIRST

HOW THIS DECK WORKS

This is not a list of problems. It is 8 learning units, and every one runs the same four beats. Go in order and the problems stop feeling random.

01 INTROWhat the concept is, and what to watch for
02 VIDEOThe lecture, full-width in theatre mode
03 DRILLS2–4 questions checking the lecture landed
04 PROBLEMSThe sheet problems that concept unlocks

Moving around

← ↑Back a slide — or A / W → ↓Forward — or D / S / Space 2×clickDouble-click the right side to advance, left to go back. A single click never moves the deck. IThe index: every problem, clickable, with your progress GJump straight to a problem by its number FFullscreen

While you study

HHide solutions — blurs code and steps so you try first PPredict mode: call the next step before the animation plays it TClose the video — Esc works too ☐ ★Mark solved, or star to revisit. Both are saved automatically.

7 PROBLEMS · EVERY ONE LINKS TO LEETCODE TO SOLVE

INVARIANT · BINARY TREES · CONSTRUCTION & TREE DP · DECK 3 OF 3
03 / INDEX PRESS I FROM ANYWHERE

INDEX — ALL 7 PROBLEMS

Click any problem to jump straight to it. Solved ones turn green. Press I from anywhere to come back here. The number is the sheet's row, not your position — the deck teaches in the order that builds, so you will meet them slightly out of numerical sequence.

STRUCTURE · 02
BUILD FROM TRAVERSALS · 03
TREE DP · 02
SOLVED HAS A LEETCODE LINK
INVARIANT · BINARY TREES · CONSTRUCTION & TREE DP · DECK 3 OF 3
04 / SIGNALS WHEN YOU SEE X, REACH FOR Y

WHICH TECHNIQUE, AND WHY

The finale's toolbox. The cards are the phrase that selects it: “complete tree” → the log² shortcut, “from traversals” → reconstruct with an index map, “O(1) space” → Morris/in-place threading, “no two adjacent” → tree DP.

“COMPLETE TREE” + “COUNT NODES FAST”

left-height == right-height means a perfect subtree of 2^h − 1 nodes, no walk

COMPLETE-TREE SHORTCUTO(log²n)
“FLATTEN” / “REARRANGE INTO A LIST”

reuse the tree's own right pointers; thread the left subtree in before the right

IN-PLACE REWIREO(n) · O(1) space
“BUILD THE TREE FROM ITS TRAVERSALS”

pre/post gives the root, inorder splits left|right; recurse with an index map

RECONSTRUCT FROM SEQUENCESO(n)
“SEND A TREE / STORE IT AS A STRING”

serialize with null markers (preorder or BFS); deserialize by consuming tokens

SERIALIZE + DESERIALIZEO(n)
“INORDER IN O(1) SPACE” / NO STACK, NO RECURSION

temporarily thread each node to its inorder predecessor, then undo the thread

MORRIS TRAVERSALO(n) · O(1) space
“MAX/MIN OVER THE TREE WITH A CONSTRAINT” (no two adjacent…)

each node returns a small tuple of sub-answers; combine in one postorder

TREE DPO(n)
INVARIANT · BINARY TREES · CONSTRUCTION & TREE DP · DECK 3 OF 3
05 / CONSTRAINTS BEFORE YOU WRITE A LINE

READ THE ANSWER OFF THE CONSTRAINTS

Most of this deck is one O(n) pass. The two special cases: counting a complete tree beats O(n) with the equal-heights shortcut (O(log²n)), and Morris/flatten reach O(1) extra space by threading the tree instead of using a stack.

n ≤
BUDGET
WHAT THAT BUYS YOU
n ≤ 10⁵
O(n)
one traversal, construction, serialize, flatten, tree DP all live here
complete tree
O(log²n)
count complete nodes: log n levels × log n height check, no full walk
O(1) extra space
O(1)
Morris / in-place flatten thread the tree instead of using a stack
n ≤ 10⁶
O(n)
linear passes scale; construction needs an O(1) index map to stay O(n)
n ≤ 10³
O(n²)
naive construction (linear search for the root each time), avoid it

TREE FINALE ⇒ O(n) PASS · COMPLETE-TREE ⇒ O(log²n) · THREADING ⇒ O(1) SPACE

INVARIANT · BINARY TREES · CONSTRUCTION & TREE DP · DECK 3 OF 3
06 / ROADMAP WHAT YOU ARE ABOUT TO DO

THE WHOLE RUN, 8 UNITS

Eight units. Structure exploits (count complete, flatten) open; then reconstruction from pre+in and in+post, serialize/deserialize, Morris (a concept unit, O(1)-space traversal), and the two canonical tree DPs. This closes Binary Trees; BST (Step 14) is next.

UNIT 01

Count Complete Nodes

▶ 16:132 DRILLS1 PROBLEM
UNIT 02

Build from Pre + In

▶ 18:522 DRILLS1 PROBLEM
UNIT 03

Build from In + Post

▶ 19:462 DRILLS1 PROBLEM
UNIT 04

Serialize & Deserialize

▶ 17:182 DRILLS1 PROBLEM
UNIT 05

Morris Traversal

▶ 23:502 DRILLSNO SHEET ROW
UNIT 06

Flatten to Linked List

▶ 21:512 DRILLS1 PROBLEM
BEYOND THE SHEETUNIT 07

House Robber III

NO LECTURE2 DRILLS1 PROBLEM
BEYOND THE SHEETUNIT 08

Binary Tree Cameras

NO LECTURE2 DRILLS1 PROBLEM
INVARIANT · BINARY TREES · CONSTRUCTION & TREE DP · DECK 3 OF 3
07 / WARMUP REVERSE THE TRAVERSAL, FOLD UP THE TREE

UNIQUE-TREE RULE · TREE DP TUPLES

DRILL 01 · RECALL

To reconstruct a unique binary tree, which pair of traversals is enough, and which pair is NOT?

Inorder is the essential ingredient. Preorder or postorder tells you the root; inorder then tells you which nodes fall in the left subtree vs the right (everything before the root vs after). Without inorder, e.g. preorder+postorder, a node with a single child is ambiguous (left or right?), so the tree isn't unique. This is the 'requirements for a unique tree' theory (L33), folded into this deck.

DRILL 02 · RECALL

Tree DP (House Robber III, Cameras) has each node return information to its parent. Why a tuple/pair rather than a single number?

The parent needs several conditional sub-answers. In House Robber III a node must know both 'best if this child is robbed' and 'best if it isn't', because robbing the parent forbids robbing the child. A single scalar loses that; a pair (rob, notRob) carries exactly what the parent needs to combine. Tree DP is 'return a small tuple, fold up in one postorder'.

INVARIANT · BINARY TREES · CONSTRUCTION & TREE DP · DECK 3 OF 3
08 / INTRO UNIT 01 · Count Complete Nodes

UNIT 01 — Count Complete Nodes

Counting the nodes of an arbitrary tree is a trivial O(n) walk, but a complete tree lets you do it in O(log²n). At any node, measure the left height (keep going left) and the right height (keep going right). If they're equal, the subtree is perfect and has exactly 2ʰ − 1 nodes. Return that without touching them. Only when the heights differ (the one ragged path) do you recurse into both children.

THE QUESTION THIS LECTURE ANSWERS

HOW DO YOU COUNT A COMPLETE TREE'S NODES WITHOUT VISITING THEM ALL?

complete treeleft vs right heightperfect ⇒ 2^h − 1ragged path onlyO(log²n)
WHAT TO WATCH FOR
  • 01LEFT HEIGHT (ALL-LEFT) VS RIGHT HEIGHT (ALL-RIGHT) AT A NODE
  • 02EQUAL ⇒ PERFECT SUBTREE ⇒ RETURN 2^h − 1 INSTANTLY
  • 03DIFFERENT ⇒ RECURSE: 1 + count(left) + count(right)
  • 04ONLY THE RAGGED PATH RECURSES → O(log n) CALLS × O(log n) HEIGHT = O(log²n)
INVARIANT · BINARY TREES · CONSTRUCTION & TREE DP · DECK 3 OF 3
09 / VIDEO UNIT 01 · Count Complete Nodes

L32. Count Nodes in a Complete Binary Tree

STRIVER A2Z
Count Complete Nodes
RUNTIME 16:13
AFTER THIS → 2 DRILLS · PROBLEM #01
INVARIANT · BINARY TREES · CONSTRUCTION & TREE DP · DECK 3 OF 3
10 / DRILL UNIT 01 · Count Complete Nodes

CHECK — CAN YOU ANSWER IT WITHOUT LOOKING BACK?

DRILL 01 · RECALL

Why does equal left-height and right-height mean the subtree is perfect?

Same spine lengths ⇒ no gaps ⇒ every level full. A complete tree fills left-to-right, so a shortfall would show up as the rightmost path being shorter than the leftmost. When they match, the last level is full too, making it perfect, 2ʰ − 1 nodes by formula, computed without a single node-by-node visit.

DRILL 02 · RECALL

Where does the O(log²n) come from?

O(log n) recursions × O(log n) height check. Every perfect subtree is dispatched by formula in O(log n) (the height measurement) with no recursion. Only the one path where left and right heights disagree recurses, and that path has length O(log n). Multiply and you get O(log²n), dramatically better than the naive O(n).

INVARIANT · BINARY TREES · CONSTRUCTION & TREE DP · DECK 3 OF 3
11 / MECHANISM UNIT 01 · COUNTNODES · CODE MIRRORED

THE SAME SKELETON, WITH THE SIMPLEST COMBINE

Worth seeing precisely because it is dull. Identical walk, identical dependency, and the combine line is just 1 + L + R. Once you can see that depth, balance, diameter, max-path-sum and node-count are one function with one line swapped, deck 2 stops being sixteen problems and becomes about three. (For a complete tree there is a genuinely faster O(log²n) method that compares left and right spines, but the linear version is the one to reach for first, and the one every sibling problem is built on.)

THE TREE
CALL STACK
RESULT · VISIT ORDER
CODE MIRROR
INVARIANT · BINARY TREES · CONSTRUCTION & TREE DP · DECK 3 OF 3
12 / PROBLEM #01 · STRUCTURE · MED

Count Complete Tree Nodes

MED structure ▶ SOLVE ON LEETCODE
SIGNAL — WHAT GIVES IT AWAY

“Count nodes of a COMPLETE binary tree, better than O(n).” 'Complete' is the signal to exploit the shape.

INTUITION

Measure left height (all-left) and right height (all-right). If equal, the subtree is perfect: return 2^h − 1. Otherwise recurse into both children and add 1.

STEPS
  1. leftHeight(node): count nodes going only left; rightHeight: only right
  2. count(node): if null, return 0
  3. if leftHeight == rightHeight: return 2^leftHeight − 1 (perfect)
  4. else return 1 + count(left) + count(right)
↕ SCROLL
// Perfect subtrees counted by formula; recurse only the ragged path.
int leftH(TreeNode* n)  { int h = 0; while (n) { h++; n = n->left;  } return h; }
int rightH(TreeNode* n) { int h = 0; while (n) { h++; n = n->right; } return h; }
int countNodes(TreeNode* root) {
    if (!root) return 0;
    int lh = leftH(root), rh = rightH(root);
    if (lh == rh) return (1 << lh) - 1;      // perfect: 2^lh - 1
    return 1 + countNodes(root->left) + countNodes(root->right);
}
TIMEO(log²n)O(log n) recursions, each an O(log n) height check
SPACEO(log n)recursion depth
TRAP

Falling back to an O(n) walk. Counting every node ignores the 'complete' guarantee. The equal-heights shortcut dispatches whole perfect subtrees by formula, so only the single ragged path recurses, O(log²n). Measure heights with a plain left/right spine walk, not a full traversal.

INVARIANT · BINARY TREES · CONSTRUCTION & TREE DP · DECK 3 OF 3
13 / INTRO UNIT 02 · Build from Pre + In

UNIT 02 — Build from Pre + In

You can rebuild the exact tree from its preorder and inorder sequences. Preorder's first element is the root. Find that root in inorder: everything to its left is the left subtree, everything to its right is the right subtree. Recurse, advancing a preorder index. A value → inorder index hashmap makes the split O(1), keeping the whole build O(n). (Inorder is the essential half. It's what separates left from right.)

THE QUESTION THIS LECTURE ANSWERS

HOW DO YOU RECONSTRUCT THE UNIQUE TREE FROM PREORDER + INORDER?

preorder[0] = rootinorder splits L|Rindex hashmapadvance preIdxO(n)
WHAT TO WATCH FOR
  • 01PREORDER GIVES THE ROOT (FRONT TO BACK, ONE PER RECURSION)
  • 02FIND THE ROOT IN INORDER: LEFT PART = LEFT SUBTREE, RIGHT PART = RIGHT SUBTREE
  • 03USE A value→inorderIndex HASHMAP FOR O(1) SPLITS (ELSE O(n²))
  • 04BUILD LEFT THEN RIGHT, ADVANCING THE PREORDER INDEX
INVARIANT · BINARY TREES · CONSTRUCTION & TREE DP · DECK 3 OF 3
14 / VIDEO UNIT 02 · Build from Pre + In

L34. Construct Tree from Preorder & Inorder

STRIVER A2Z
Build from Pre + In
RUNTIME 18:52
AFTER THIS → 2 DRILLS · PROBLEM #02
INVARIANT · BINARY TREES · CONSTRUCTION & TREE DP · DECK 3 OF 3
15 / DRILL UNIT 02 · Build from Pre + In

CHECK — CAN YOU ANSWER IT WITHOUT LOOKING BACK?

DRILL 01 · RECALL

What role does each of the two traversals play in the reconstruction?

Preorder → root; inorder → split. Preorder visits root-first, so its front is always the next root. Once you know the root's value, its index in inorder cleaves the remaining nodes into 'before' (left subtree) and 'after' (right subtree). Recursing with those bounds and the advancing preorder index rebuilds the tree uniquely.

DRILL 02 · RECALL

Why build a hashmap of inorder values to indices instead of scanning inorder each time?

O(1) root location keeps the build O(n). The recursion visits each node once, but finding the root's split point by scanning is O(n) each time. Quadratic. Precomputing value → inorder index once turns every split into a constant-time lookup. Assumes distinct values, which these problems guarantee.

INVARIANT · BINARY TREES · CONSTRUCTION & TREE DP · DECK 3 OF 3
16 / MECHANISM UNIT 02 · PREORDER · CODE MIRRORED

PREORDER. RECORD THE NODE, THEN ITS SUBTREES

Preorder is root → left → right: you record a node the moment you reach it, before going into either subtree. The recursion is three lines. Record, go left, go right, and the call stack on the right is nothing more than the path from the root down to wherever you currently are. Watch the badges land: the root is always #1, and a subtree is numbered completely before its sibling gets started. This is the order you would copy or serialize a tree in, because the parent always comes out ahead of its children.

THE TREE
CALL STACK
RESULT · VISIT ORDER
CODE MIRROR
INVARIANT · BINARY TREES · CONSTRUCTION & TREE DP · DECK 3 OF 3
17 / PROBLEM #02 · BUILD · MED

Construct Binary Tree from Preorder and Inorder Traversal

MED build ▶ SOLVE ON LEETCODE
SIGNAL — WHAT GIVES IT AWAY

“Reconstruct the tree from preorder + inorder.” Two sequences, one unique tree.

INTUITION

Preorder's front is the root. Locate it in inorder (via a value→index map) to split left and right subtrees; recurse, advancing the preorder index.

STEPS
  1. build a map: inorder value → its index
  2. preIdx = 0
  3. build(inL, inR): if inL > inR, return null
  4. rootVal = preorder[preIdx++]; mid = map[rootVal]
  5. node->left = build(inL, mid−1); node->right = build(mid+1, inR); return node
↕ SCROLL
// Preorder gives roots; inorder (via a map) splits each subtree.
unordered_map<int,int> pos; int preIdx = 0;
TreeNode* build(vector<int>& pre, int inL, int inR) {
    if (inL > inR) return nullptr;
    int rootVal = pre[preIdx++];             // next root, preorder front-to-back
    TreeNode* root = new TreeNode(rootVal);
    int mid = pos[rootVal];                   // its split point in inorder
    root->left  = build(pre, inL, mid - 1);
    root->right = build(pre, mid + 1, inR);
    return root;
}
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
    for (int i = 0; i < inorder.size(); i++) pos[inorder[i]] = i;
    preIdx = 0;
    return build(preorder, 0, inorder.size() - 1);
}
TIMEO(n)each node created once with O(1) split lookups
SPACEO(n)the index map + recursion
TRAP

Scanning inorder for the root each call. O(n²). Precompute a value→index map so the split is O(1). Also build LEFT before RIGHT here (preorder is root-left-right), and advance the shared preorder index exactly once per node.

INVARIANT · BINARY TREES · CONSTRUCTION & TREE DP · DECK 3 OF 3
18 / INTRO UNIT 03 · Build from In + Post

UNIT 03 — Build from In + Post

Inorder + postorder rebuilds the tree the same way, with two twists. Postorder's LAST element is the root (postorder ends with the root), so you consume postorder from the back. And because you're taking roots in reverse (root, then right, then left), you must build the right subtree before the left. Same inorder split, same index hashmap.

THE QUESTION THIS LECTURE ANSWERS

HOW DOES CONSTRUCTION CHANGE FOR INORDER + POSTORDER?

postorder[last] = rootconsume from backright before leftinorder splitsO(n)
WHAT TO WATCH FOR
  • 01POSTORDER'S LAST ELEMENT IS THE ROOT. CONSUME FROM THE BACK
  • 02BUILD THE RIGHT SUBTREE BEFORE THE LEFT (ROOTS COME OUT ROOT-RIGHT-LEFT)
  • 03INORDER STILL SPLITS LEFT | RIGHT AROUND THE ROOT
  • 04SAME value→index HASHMAP FOR O(1) SPLITS
INVARIANT · BINARY TREES · CONSTRUCTION & TREE DP · DECK 3 OF 3
19 / VIDEO UNIT 03 · Build from In + Post

L35. Construct Tree from Inorder & Postorder

STRIVER A2Z
Build from In + Post
RUNTIME 19:46
AFTER THIS → 2 DRILLS · PROBLEM #03
INVARIANT · BINARY TREES · CONSTRUCTION & TREE DP · DECK 3 OF 3
20 / DRILL UNIT 03 · Build from In + Post

CHECK — CAN YOU ANSWER IT WITHOUT LOOKING BACK?

DRILL 01 · RECALL

Why must you build the right subtree before the left when using postorder from the back?

Backwards postorder is root-right-left. Postorder is left-right-root; reversed it's root-right-left. As you pop roots from the back, the immediately following ones are the right subtree's nodes, so you must recurse right before left to keep the index aligned. Building left-first (as in pre+in) would grab the wrong nodes.

DRILL 02 · RECALL

What is common to both construction problems?

Inorder split + index map is the shared engine. Whichever of preorder/postorder you pair with inorder, inorder does the left|right partition and the hashmap keeps it O(n). The only differences are where the root comes from (front of preorder vs back of postorder) and, consequently, the recursion order. Recognising the shared skeleton is the point.

INVARIANT · BINARY TREES · CONSTRUCTION & TREE DP · DECK 3 OF 3
21 / MECHANISM UNIT 03 · BUILD · CODE MIRRORED

INORDER IS WHAT SPLITS THE PROBLEM

Neither traversal can rebuild a tree alone. preorder tells you the roots in order but not where each subtree ends; inorder tells you where a root splits its range but not which node is the root. Together they are exact. Take the next value from preorder, find it in inorder, and everything to its left in that array is its left subtree, everything to the right is its right. Building from inorder + postorder is the identical method with one change: postorder gives the roots from the end backwards, so you consume it in reverse and build the right subtree first. The scan for the root is what makes a naive version O(n²). A value-to-index map takes it to O(n).

THE TREE
CALL STACK
RESULT · VISIT ORDER
CODE MIRROR
INVARIANT · BINARY TREES · CONSTRUCTION & TREE DP · DECK 3 OF 3
22 / PROBLEM #03 · BUILD · MED

Construct Binary Tree from Inorder and Postorder Traversal

MED build ▶ SOLVE ON LEETCODE
SIGNAL — WHAT GIVES IT AWAY

“Reconstruct the tree from inorder + postorder.” Same idea, postorder from the back.

INTUITION

Postorder's last element is the root. Consume postorder from the back; because roots come out root-right-left, build the RIGHT subtree before the left. Inorder splits as before.

STEPS
  1. build a map: inorder value → its index
  2. postIdx = postorder.size() − 1
  3. build(inL, inR): if inL > inR, return null
  4. rootVal = postorder[postIdx--]; mid = map[rootVal]
  5. node->right = build(mid+1, inR); node->left = build(inL, mid−1); return node
↕ SCROLL
// Postorder (from the back) gives roots; build RIGHT before left.
unordered_map<int,int> pos; int postIdx;
TreeNode* build(vector<int>& post, int inL, int inR) {
    if (inL > inR) return nullptr;
    int rootVal = post[postIdx--];           // roots come out root-right-left
    TreeNode* root = new TreeNode(rootVal);
    int mid = pos[rootVal];
    root->right = build(post, mid + 1, inR); // RIGHT first
    root->left  = build(post, inL, mid - 1);
    return root;
}
TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
    for (int i = 0; i < inorder.size(); i++) pos[inorder[i]] = i;
    postIdx = postorder.size() - 1;
    return build(postorder, 0, inorder.size() - 1);
}
TIMEO(n)each node created once with O(1) split lookups
SPACEO(n)the index map + recursion
TRAP

Building left before right. Consuming postorder from the back yields roots in root-right-left order, so you must recurse RIGHT first to align the index. Doing it left-first (the pre+in habit) grabs the wrong nodes and produces a mirrored/scrambled tree. Same inorder map, opposite recursion order.

INVARIANT · BINARY TREES · CONSTRUCTION & TREE DP · DECK 3 OF 3
23 / INTRO UNIT 04 · Serialize & Deserialize

UNIT 04 — Serialize & Deserialize

Serialize turns a tree into a string so it can be stored or sent; deserialize rebuilds the exact tree from that string. The key is explicit null markers. Do a preorder walk, emitting each value (and a sentinel like # for every null child) separated by commas. To rebuild, consume the tokens in the same preorder: a value creates a node and recurses for its two children; a # returns null. The markers make the shape unambiguous.

THE QUESTION THIS LECTURE ANSWERS

HOW DO YOU FLATTEN A TREE TO A STRING AND REBUILD IT EXACTLY?

null markers (#)preorder emittoken streamconsume in orderO(n)
WHAT TO WATCH FOR
  • 01PREORDER (OR BFS) EMITTING EACH VALUE, WITH A # MARKER FOR EVERY null
  • 02THE null MARKERS ARE WHAT MAKE THE SHAPE UNAMBIGUOUS
  • 03DESERIALIZE CONSUMES TOKENS IN THE SAME ORDER
  • 04A VALUE → NEW NODE + RECURSE TWO CHILDREN; A # → null
INVARIANT · BINARY TREES · CONSTRUCTION & TREE DP · DECK 3 OF 3
24 / VIDEO UNIT 04 · Serialize & Deserialize

L36. Serialize and De-serialize Binary Tree

STRIVER A2Z
Serialize & Deserialize
RUNTIME 17:18
AFTER THIS → 2 DRILLS · PROBLEM #04
INVARIANT · BINARY TREES · CONSTRUCTION & TREE DP · DECK 3 OF 3
25 / DRILL UNIT 04 · Serialize & Deserialize

CHECK — CAN YOU ANSWER IT WITHOUT LOOKING BACK?

DRILL 01 · RECALL

Why are null markers essential in the serialized string?

Markers disambiguate the shape. The values alone (say 1,2,3) fit many trees; you can't tell whether 2 is a left child with no children or has a subtree. Emitting # for every absent child records the structure precisely, so the token stream reconstructs a unique tree. BFS with markers works identically.

DRILL 02 · RECALL

How does deserialize know when to stop building a subtree?

The token order drives the recursion. Reading tokens front-to-back in the same preorder used to write them, a # immediately caps a branch (return null) and a value opens a node whose children are the next tokens. No counting or lengths needed. The interleaved values and markers are the structure.

INVARIANT · BINARY TREES · CONSTRUCTION & TREE DP · DECK 3 OF 3
26 / MECHANISM UNIT 04 · SERIALIZE · CODE MIRRORED

THE NULL MARKERS ARE THE ENCODING

Writing the values out level by level is not enough: many different trees flatten to the same list of values, so the string could not be read back. What makes the encoding reversible is writing a marker for every missing child. Those markers are the structure. Deserialising is then the same loop run backwards. Read a value, and the next two entries are its children, marker or not. It is the cleanest illustration in the deck that a traversal alone loses shape, which is also why preorder alone cannot rebuild a tree but preorder with null markers can.

THE TREE
CALL STACK
RESULT · VISIT ORDER
CODE MIRROR
INVARIANT · BINARY TREES · CONSTRUCTION & TREE DP · DECK 3 OF 3
27 / PROBLEM #04 · BUILD · HARD

Serialize and Deserialize Binary Tree

HARD build ▶ SOLVE ON LEETCODE
SIGNAL — WHAT GIVES IT AWAY

“Serialize a tree to a string and deserialize it back.” Encode with null markers.

INTUITION

Serialize by preorder, emitting each value and a '#' for every null, comma-separated. Deserialize by consuming those tokens in order: a value makes a node and recurses for two children, a '#' returns null.

STEPS
  1. serialize: preorder dfs; append val + ',' or '#,' for null
  2. deserialize: split into tokens; use an index/queue
  3. read a token: '#' → null; else create node(token)
  4. node->left = build(); node->right = build(); return node
↕ SCROLL
// Preorder with '#' null markers; deserialize consumes tokens in order.
string serialize(TreeNode* root) {
    string s;
    function<void(TreeNode*)> dfs = [&](TreeNode* n) {
        if (!n) { s += "#,"; return; }
        s += to_string(n->val) + ",";
        dfs(n->left); dfs(n->right);
    };
    dfs(root);
    return s;
}
TreeNode* deserialize(string data) {
    int i = 0;
    function<TreeNode*()> build = [&]() -> TreeNode* {
        int j = data.find(',', i);
        string tok = data.substr(i, j - i); i = j + 1;
        if (tok == "#") return nullptr;
        TreeNode* n = new TreeNode(stoi(tok));
        n->left = build(); n->right = build();
        return n;
    };
    return build();
}
TIMEO(n)each node emitted and read once
SPACEO(n)the string + recursion
TRAP

Omitting null markers. Without a sentinel for every absent child the string is ambiguous and can't be rebuilt uniquely. Emit '#' (or 'null') for each null. Deserialize must consume tokens in the exact order they were written. A shared index/iterator does this naturally.

INVARIANT · BINARY TREES · CONSTRUCTION & TREE DP · DECK 3 OF 3
28 / INTRO UNIT 05 · Morris Traversal

UNIT 05 — Morris Traversal

Morris traversal walks a tree in O(1) extra space, no stack, no recursion, by temporarily threading each node to its inorder predecessor. For inorder: if a node has a left child, find the rightmost node of that left subtree (the predecessor); point its right pointer back to the current node (the thread), then go left. When you return via that thread, you remove it (restoring the tree), record the node, and go right. Elegant, and a concept unit. The technique matters, not a judge row.

THE QUESTION THIS LECTURE ANSWERS

HOW DO YOU DO INORDER IN O(1) SPACE WITH NO STACK?

thread to predecessorrightmost of left subtreeO(1) spaceundo the threadno stack
WHAT TO WATCH FOR
  • 01IF A NODE HAS A LEFT CHILD, FIND THE RIGHTMOST NODE OF THAT LEFT SUBTREE (PREDECESSOR)
  • 02THREAD: predecessor->right = current, THEN GO LEFT
  • 03RETURNING VIA THE THREAD: REMOVE IT (RESTORE THE TREE), RECORD, GO RIGHT
  • 04THE THREAD IS TEMPORARY, ALWAYS UNDONE, OR THE TREE CORRUPTS
INVARIANT · BINARY TREES · CONSTRUCTION & TREE DP · DECK 3 OF 3
29 / VIDEO UNIT 05 · Morris Traversal

L37. Morris Traversal | O(1) Space

STRIVER A2Z
Morris Traversal
RUNTIME 23:50
AFTER THIS → 2 DRILLS · CONCEPT UNIT
INVARIANT · BINARY TREES · CONSTRUCTION & TREE DP · DECK 3 OF 3
30 / DRILL UNIT 05 · Morris Traversal

CHECK — CAN YOU ANSWER IT WITHOUT LOOKING BACK?

DRILL 01 · RECALL

What does Morris traversal use instead of a stack to get back up the tree?

A temporary right-pointer thread to the predecessor. Normally you'd need a stack to return to a node after its left subtree. Morris instead points the left subtree's rightmost node's (otherwise null) right pointer back at the current node, so the traversal walks up that link for free. O(1) space. The cost is a second visit to each threaded node (to undo the link).

DRILL 02 · RECALL

Why must the thread be removed on the second visit?

Leaving the thread creates a cycle and corrupts the tree. On reaching a node whose predecessor already threads back to it, you know the left subtree is finished: remove the thread (restore the null), record the node (for inorder), and move right. Skipping the removal loops forever and mutates the input. the whole discipline of Morris is 'thread, then always unthread'.

INVARIANT · BINARY TREES · CONSTRUCTION & TREE DP · DECK 3 OF 3
31 / MECHANISM UNIT 05 · INORDER · CODE MIRRORED

INORDER. LEFT SUBTREE, THEN NODE, THEN RIGHT

Inorder is left → root → right: a node is not recorded until its entire left subtree is done. One thing moved. The record line now sits between the two calls instead of before them, and the whole visit order changes. Watch which node gets badge #1: the leftmost leaf, not the root. On a binary search tree this order emits the values sorted, and that single fact is why inorder turns up on nearly every problem in the BST section.

THE TREE
CALL STACK
RESULT · VISIT ORDER
CODE MIRROR
INVARIANT · BINARY TREES · CONSTRUCTION & TREE DP · DECK 3 OF 3
32 / INTRO UNIT 06 · Flatten to Linked List

UNIT 06 — Flatten to Linked List

Flatten rearranges the tree into a right-skewed linked list that follows preorder (each node's left becomes null, its right points to the next preorder node). The O(1)-space trick is a Morris-like rewire: for each current node, if it has a left subtree, find that subtree's rightmost node, attach the current node's right subtree there, move the left subtree to the right, null the left, and advance. The tree's own pointers become the list.

THE QUESTION THIS LECTURE ANSWERS

HOW DO YOU FLATTEN A TREE INTO A PREORDER LIST IN O(1) SPACE?

preorder listrightmost of leftrewire rightnull the leftO(1) space
WHAT TO WATCH FOR
  • 01TARGET: LEFT = null EVERYWHERE, RIGHT = THE NEXT PREORDER NODE
  • 02FOR EACH NODE WITH A LEFT CHILD: FIND THE LEFT SUBTREE'S RIGHTMOST NODE
  • 03ATTACH THE CURRENT RIGHT SUBTREE THERE; MOVE LEFT SUBTREE TO RIGHT; NULL THE LEFT
  • 04ADVANCE current = current->right. REUSES THE TREE'S OWN POINTERS
INVARIANT · BINARY TREES · CONSTRUCTION & TREE DP · DECK 3 OF 3
33 / VIDEO UNIT 06 · Flatten to Linked List

L38. Flatten a Binary Tree to Linked List

STRIVER A2Z
Flatten to Linked List
RUNTIME 21:51
AFTER THIS → 2 DRILLS · PROBLEM #05
INVARIANT · BINARY TREES · CONSTRUCTION & TREE DP · DECK 3 OF 3
34 / DRILL UNIT 06 · Flatten to Linked List

CHECK — CAN YOU ANSWER IT WITHOUT LOOKING BACK?

DRILL 01 · RECALL

In the O(1) flatten, why attach the current node's right subtree to the rightmost node of its left subtree?

The right subtree continues after the left subtree ends. Flattened order is node, then everything on the left, then everything on the right. So you move the left subtree onto the node's right, and the previous right subtree must hang off the very end of that moved-in left subtree, its rightmost node. Then advance and repeat; each node's left ends up null and its right is the next preorder node.

DRILL 02 · RECALL

What makes this flatten O(1) space rather than O(n) (e.g. collecting preorder into a list)?

In-place pointer surgery, no auxiliary storage. The naive approach records preorder into an array then relinks, O(n) space. The Morris-like method reuses the tree's own left/right fields, threading the right subtree onto the left subtree's tail and shifting left to right. Only a handful of pointer locals are needed, so it's O(1) extra space.

INVARIANT · BINARY TREES · CONSTRUCTION & TREE DP · DECK 3 OF 3
35 / MECHANISM UNIT 06 · FLATTEN · CODE MIRRORED

BUILD THE CHAIN FROM THE TAIL BACKWARDS

Flatten rewrites the tree in place into a right-leaning list, and the order it lands in is preorder. The obvious approach. Walk preorder and re-point as you go, destroys the pointer to the right subtree before you have visited it. The fix is to work in reverse: recurse right, then left, then attach the current node in front of the already-finished tail, kept in a single prev variable. Every node then points at something already correct, so nothing is overwritten before it has been used. Reverse-postorder is exactly preorder backwards, which is why this produces the sequence it does.

THE TREE
CALL STACK
RESULT · VISIT ORDER
CODE MIRROR
INVARIANT · BINARY TREES · CONSTRUCTION & TREE DP · DECK 3 OF 3
36 / PROBLEM #05 · STRUCTURE · MED

Flatten Binary Tree to Linked List

MED structure ▶ SOLVE ON LEETCODE
SIGNAL — WHAT GIVES IT AWAY

“Flatten the tree to a right-skewed linked list, preorder order, in place.” O(1) space via a Morris-like rewire.

INTUITION

For each node with a left subtree, find that subtree's rightmost node, attach the node's current right subtree there, move the left subtree to the right, null the left, and advance to the right. The tree's own pointers become the list.

STEPS
  1. cur = root
  2. while cur: if cur->left is not null:
  3. prev = cur->left; while prev->right: prev = prev->right // rightmost of left
  4. prev->right = cur->right; cur->right = cur->left; cur->left = null
  5. cur = cur->right
↕ SCROLL
// In-place: splice the right subtree onto the left subtree's tail.
void flatten(TreeNode* root) {
    TreeNode* cur = root;
    while (cur) {
        if (cur->left) {
            TreeNode* prev = cur->left;
            while (prev->right) prev = prev->right;   // rightmost of left subtree
            prev->right = cur->right;                 // old right hangs off it
            cur->right = cur->left;                   // left subtree becomes right
            cur->left = nullptr;
        }
        cur = cur->right;                             // advance down the list
    }
}
TIMEO(n)each node visited a constant number of times
SPACEO(1)no auxiliary storage
TRAP

Collecting preorder into a list first. O(n) space. That works but misses the point. The in-place rewire threads the right subtree onto the rightmost node of the left, moves left to right, and nulls the left, reusing the tree's own pointers for O(1) extra space. Don't forget to null the left pointer, or the list still branches.

INVARIANT · BINARY TREES · CONSTRUCTION & TREE DP · DECK 3 OF 3
37 / INTRO UNIT 07 · House Robber III

UNIT 07 — House Robber III

House Robber III is the canonical tree DP. You may not rob two directly connected houses (a node and its child). Each node returns a pair: (rob, notRob). If you rob this node you take its value plus the not-robbed results of both children (they must be skipped): rob = val + notRob(L) + notRob(R). If you don't, each child is free to be robbed or not: notRob = max(rob,notRob)(L) + max(rob,notRob)(R). The answer is the max of the root's pair.

THE QUESTION THIS LECTURE ANSWERS

HOW DO YOU MAXIMISE A SUM WITH 'NO TWO ADJACENT' ON A TREE?

(rob, notRob) pairrob ⇒ skip childrennotRob ⇒ children freepostorder foldmax at root
WHAT TO WATCH FOR
  • 01EACH NODE RETURNS A PAIR (rob, notRob)
  • 02rob = node->val + notRob(left) + notRob(right) // children must be skipped
  • 03notRob = max(children's rob, notRob) SUMMED // children free to choose
  • 04ANSWER = max(rob, notRob) AT THE ROOT, ONLY COLLAPSE AT THE END
INVARIANT · BINARY TREES · CONSTRUCTION & TREE DP · DECK 3 OF 3
38 / DRILL UNIT 07 · House Robber III

CHECK — CAN YOU ANSWER IT WITHOUT LOOKING BACK?

DRILL 01 · RECALL

Why must each node return BOTH the rob and not-rob values, not just the better one?

The parent needs the child's not-robbed value. Robbing the parent forbids robbing its children, so the parent must add the children's not-robbed totals, a value that would be lost if the child collapsed to max(rob, notRob). Returning the full pair preserves exactly the information a parent might need either way; the max is taken only once, at the root.

DRILL 02 · RECALL

What is notRob for a node, in terms of its children?

Children are free when the parent is skipped. Not robbing the current node places no restriction on its children, so each contributes its own maximum. max(rob, notRob). Independently. Summing those gives the node's not-robbed value. Contrast rob, which forces the children's not-robbed values. Two clean cases, folded in one postorder.

INVARIANT · BINARY TREES · CONSTRUCTION & TREE DP · DECK 3 OF 3
39 / MECHANISM UNIT 07 · ROBTREE · CODE MIRRORED

ONE NUMBER PER NODE IS NOT ENOUGH

Every earlier member of the postorder-value family returned a single number. This one cannot, and the reason is worth the slide: robbing a node forbids robbing either child, so the parent needs each child's “best if I was skipped” figure, not merely its best. Return both. (robbed, skipped), and the recursion stays one clean pass. Collapse it to one number too early and you get a greedy answer that is wrong on trees where taking a child beats taking the parent. This is the tree version of the classic take-or-skip DP, and the shape carries directly to Cameras.

THE TREE
CALL STACK
RESULT · VISIT ORDER
CODE MIRROR
INVARIANT · BINARY TREES · CONSTRUCTION & TREE DP · DECK 3 OF 3
40 / PROBLEM #06 · TREEDP · MED

House Robber III

MED treedp ▶ SOLVE ON LEETCODE
SIGNAL — WHAT GIVES IT AWAY

“Max sum with no two directly-connected houses robbed.” A tree DP returning a pair.

INTUITION

Each node returns (rob, notRob). Rob = val + notRob of both children. NotRob = sum of each child's max(rob, notRob). Answer is the root's max.

STEPS
  1. solve(node): if null, return (0, 0)
  2. (lr, ln) = solve(left); (rr, rn) = solve(right)
  3. rob = node->val + ln + rn // children skipped
  4. notRob = max(lr, ln) + max(rr, rn) // children free
  5. return (rob, notRob); answer = max(solve(root))
↕ SCROLL
// Return (rob, notRob); collapse to max only at the root.
pair<int,int> solve(TreeNode* node) {
    if (!node) return {0, 0};
    auto [lr, ln] = solve(node->left);
    auto [rr, rn] = solve(node->right);
    int rob    = node->val + ln + rn;         // rob node -> skip children
    int notRob = max(lr, ln) + max(rr, rn);   // skip node -> children free
    return {rob, notRob};
}
int rob(TreeNode* root) {
    auto [r, nr] = solve(root);
    return max(r, nr);
}
TIMEO(n)each node visited once, O(1) combine
SPACEO(h)recursion depth
TRAP

Returning a single max up the tree. The parent needs the child's not-robbed value specifically (robbing the parent forbids robbing the child), which is lost if you collapse to max early. Return the full (rob, notRob) pair and take the max only at the root.

INVARIANT · BINARY TREES · CONSTRUCTION & TREE DP · DECK 3 OF 3
41 / INTRO UNIT 08 · Binary Tree Cameras

UNIT 08 — Binary Tree Cameras

Binary Tree Cameras places the fewest cameras so every node is monitored (a camera covers its own node, its parent, and its children). It's a greedy postorder with three states: 0 = uncovered, 1 = covered (a child has a camera), 2 = has a camera. A null returns covered (1), so leaves come back uncovered (0). Going bottom-up, if either child is uncovered you must place a camera here; if a child has a camera you're covered; otherwise you're uncovered and defer to your parent. Place cameras as high as possible.

THE QUESTION THIS LECTURE ANSWERS

HOW DO YOU COVER EVERY NODE WITH THE FEWEST CAMERAS?

3 states 0/1/2null = coveredcamera when child uncoveredgreedy bottom-upcameras as high as possible
WHAT TO WATCH FOR
  • 01THREE STATES: 0 UNCOVERED · 1 COVERED · 2 HAS A CAMERA
  • 02null RETURNS 1 (COVERED) SO LEAVES ARE UNCOVERED (0), NEVER CAMERA A LEAF
  • 03EITHER CHILD 0 (UNCOVERED) → PLACE A CAMERA HERE (2), count++
  • 04EITHER CHILD 2 (HAS CAMERA) → THIS NODE IS COVERED (1); ELSE UNCOVERED (0)
INVARIANT · BINARY TREES · CONSTRUCTION & TREE DP · DECK 3 OF 3
42 / DRILL UNIT 08 · Binary Tree Cameras

CHECK — CAN YOU ANSWER IT WITHOUT LOOKING BACK?

DRILL 01 · RECALL

Why does a null node return 'covered' (1), and what does that achieve for leaves?

Null-as-covered pushes cameras up to parents of leaves. If nulls were uncovered, you'd camera every leaf. Wasteful. Returning covered for null makes leaves uncovered, so their parents (one level up) take the camera, each covering three levels' worth of nodes. Placing cameras at the parents of uncovered nodes, as high as possible, is the greedy optimum.

DRILL 02 · RECALL

After the postorder returns, why might you still need to add one camera at the root?

The root has no parent to defer to. A node in state 0 normally relies on its parent to place the covering camera. The root has none, so if the recursion returns 0 for the root, it's still uncovered. You add a final camera there. Every other uncovered node was already handled by its parent during the bottom-up pass.

INVARIANT · BINARY TREES · CONSTRUCTION & TREE DP · DECK 3 OF 3
43 / MECHANISM UNIT 08 · CAMERAS · CODE MIRRORED

PUT THE CAMERA ON THE PARENT, NEVER THE LEAF

Cameras is the pair idea widened to three states. Needs cover, has a camera, covered, and a genuinely greedy decision: place a camera only when a child is still uncovered, and place it on the parent. That choice is the whole optimisation. A camera on a leaf covers the leaf and its parent; the same camera one level up covers the leaf, its sibling and the parent. Strictly better, always. So leaves should never hold one. The one case the recursion cannot handle is the root: it has no parent to fall back on, so if it comes back still uncovered it must buy its own.

THE TREE
CALL STACK
RESULT · VISIT ORDER
CODE MIRROR
INVARIANT · BINARY TREES · CONSTRUCTION & TREE DP · DECK 3 OF 3
44 / PROBLEM #07 · TREEDP · HARD

Binary Tree Cameras

HARD treedp ▶ SOLVE ON LEETCODE
SIGNAL — WHAT GIVES IT AWAY

“Fewest cameras so every node is monitored.” A 3-state greedy postorder.

INTUITION

States: 0 uncovered, 1 covered, 2 has a camera. Null returns 1. If either child is uncovered (0), place a camera here (return 2); if a child has a camera (2), you're covered (1); else you're uncovered (0). Add one at the root if it ends uncovered.

STEPS
  1. cameras = 0
  2. dfs(node): if null, return 1 (covered)
  3. l = dfs(left); r = dfs(right)
  4. if l == 0 or r == 0: cameras++; return 2 (place a camera)
  5. if l == 2 or r == 2: return 1 (covered); else return 0 (uncovered)
  6. answer: if dfs(root) == 0, cameras++; return cameras
↕ SCROLL
// 3-state greedy: camera only when a child is uncovered.
int cameras = 0;
int dfs(TreeNode* node) {          // 0 uncovered, 1 covered, 2 has camera
    if (!node) return 1;           // null counts as covered
    int l = dfs(node->left), r = dfs(node->right);
    if (l == 0 || r == 0) { cameras++; return 2; }  // must cover a child
    if (l == 2 || r == 2) return 1;                 // a child's camera covers us
    return 0;                                        // uncovered -> parent handles
}
int minCameraCover(TreeNode* root) {
    cameras = 0;
    if (dfs(root) == 0) cameras++;  // root left uncovered
    return cameras;
}
TIMEO(n)each node visited once
SPACEO(h)recursion depth
TRAP

Cameraing leaves, or forgetting the root fix-up. Null-as-covered makes leaves uncovered so their parents take the camera (covering three levels), never camera a leaf. And since the root has no parent, if the recursion returns 0 for it, add one final camera. Greedy 'cameras as high as possible' is provably optimal here.

INVARIANT · BINARY TREES · CONSTRUCTION & TREE DP · DECK 3 OF 3
45 / RECALL RETRIEVAL, NOT RECOGNITION

NAME THE TECHNIQUE FROM THE STATEMENT

DRILL 01 · TRANSFER

Counting nodes in a COMPLETE binary tree in O(log²n): what shortcut avoids visiting every node?

Equal left/right heights ⇒ a perfect subtree, counted by formula. A complete tree is perfect except possibly the last level. If the leftmost and rightmost spines are the same length, every level is full: 2ʰ − 1 nodes, no traversal. Only along the one 'ragged' path do heights differ and you recurse, giving O(log n) recursions each doing an O(log n) height check.

DRILL 02 · RECALL

Binary Tree Cameras uses a greedy postorder with three node states. What are they, and when do you place a camera?

Place a camera only when a child is uncovered. Nulls are treated as covered (return 1), so leaves are uncovered (0). You never waste a camera on a leaf; instead the leaf's parent gets one, covering the leaf, the parent and the grandparent. Greedily placing cameras as high as possible (at parents of uncovered nodes) is optimal. The three states encode exactly the information the parent needs.

INVARIANT · BINARY TREES · CONSTRUCTION & TREE DP · DECK 3 OF 3
46 / TRAPS PLAUSIBLE, WRONG, AND SILENT

SIX WAYS TO BE PLAUSIBLY WRONG

Finale bugs are quiet: an O(n²) construction from a linear root search, left-before-right in the postorder build, a forgotten Morris un-thread, missing null markers, a tree-DP tuple collapsed too early. Each returns a believable answer.

CONSTRUCTION: LINEAR SEARCH FOR THE ROOT ⇒ O(n²)

Scanning inorder to find the root's split point every call is O(n) per node. Build a value → inorder index hashmap once so the split is O(1), keeping the whole build O(n).

IN+POST: BUILDING THE LEFT SUBTREE BEFORE THE RIGHT

Consuming postorder from the back gives roots in root-right-left order, so you must build the right subtree before the left. Do it left-first (as in the pre+in version) and the tree comes out wrong.

COUNT COMPLETE NODES: FALLING BACK TO AN O(n) WALK

Just counting every node is O(n) and misses the point. Use the equal-heights shortcut to skip whole perfect subtrees; only the single ragged path costs recursion, O(log²n).

MORRIS: NOT REMOVING THE THREAD

Morris threads a node to its inorder predecessor to climb back up without a stack. If you don't detect and REMOVE that temporary right-link on the second visit, you corrupt the tree (and loop forever). The thread must be undone.

SERIALIZE: NO NULL MARKERS

Without explicit markers for null children, a string like 1,2,3 is ambiguous, many trees share it. Emit a sentinel (e.g. #) for every null so deserialize can rebuild the exact shape.

TREE DP: COLLAPSING THE TUPLE TOO EARLY

Returning max(rob, notRob) up the tree loses the information the parent needs (it must know the not-robbed value specifically). Return the full pair and only take the max at the very end (the root).

INVARIANT · BINARY TREES · CONSTRUCTION & TREE DP · DECK 3 OF 3
47 / CHEATSHEET REVISION SURFACE

ONE SCREEN, THE WHOLE DECK

Eight techniques, one page. The right column is the phrase that should trigger each, the night-before surface for construction, threading and tree DP.

ALGORITHM
TIME
SPACE
REACH FOR IT WHEN
Count complete nodes
O(log²n)
O(log n)
equal left/right height ⇒ 2^h − 1; else recurse
Build from pre + in
O(n)
O(n)
preorder gives root; inorder index map splits left|right
Build from in + post
O(n)
O(n)
postorder (from back) gives root; build RIGHT then left
Serialize / deserialize
O(n)
O(n)
preorder (or BFS) with # null markers; consume tokens
Morris inorder
O(n)
O(1)
thread to predecessor, visit, then undo the thread
Flatten to list
O(n)
O(1)
attach right subtree to the left subtree's rightmost, move left→right
House Robber III
O(n)
O(h)
return (rob, notRob); rob = val + notRob(children)
Binary Tree Cameras
O(n)
O(h)
3-state greedy; camera when a child is uncovered
INVARIANT · BINARY TREES · CONSTRUCTION & TREE DP · DECK 3 OF 3
48 / CLOSE STEP 13 · DECK 3 OF 3

BUILD THE TREE

Structure gave you O(log²n) counting and O(1)-space flatten; running traversals backwards reconstructed and serialized the exact tree; Morris threaded a walk into O(1) space; and tree DP folded the whole tree up in one postorder. That completes Binary Trees across three decks. Next on the sheet: Binary Search Trees, where inorder's sorted property does most of the work.

00%
OF THIS DECK SOLVED
← ALL TOPICS← DECK 1 · TRAVERSALS← DECK 2 · PROPERTIES

Deck 3 of 3. Uses L32, L34-L38. L33 (unique-tree theory) folded into the construction intro. Morris is a concept unit. Added at the user's request: House Robber III, Binary Tree Cameras (the canonical tree DPs). BST (L39-L53) is a separate Step-14 deck.

INVARIANT · BINARY TREES · CONSTRUCTION & TREE DP · DECK 3 OF 3
■ TOO SMALL TO READ
1280×720
INVARIANT · STEP 13 · DECK 3 OF 3

This one needs a laptop

Not a preference — an arithmetic one. Every slide is a fixed 1280 × 720 stage: a graph animating beside the code that drives it, with the problems laid out two columns wide. It scales as a single piece, so on a screen this size the body text comes out around 4px tall.

Shrinking it further would not help, and rebuilding it to reflow would mean losing the thing that makes it worth reading.

YOUR SCREEN0 × 0
NEEDED1060 × 610
WHAT IS WAITING ON THE LAPTOP
WATCH IT RUN, THEN RUN IT FROM MEMORY
Your progress is saved per device, so anything you tick on the laptop will be waiting there.