Skip to content
Merged
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
9 changes: 9 additions & 0 deletions .github/workflows/main.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,15 @@ jobs:

- run: python3 -m pip install -r requirements.txt

- uses: actions/setup-node@v4
with:
node-version: '24'
cache: 'npm'

- run: npm ci

- run: npm run format:check

- run: python3 scripts/check_math.py

- run: mkdocs build --strict
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
.DS_Store
site/
venv/
node_modules/
5 changes: 4 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,18 @@ Solutions to _Introduction to Algorithms_, Third Edition, published at https://w
- Literal braces are `\\{` and `\\}`.
- `_` and `*` inside `$...$` sometimes need `\_` and `\*` when Markdown turns them into emphasis.
- GitHub's preview applies the same Markdown escaping inside math, so this source renders there too, apart from occasional quirks in `$$` blocks.
- List items use `- `, and anything continuing an item is indented 4 spaces; MkDocs drops 2-space continuations out of the list. `npm run format` (Prettier with math masked out) enforces this.
- Inside a list item, GitHub ignores `$$`. Use a ` ```math ` fence there instead, with plain LaTeX (`\\`, `\{`), since fenced content skips Markdown.

## Verify

```sh
pip install -r requirements.txt
npm ci
npm run format
python3 scripts/check_math.py
mkdocs build --strict
mkdocs serve
```

CI runs `scripts/check_math.py` and `mkdocs build --strict` on every pull request and deploys `main` to the `gh-pages` branch.
CI runs `npm run format:check`, `scripts/check_math.py`, and `mkdocs build --strict` on every pull request and deploys `main` to the `gh-pages` branch.
68 changes: 34 additions & 34 deletions docs/Chap02/2.3.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,22 +62,22 @@ MERGE(A, p, q, r)

- Base case

For $n = 2^1$, $T(n) = 2\lg 2 = 2$.
For $n = 2^1$, $T(n) = 2\lg 2 = 2$.

- Suppose $n = 2^k$, $T(n) = n\lg n = 2^k \lg 2^k = 2^kk$.

For $n = 2^{k + 1}$,
For $n = 2^{k + 1}$,

```math
\begin{aligned}
T(n) & = 2T(2^{k + 1} / 2) + 2^{k + 1} \\
& = 2T(2^k) + 2^{k + 1} \\
& = 2 \cdot 2^kk + 2^{k + 1} \\
& = 2^{k + 1}(k + 1) \\
& = 2^{k + 1} \lg 2^{k + 1} \\
& = n\lg n.
\end{aligned}
```
```math
\begin{aligned}
T(n) & = 2T(2^{k + 1} / 2) + 2^{k + 1} \\
& = 2T(2^k) + 2^{k + 1} \\
& = 2 \cdot 2^kk + 2^{k + 1} \\
& = 2^{k + 1}(k + 1) \\
& = 2^{k + 1} \lg 2^{k + 1} \\
& = n\lg n.
\end{aligned}
```

By P.M.I., $T(n) = n\lg n$, when $n$ is an exact power of $2$.

Expand All @@ -102,31 +102,31 @@ The solution of the recurrence is $\Theta(n^2)$.

- Iterative:

```cpp
ITERATIVE-BINARY-SEARCH(A, v, low, high)
while low ≤ high
mid = floor((low + high) / 2)
if v == A[mid]
return mid
else if v > A[mid]
low = mid + 1
else high = mid - 1
return NIL
```
```cpp
ITERATIVE-BINARY-SEARCH(A, v, low, high)
while low ≤ high
mid = floor((low + high) / 2)
if v == A[mid]
return mid
else if v > A[mid]
low = mid + 1
else high = mid - 1
return NIL
```

- Recursive:

```cpp
RECURSIVE-BINARY-SEARCH(A, v, low, high)
if low > high
return NIL
mid = floor((low + high) / 2)
if v == A[mid]
return mid
else if v > A[mid]
return RECURSIVE-BINARY-SEARCH(A, v, mid + 1, high)
else return RECURSIVE-BINARY-SEARCH(A, v, low, mid - 1)
```
```cpp
RECURSIVE-BINARY-SEARCH(A, v, low, high)
if low > high
return NIL
mid = floor((low + high) / 2)
if v == A[mid]
return mid
else if v > A[mid]
return RECURSIVE-BINARY-SEARCH(A, v, mid + 1, high)
else return RECURSIVE-BINARY-SEARCH(A, v, low, mid - 1)
```

Each time we do the comparison of $v$ with the middle element, the search range continues with range halved.

Expand Down
56 changes: 28 additions & 28 deletions docs/Chap04/4.3.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,37 +99,37 @@ To show $\Theta$ bound, separately show $O$ and $\Omega$ bounds.

- For $O(n\lg n)$, we guess $T(n) \le c(n - 2)\lg(n - 2) - 2c$,

```math
\begin{aligned}
T(n) & \le c(\lceil n / 2 \rceil -2 )\lg(\lceil n / 2 \rceil - 2) + c(\lfloor n / 2 \rfloor - 2)\lg(\lfloor n / 2 \rfloor - 2) + dn \\
& \le c(n / 2 + 1 - 2)\lg(n / 2 + 1 - 2) - 2c + c(n / 2 - 2)\lg(n / 2 - 2) - 2c + dn \\
& \le c(n / 2 - 1 )\lg(n / 2 - 1) + c(n / 2 - 1)\lg(n / 2 - 1) + dn \\
& = c\frac{n - 2}{2}\lg\frac{n - 2}{2} + c\frac{n - 2}{2}\lg\frac{n - 2}{2} - 4c + dn \\
& = c(n - 2)\lg\frac{n - 2}{2} - 4c + dn \\
& = c(n - 2)\lg(n - 2) - c(n - 2) - 4c + dn \\
& = c(n - 2)\lg(n - 2) + (d - c)n + 2c - 4c \\
& \le c(n - 2)\lg(n - 2) - 2c,
\end{aligned}
```

where the last step holds for $c > d$.
```math
\begin{aligned}
T(n) & \le c(\lceil n / 2 \rceil -2 )\lg(\lceil n / 2 \rceil - 2) + c(\lfloor n / 2 \rfloor - 2)\lg(\lfloor n / 2 \rfloor - 2) + dn \\
& \le c(n / 2 + 1 - 2)\lg(n / 2 + 1 - 2) - 2c + c(n / 2 - 2)\lg(n / 2 - 2) - 2c + dn \\
& \le c(n / 2 - 1 )\lg(n / 2 - 1) + c(n / 2 - 1)\lg(n / 2 - 1) + dn \\
& = c\frac{n - 2}{2}\lg\frac{n - 2}{2} + c\frac{n - 2}{2}\lg\frac{n - 2}{2} - 4c + dn \\
& = c(n - 2)\lg\frac{n - 2}{2} - 4c + dn \\
& = c(n - 2)\lg(n - 2) - c(n - 2) - 4c + dn \\
& = c(n - 2)\lg(n - 2) + (d - c)n + 2c - 4c \\
& \le c(n - 2)\lg(n - 2) - 2c,
\end{aligned}
```

where the last step holds for $c > d$.

- For $\Omega(n\lg n)$, we guess $T(n) \ge c(n + 2)\lg (n + 2) + 2c$,

```math
\begin{aligned}
T(n) & \ge c(\lceil n / 2 \rceil +2 )\lg(\lceil n / 2 \rceil + 2) + c(\lfloor n / 2 \rfloor + 2)\lg(\lfloor n / 2 \rfloor + 2) + dn \\
& \ge c(n / 2 + 2)\lg(n / 2 + 2) + 2c + c(n / 2 - 1 + 2)\lg(n / 2 - 1 + 2) + 2c + dn \\
& \ge c(n / 2 + 1)\lg(n / 2 + 1) + c(n / 2 + 1)\lg(n / 2 + 1) + 4c + dn \\
& \ge c\frac{n + 2}{2}\lg\frac{n + 2}{2} + c\frac{n + 2}{2}\lg\frac{n + 2}{2} + 4c + dn \\
& = c(n + 2)\lg\frac{n + 2}{2} + 4c + dn \\
& = c(n + 2)\lg(n + 2) - c(n + 2) + 4c + dn \\
& = c(n + 2)\lg(n + 2) + (d - c)n - 2c + 4c \\
& \ge c(n + 2)\lg(n + 2) + 2c,
\end{aligned}
```

where the last step holds for $d > c$.
```math
\begin{aligned}
T(n) & \ge c(\lceil n / 2 \rceil +2 )\lg(\lceil n / 2 \rceil + 2) + c(\lfloor n / 2 \rfloor + 2)\lg(\lfloor n / 2 \rfloor + 2) + dn \\
& \ge c(n / 2 + 2)\lg(n / 2 + 2) + 2c + c(n / 2 - 1 + 2)\lg(n / 2 - 1 + 2) + 2c + dn \\
& \ge c(n / 2 + 1)\lg(n / 2 + 1) + c(n / 2 + 1)\lg(n / 2 + 1) + 4c + dn \\
& \ge c\frac{n + 2}{2}\lg\frac{n + 2}{2} + c\frac{n + 2}{2}\lg\frac{n + 2}{2} + 4c + dn \\
& = c(n + 2)\lg\frac{n + 2}{2} + 4c + dn \\
& = c(n + 2)\lg(n + 2) - c(n + 2) + 4c + dn \\
& = c(n + 2)\lg(n + 2) + (d - c)n - 2c + 4c \\
& \ge c(n + 2)\lg(n + 2) + 2c,
\end{aligned}
```

where the last step holds for $d > c$.

## 4.3-6

Expand Down
2 changes: 1 addition & 1 deletion docs/Chap04/4.6.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

> Give a simple and exact expression for $n_j$ in equation $\text{(4.27)}$ for the case in which $b$ is a positive integer instead of an arbitrary real number.

We state that $\forall{j \ge 0}, n_j = \left \lceil \frac{n}{b^j} \right \rceil$.
We state that $\forall{j \ge 0}, n_j = \left \lceil \frac{n}{b^j} \right \rceil$.

Indeed, for $j = 0$ we have from the recurrence's base case that $n_0 = n = \left \lceil \frac{n}{b^0} \right \rceil$.
Now, suppose $n_{j - 1} = \left \lceil \frac{n}{b^{j - 1}} \right \rceil$ for some $j > 0$. By definition, $n_j = \left \lceil \frac{n_{j - 1}}{b} \right \rceil$.
Expand Down
2 changes: 1 addition & 1 deletion docs/Chap06/Problems/6-1.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,4 @@
- $\text{BUILD-MAX-HEAP}(A)$: $A = \langle 3, 2, 1 \rangle$.
- $\text{BUILD-MAX-HEAP}'(A)$: $A = \langle 3, 1, 2 \rangle$.

**b.** It is very easy to find out that the $\text{MAX-HEAP-INSERT}$ operation for each iteration takes $\Theta(\log i)$ time, therefore the total time complexity is the sum of the individual complexities for $i$ from $2$ to $n$, which is : $\Theta(\log 2) + \Theta(\log 3) + \dots + \Theta(\log n) = \Theta(\log n!) $. By using Stirling's approximation, $\Theta (\log n!) \approx \Theta(n\log n)$, so the overall complexity is $\Theta(n\log⁡ n)$.
**b.** It is very easy to find out that the $\text{MAX-HEAP-INSERT}$ operation for each iteration takes $\Theta(\log i)$ time, therefore the total time complexity is the sum of the individual complexities for $i$ from $2$ to $n$, which is : $\Theta(\log 2) + \Theta(\log 3) + \dots + \Theta(\log n) = \Theta(\log n!) $. By using Stirling's approximation, $\Theta (\log n!) \approx \Theta(n\log n)$, so the overall complexity is $\Theta(n\log⁡ n)$.
3 changes: 1 addition & 2 deletions docs/Chap07/Problems/7-4.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@

It is clear that the conditions starting the second iteration of the **while** loop in $\text{TAIL-RECURSIVE-QUICKSORT}$ are identical to the conditions starting the second recursive call in $\text{QUICKSORT}$. Therefore, $\text{TAIL-RECURSIVE-QUICKSORT}$ effectively performs the sort in the same manner as $\text{QUICKSORT}$. Therefore, $\text{TAIL-RECURSIVE-QUICKSORT}$ must correctly sort the array $A$.


**b.** The stack depth will be $\Theta(n)$ if the input array is already sorted. The right subarray will always have size $0$ so there will be $n − 1$ recursive calls before the **while**-condition $p < r$ is violated.

**c.**
Expand All @@ -36,4 +35,4 @@ MODIFIED-TAIL-RECURSIVE-QUICKSORT(A, p, r)
else
MODIFIED-TAIL-RECURSIVE-QUICKSORT(A, q + 1, r)
r = q - 1
```
```
2 changes: 1 addition & 1 deletion docs/Chap09/Problems/9-2.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@

**a.** Let $m_k$ be the number of $x_i$ smaller than $x_k$. When weights of $1 / n$ are assigned to each $x_i$, we have $\sum_{x_i < x_k} w_i = m_k / n$ and $\sum_{x_i > x_k} w_i = (n - m_k - 1) / 2$. The only value of $m_k$ which makes these sums $< 1 / 2$ and $\le 1 / 2$ respectively is when $\lceil n / 2 \rceil - 1$, and this value $x$ must be the median since it has equal numbers of $x_i's$ which are larger and smaller than it.

**b.** First use mergesort to sort the $x_i$'s by value in $O(n\lg n)$ time. Let $S_i$ be the sum of the weights of the first $i$ elements of this sorted array and note that it is $O(1)$ to update $S_i$. Compute $S_1, S_2, \dots$ until you reach $k$ such that $S_{k − 1} < 1 / 2$ and $S_k \ge 1 / 2$. The weighted median is $x_k$.
**b.** First use mergesort to sort the $x_i$'s by value in $O(n\lg n)$ time. Let $S_i$ be the sum of the weights of the first $i$ elements of this sorted array and note that it is $O(1)$ to update $S_i$. Compute $S_1, S_2, \dots$ until you reach $k$ such that $S_{k − 1} < 1 / 2$ and $S_k \ge 1 / 2$. The weighted median is $x_k$.

**c.** We modify $\text{SELECT}$ to do this in linear time. Let $x$ be the median of medians. Compute $\sum_{x_i < x} w_i$ and $\sum_{x_i > x} w_i$ and check if either of these is larger than $1 / 2$. If not, stop. If so, recurse on the collection of smaller or larger elements known to contain the weighted median. This doesn't change the runtime, so it is $\Theta(n)$.

Expand Down
2 changes: 1 addition & 1 deletion docs/Chap10/10.4.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ PRINT-KEY(T)
else
if x.right
x = x.right
else
else
x = x.parent
else if prev == x.left and x.right != NIL
prev = x
Expand Down
2 changes: 1 addition & 1 deletion docs/Chap11/11.4.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ HASH-INSERT(T, k)
- successful: $\frac{1}{\frac{3}{4}} \ln\frac{1}{1-\frac{3}{4}} \approx 1.848$ probes.

- $\alpha = 7 / 8$,

- unsuccessful: $\frac{1}{1 - \frac{7}{8}} = 8$ probes,
- successful: $\frac{1}{\frac{7}{8}} \ln\frac{1}{1 - \frac{7}{8}} \approx 2.377$ probes.

Expand Down
10 changes: 5 additions & 5 deletions docs/Chap12/12.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,23 @@

- $height = 2$:

![](../img/12.1-1-1.png)
![](../img/12.1-1-1.png)

- $height = 3$:

![](../img/12.1-1-2.png)
![](../img/12.1-1-2.png)

- $height = 4$:

![](../img/12.1-1-3.png)
![](../img/12.1-1-3.png)

- $height = 5$:

![](../img/12.1-1-4.png)
![](../img/12.1-1-4.png)

- $height = 6$:

![](../img/12.1-1-5.png)
![](../img/12.1-1-5.png)

## 12.1-2

Expand Down
8 changes: 4 additions & 4 deletions docs/Chap13/13.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,19 @@

- Complete binary tree of $height = 3$:

![](../img/13.1-1-1.png)
![](../img/13.1-1-1.png)

- Red-black tree of $black\text-heights = 2$:

![](../img/13.1-1-2.png)
![](../img/13.1-1-2.png)

- Red-black tree of $black\text-heights = 3$:

![](../img/13.1-1-3.png)
![](../img/13.1-1-3.png)

- Red-black tree of $black\text-heights = 4$:

![](../img/13.1-1-4.png)
![](../img/13.1-1-4.png)

## 13.1-2

Expand Down
12 changes: 6 additions & 6 deletions docs/Chap13/13.3.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,27 +10,27 @@ If we chose to set the color of $z$ to black then we would be violating property

- insert $41$:

![](../img/13.3-2-1.png)
![](../img/13.3-2-1.png)

- insert $38$:

![](../img/13.3-2-2.png)
![](../img/13.3-2-2.png)

- insert $31$:

![](../img/13.3-2-3.png)
![](../img/13.3-2-3.png)

- insert $12$:

![](../img/13.3-2-4.png)
![](../img/13.3-2-4.png)

- insert $19$:

![](../img/13.3-2-5.png)
![](../img/13.3-2-5.png)

- insert $8$:

![](../img/13.3-2-6.png)
![](../img/13.3-2-6.png)

## 13.3-3

Expand Down
2 changes: 1 addition & 1 deletion docs/Chap15/15.3.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ Consider the treatment of subproblems by each approach:

> Draw the recursion tree for the $\text{MERGE-SORT}$ procedure from Section 2.3.1 on an array of $16$ elements. Explain why memoization fails to speed up a good divide-and-conquer algorithm such as $\text{MERGE-SORT}$.

Draw a recursion tree.
Draw a recursion tree.
![](../img/15.3-2.png)
The $\text{MERGE-SORT}$ procedure performs at most a single call to any pair of indices of the array that is being sorted. In other words, the subproblems do not overlap and therefore memoization will not improve the running time.

Expand Down
Binary file modified docs/Chap15/Problems/15-9.md
Binary file not shown.
4 changes: 2 additions & 2 deletions docs/Chap22/22.2.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ $$

The textbook introduces the $\text{GRAY}$ color for the pedagogical purpose to distinguish between the $\text{GRAY}$ nodes (which are enqueued) and the $\text{BLACK}$ nodes (which are dequeued).

Therefore, it suffices to use a single bit to store each vertex color.
Therefore, it suffices to use a single bit to store each vertex color.

## 22.2-4

Expand All @@ -42,7 +42,7 @@ The time of iterating all edges becomes $O(V^2)$ from $O(E)$. Therefore, the run

> Argue that in a breadth-first search, the value $u.d$ assigned to a vertex $u$ is independent of the order in which the vertices appear in each adjacency list. Using Figure 22.3 as an example, show that the breadth-first tree computed by $\text{BFS}$ can depend on the ordering within adjacency lists.

First, we will show that the value $d$ assigned to a vertex is independent of the order that entries appear in adjacency lists. To show this, we rely on theorem 22.5, which proves correctness of $\text{BFS}$. In particular, the theorem states that $v.d = \delta(s, v)$ at the termination of $\text{BFS}$. Since $\delta(s, v)$ is a property of the underlying graph, for any adjacency list representation of the graph (including any reordering of the adjacency lists), $\delta(s, v)$ will not change. Since the $d$ values are equal to $\delta(s, v)$ and $\delta(s, v)$ is invariant for any ordering of the adjacency list, $d$ is also not dependent of the ordering of the adjacency list.
First, we will show that the value $d$ assigned to a vertex is independent of the order that entries appear in adjacency lists. To show this, we rely on theorem 22.5, which proves correctness of $\text{BFS}$. In particular, the theorem states that $v.d = \delta(s, v)$ at the termination of $\text{BFS}$. Since $\delta(s, v)$ is a property of the underlying graph, for any adjacency list representation of the graph (including any reordering of the adjacency lists), $\delta(s, v)$ will not change. Since the $d$ values are equal to $\delta(s, v)$ and $\delta(s, v)$ is invariant for any ordering of the adjacency list, $d$ is also not dependent of the ordering of the adjacency list.

Now, to show that $\pi$ does depend on the ordering of the adjacency lists, we will be using Figure 22.3 as a guide.

Expand Down
2 changes: 1 addition & 1 deletion docs/Chap27/Problems/27-1.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ SUM-ARRAYS(A, B, C)
**c.** Let $g$ be the grainsize. The runtime of the function that spawns all the other functions is $\left\lceil \frac{n}{g} \right\rceil$. The runtime of any particular spawned task is $g$. So, we want to minimize

$$\frac{n}{g} + g.$$

To do this we pull out our freshman calculus hat and take a derivative, we have

$$0 = 1 − \frac{n}{g^2}.$$
Expand Down
Loading
Loading