Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
147 changes: 147 additions & 0 deletions algorithms/BinarySearch.typ
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
#import "../lib/style.typ": *
#import "../lib/mapcode.typ": *

== Binary Search
#set math.equation(numbering: none)

Search for a target value in a sorted array and return its index. If the target is not found, return -1.

Formal definition:
$
"bs"("low", "high") = cases(
-1 & "if " "low" > "high",
"mid" & "if " A["mid"] = "target",
"bs"("low", "mid"-1) & "if " A["mid"] > "target",
"bs"("mid"+1, "high") & "if " A["mid"] < "target"
)
$

where $"mid" = floor(("low" + "high")/2)$

Examples:
- $A = [1, 3, 5, 7, 9]$, target $= 5 -> 2$
- $A = [1, 3, 5, 7, 9]$, target $= 6 -> -1$

*As mapcode:*

_primitives_: `comparison` ($<$, $=$), `arithmetic` ($+$, $-$, $floor(dot / 2)$) are strict. i.e., operations on $bot$ are undefined.

$
I = (A: ZZ^*, "target": ZZ) quad quad quad
X &= (NN times NN) -> (ZZ union {-1} union {bot}) quad quad quad
A = ZZ union {-1}\
rho(A, "target") &= {("low", "high") -> bot | "low" in {0 dots n}, "high" in {-1 dots n-1}}\
F(x_(l,h)) &= cases(
-1 & "if " l > h,
m & "if " A[m] = "target",
x_(l, m-1) & "if " A[m] > "target",
x_(m+1, h) & "if " A[m] < "target"
) quad "where " m = floor((l + h)/2)\
pi(x) &= x_(0, n-1) quad "where " n = |A|
$

#let inst_arr = (1, 3, 5, 7, 9, 11, 13, 15, 17, 19);
#let inst_target = 13;
#let inst_n = inst_arr.len();

#figure(
caption: [Binary Search computation using mapcode for $A = #inst_arr$ and target $= #inst_target$],
$
#{
let rho = ((arr, target)) => {
let n = arr.len()
let x = (:)
for low in range(0, n + 1) {
for high in range(low - 1, n) {
let key = "(" + str(low) + "," + str(high) + ")"
x.insert(key, none)
}
}
x
}

let F_key = ((arr, target)) => (x) => (key) => {
// Parse key string like "(0, 9)" back to (low, high)
let parts = key.trim("(").trim(")").split(",")
let low = int(parts.at(0).trim())
let high = int(parts.at(1).trim())

if low > high {
-1
} else {
let mid = calc.floor((low + high) / 2)
if arr.at(mid) == target {
mid
} else if arr.at(mid) > target {
let dep_key = "(" + str(low) + "," + str(mid - 1) + ")"
if dep_key in x and x.at(dep_key) != none {
x.at(dep_key)
} else {
none
}
} else {
let dep_key = "(" + str(mid + 1) + "," + str(high) + ")"
if dep_key in x and x.at(dep_key) != none {
x.at(dep_key)
} else {
none
}
}
}
}

let F = ((arr, target)) => (x) => {
let x_new = (:)
for (key, val) in x {
if val != none {
x_new.insert(key, val)
} else {
x_new.insert(key, F_key((arr, target))(x)(key))
}
}
x_new
}

let pi = ((arr, target)) => (x) => {
let n = arr.len()
if n == 0 { return -1 }
let key = "(" + str(0) + "," + str(n - 1) + ")"
let result = x.at(key)
if result == none { -1 } else { result }
}

let X_h = (x, diff_mask: none) => {
// Show only key ranges for visualization
let entries = x.pairs().sorted(key: ((k, v)) => k)
let cells = ()

// Show important ranges
let important_keys = ("(0," + str(inst_n - 1) + ")", "(0,4)", "(5,9)", "(6,9)", "(6,7)", "(6,6)")
for key in important_keys {
if key in x {
let val = if x.at(key) != none {[$#x.at(key)$]} else {[$bot$]}
cells.push(table.cell([$#key$]))
cells.push(table.cell([$-> #val$]))
}
}

table(
columns: 2,
align: (right, left),
stroke: none,
inset: 3pt,
..cells
)
}

mapcode-viz(
rho, F((inst_arr, inst_target)), pi((inst_arr, inst_target)),
X_h: X_h,
pi_name: [$mpi$],
group-size: 3,
cell-size: 50mm, scale-fig: 80%
)((inst_arr, inst_target))
}
$
)

144 changes: 144 additions & 0 deletions algorithms/InorderTraversal.typ
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
#import "../lib/style.typ": *
#import "../lib/mapcode.typ": *

== Inorder Traversal of Binary Tree
#set math.equation(numbering: none)

Perform an inorder traversal of a binary tree, returning the sequence of node values visited in the order: left subtree, root, right subtree.

Formal definition:
$
"inorder"(i) = cases(
[v_i] & "if " i "is leaf",
"inorder"("left"(i)) + [v_i] + "inorder"("right"(i)) & "otherwise"
)
$

where $v_i$ is the value at node $i$, and $+$ denotes list concatenation.

Example:

```
4
/ \
2 6
/ \ / \
1 3 5 7
```

Result: $[1, 2, 3, 4, 5, 6, 7]$

*As mapcode:*

_primitives_: `list concatenation` ($+$) is strict. i.e., concatenation with $bot$ is undefined.

$
I = "Tree represented as array" [(v_i, l_i, r_i)]_i quad quad quad
X &= NN -> (ZZ^* union {bot}) quad quad quad
A = ZZ^*\
rho("Tree") &= {i -> bot | i in {0 dots |"Tree"|-1}}\
F(x_i) &= cases(
[v_i] & "if " l_i = "None" and r_i = "None",
x_(l_i) + [v_i] + x_(r_i) & "otherwise"
)\
pi(x) &= x_0 quad "(" "root's inorder sequence" ")"
$

where each tree node $i$ has value $v_i$, left child index $l_i$, and right child index $r_i$ (None if no child).

#let inst_tree = (
(4, 1, 2), // Node 0: value=4, left=1, right=2
(2, 3, 4), // Node 1: value=2, left=3, right=4
(6, 5, 6), // Node 2: value=6, left=5, right=6
(1, none, none), // Node 3: value=1, leaf
(3, none, none), // Node 4: value=3, leaf
(5, none, none), // Node 5: value=5, leaf
(7, none, none) // Node 6: value=7, leaf
);

#figure(
caption: [Inorder Traversal computation using mapcode for the binary tree shown above],
$
#{
let rho = (tree) => {
let x = ()
for i in range(0, tree.len()) {
x.push(none)
}
x
}

let F_i = (tree) => (x) => ((i,)) => {
let node = tree.at(i)
let val = node.at(0)
let left_idx = node.at(1)
let right_idx = node.at(2)

// Leaf node
if left_idx == none and right_idx == none {
(val,)
} else {
let left_list = if left_idx != none { x.at(left_idx) } else { () }
let right_list = if right_idx != none { x.at(right_idx) } else { () }

// Check if dependencies are resolved
if left_idx != none and left_list == none {
return none
}
if right_idx != none and right_list == none {
return none
}

// Concatenate: left + [val] + right
let result = ()
if left_list != none {
result = result + left_list
}
result = result + (val,)
if right_list != none {
result = result + right_list
}
result
}
}

let F = (tree) => map_tensor(F_i(tree), dim: 1)

let pi = (tree) => (x) => x.at(0)

let X_h = (x, diff_mask: none) => {
let cells = ()
for i in range(0, x.len()) {
let val = x.at(i)
let node_val = inst_tree.at(i).at(0)

let content = if val != none {
$[#val.map(v => str(v)).join(", ")]$
} else {
$bot$
}

if diff_mask != none and diff_mask.at(i) {
cells.push(rect(fill: yellow.transparentize(70%), inset: 3pt)[
#text(size: 8pt)[$"node"_#node_val$]: #content
])
} else {
cells.push(rect(stroke: none, inset: 3pt)[
#text(size: 8pt)[$"node"_#node_val$]: #content
])
}
}
stack(dir: ttb, spacing: 3pt, ..cells)
}

mapcode-viz(
rho, F(inst_tree), pi(inst_tree),
X_h: X_h,
pi_name: [$mpi$],
group-size: 4,
cell-size: 35mm, scale-fig: 70%
)(inst_tree)
}
$
)

4 changes: 4 additions & 0 deletions main.typ
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,8 @@ All primitives are _strict_ meaning they do not allow for undefined values (i.e.
#pagebreak()
#include "algorithms/LongestCommonSubsequence.typ"
#pagebreak()
#include "algorithms/BinarySearch.typ"
#pagebreak()
#include "algorithms/InorderTraversal.typ"
#pagebreak()
#include "algorithms/leetcode/P2_add-two-numbers.typ"