Starter Types
Use these types and sample values for the exercises:
type ListNode = { value: string; next?: ListNode };
const c: ListNode = { value: "hit" };
const b: ListNode = { value: "miss", next: c };
const a: ListNode = { value: "hit", next: b };
type TreeNode = { value: number; left?: TreeNode; right?: TreeNode };
const sampleTree: TreeNode = {
value: 4,
left: {
value: 3,
left: { value: 2 },
},
right: { value: 8 },
};Programming Challenge
Complete the helper groups below. Predict the expected outputs before running checks.
Exercise 1: Review Chain Recursion
Complete the helpers. For each one, write the base case before the recursive case.
function countNodes(node: ListNode | undefined): number {
// TODO: empty chain
// TODO: current node plus the smaller chain
}
function contains(node: ListNode | undefined, target: string): boolean {
// TODO: empty chain
// TODO: match at this node
// TODO: search the smaller chain
}
function countMatches(node: ListNode | undefined, target: string): number {
// TODO: empty chain
const here = node.value === target ? 1 : 0;
// TODO: combine here with the smaller answer
}Expected checks:
countNodes(undefined) === 0;
countNodes(c) === 1;
countNodes(a) === 3;
contains(a, "hit") === true;
contains(a, "miss") === true;
contains(a, "coin") === false;
countMatches(a, "hit") === 2;
countMatches(a, "coin") === 0;Exercise 2: Factorial With Invalid Input
Complete factorial. The function should return undefined for negative input.
function factorial(num: number): number | undefined {
// TODO: invalid input
// TODO: base case for 0!
const next = factorial(num - 1);
// TODO: handle undefined before multiplying
// TODO: combine num with the smaller answer
}Expected checks:
factorial(-1) === undefined;
factorial(0) === 1;
factorial(4) === 24;Exercise 3: Tree Count And Sum
Each missing child is a base case with value 0.
function countTreeNodes(root: TreeNode | undefined): number {
// TODO: missing tree
// TODO: current node plus left branch plus right branch
}
function sumTreeNodes(root: TreeNode | undefined): number {
// TODO: missing tree
// TODO: current value plus left sum plus right sum
}Expected checks:
countTreeNodes(undefined) === 0;
countTreeNodes(sampleTree) === 4;
sumTreeNodes(undefined) === 0;
sumTreeNodes(sampleTree) === 17;Exercise 4: Sum Even Tree Nodes
Use % to test whether the current value is even. Still recurse into both branches whether the current value is even or odd.
function sumEvenTreeNodes(root: TreeNode | undefined): number {
// TODO: missing tree
// TODO: sum the left and right branches
// TODO: include root.value only when it is even
}Expected checks:
sumEvenTreeNodes(undefined) === 0;
sumEvenTreeNodes({ value: 3 }) === 0;
sumEvenTreeNodes(sampleTree) === 14;Predict Before You Run
Before running checks, answer:
- Which functions make one recursive call?
- Which functions make two recursive calls?
- Why does
factorialneed the return typenumber | undefined? - In
sumEvenTreeNodes, why do odd nodes still need recursive calls?
Extra Practice Bank
getAt(node: ListNode | undefined, index: number): string | undefined: return the value at the index, orundefinedwhen the index runs past the end.reverseList(node: ListNode | undefined): ListNode | undefined: return a new reversed chain. Do not mutate the original chain.preorder(root: TreeNode | undefined): number[]: visit current value, then left branch, then right branch.inorder(root: TreeNode | undefined): number[]: visit left branch, then current value, then right branch.postorder(root: TreeNode | undefined): number[]: visit left branch, then right branch, then current value.treeHeight(root: TreeNode | undefined): number: return0for an empty tree and1 + max(leftHeight, rightHeight)otherwise.treeContains(root: TreeNode | undefined, target: number): boolean: check the current value, then search left or right branches.treeMaxValue(root: TreeNode | undefined): number | undefined: returnundefinedfor an empty tree, then combine current, left max, and right max.countTreeNodesvariation: write expected outputs for an empty tree, a one-node tree, and an uneven tree before coding.
Common Bugs
- Forgetting the empty-chain or empty-tree base case.
- Calling the same node again instead of
node.next,root.left, orroot.right. - Forgetting one tree branch in the combine step.
- Multiplying by the result of
factorial(num - 1)before checking whether it isundefined. - Skipping odd nodes' branches in
sumEvenTreeNodes.
Bonus Puzzles
- Write
containsTree(root, target)for numbers. - Write
maxTreeValue(root)and decide what it should return for an empty tree. - Ask AI for a wrong
sumEvenTreeNodesimplementation and identify the bug.