Showing preview only (3,059K chars total). Download the full file or copy to clipboard to get everything.
Repository: claytonjwong/Algorithms-Illuminated
Branch: main
Commit: 7874d6752bef
Files: 174
Total size: 85.3 MB
Directory structure:
gitextract_gtzmg6or/
├── .gitignore
├── Algorithms-Illuminated/
│ ├── .gitignore
│ ├── Algorithms-Illuminated.iml
│ ├── Cargo.toml
│ ├── README.md
│ ├── src/
│ │ └── main.rs
│ └── target/
│ ├── .rustc_info.json
│ └── CACHEDIR.TAG
├── README.md
├── _challenge_problems/
│ ├── index_equal_element.py
│ └── unimodal_array.py
├── bellman_ford/
│ ├── CMakeLists.txt
│ ├── main.cpp
│ ├── main.js
│ ├── main.kt
│ ├── main.py
│ ├── package.json
│ └── test.txt
├── closest_pair/
│ ├── naive.py
│ └── recursive.py
├── dijkstra/
│ ├── CMakeLists.txt
│ ├── main.cpp
│ ├── main.js
│ ├── main.kt
│ ├── main.py
│ ├── package.json
│ ├── problem9.8.txt
│ └── problem9.8test.txt
├── floyd_warshall/
│ ├── CMakeLists.txt
│ ├── floyd_warshall
│ ├── main.cpp
│ ├── main.js
│ ├── main.kt
│ ├── main.py
│ ├── package.json
│ ├── problem18.8file1.txt
│ ├── problem18.8file2.txt
│ ├── problem18.8file3.txt
│ ├── problem18.8file4.txt
│ ├── problem18.8test1.txt
│ └── problem18.8test2.txt
├── greedy_scheduling/
│ ├── CMakeLists.txt
│ ├── main.cpp
│ ├── main.js
│ ├── main.kt
│ ├── main.py
│ ├── package.json
│ ├── problem13.4.txt
│ ├── problem13.4test1.txt
│ └── problem13.4test2.txt
├── huffman/
│ ├── CMakeLists.txt
│ ├── main.cpp
│ ├── main.js
│ ├── main.kt
│ ├── main.py
│ ├── package.json
│ ├── problem14.6.txt
│ ├── problem14.6test1.txt
│ └── problem14.6test2.txt
├── karatsuba/
│ ├── main.jl
│ └── main.py
├── knapsack/
│ ├── CMakeLists.txt
│ ├── main.cpp
│ ├── main.js
│ ├── main.kt
│ ├── main.py
│ ├── package.json
│ ├── problem16.7.txt
│ └── problem16.7test.txt
├── kosaraju/
│ ├── CMakeLists.txt
│ ├── main.cpp
│ ├── main.js
│ ├── main.kt
│ ├── main.py
│ ├── package.json
│ ├── problem8.10.txt
│ ├── problem8.10test1.txt
│ ├── problem8.10test2.txt
│ ├── problem8.10test3.txt
│ ├── problem8.10test4.txt
│ ├── problem8.10test5.txt
│ └── section8.6.5page64.txt
├── kotlin/
│ ├── dijkstra.kt
│ ├── greedy_scheduling.kt
│ ├── huffman.kt
│ ├── knapsack.kt
│ ├── kosaraju.kt
│ ├── kruskal.kt
│ ├── merge_sort.kt
│ ├── merge_sort_inversions.kt
│ ├── prim.kt
│ ├── quick_sort.kt
│ ├── rselect.kt
│ ├── topo_sort.kt
│ └── weighted_independent_set.kt
├── kruskal/
│ ├── CMakeLists.txt
│ ├── main.cpp
│ ├── main.js
│ ├── main.kt
│ ├── main.py
│ ├── package.json
│ ├── problem15.9.txt
│ └── problem15.9test.txt
├── matrix_multiplication/
│ ├── main.jl
│ └── main.py
├── merge_sort/
│ ├── CMakeLists.txt
│ ├── merge_sort.cpp
│ ├── merge_sort.jl
│ ├── merge_sort.js
│ ├── merge_sort.kt
│ └── merge_sort.py
├── merge_sort_inversions/
│ ├── CMakeLists.txt
│ ├── merge_sort_inversions.cpp
│ ├── merge_sort_inversions.jl
│ ├── merge_sort_inversions.js
│ ├── merge_sort_inversions.kt
│ ├── merge_sort_inversions.py
│ ├── problem3.5.txt
│ └── problem3.5test.txt
├── prim/
│ ├── CMakeLists.txt
│ ├── main.cpp
│ ├── main.js
│ ├── main.kt
│ ├── main.py
│ ├── package.json
│ ├── problem15.9.txt
│ └── problem15.9test.txt
├── quick_sort/
│ ├── CMakeLists.txt
│ ├── main.cpp
│ ├── main.js
│ ├── main.kt
│ ├── main.py
│ ├── package.json
│ ├── problem5.6.txt
│ ├── problem5.6test1.txt
│ └── problem5.6test2.txt
├── rec_int_mult/
│ └── main.jl
├── rec_mat_mult/
│ ├── main.py
│ └── pyproject.toml
├── rselect/
│ ├── CMakeLists.txt
│ ├── main.cpp
│ ├── main.js
│ ├── main.kt
│ ├── main.py
│ ├── package.json
│ ├── problem6.5test1.txt
│ └── problem6.5test2.txt
├── strassen/
│ ├── main.py
│ ├── pyproject.toml
│ └── variants.py
├── topo_sort/
│ ├── CMakeLists.txt
│ ├── main.cpp
│ ├── main.js
│ ├── main.kt
│ └── main.py
├── traveling_salesman/
│ ├── README.md
│ ├── main.cpp
│ ├── main.js
│ ├── main.kt
│ ├── main.py
│ ├── package.json
│ ├── quiz19.2.txt
│ └── quiz20.7.txt
├── traveling_salesman_nearest_neighbor/
│ ├── README.md
│ ├── input.txt
│ └── main.py
└── weighted_independent_set/
├── CMakeLists.txt
├── main.cpp
├── main.js
├── main.kt
├── main.py
├── package.json
├── problem16.6.txt
└── problem16.6test.txt
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
# IDE
.idea
.vscode
.python-version
# Prerequisites
*.d
# Compiled Object files
*.slo
*.lo
*.o
*.obj
# Precompiled Headers
*.gch
*.pch
# Compiled Dynamic libraries
*.so
*.dylib
*.dll
# Fortran module files
*.mod
*.smod
# Compiled Static libraries
*.lai
*.la
*.a
*.lib
# Executables
*.exe
*.out
*.app
*.jar
cmake-build-debug
# Dependencies
node_modules
# rust
target/debug
target/package
# C++
main
main.dSYM
================================================
FILE: Algorithms-Illuminated/.gitignore
================================================
# IDE
.idea
.vscode
.python-version
# Prerequisites
*.d
# Compiled Object files
*.slo
*.lo
*.o
*.obj
# Precompiled Headers
*.gch
*.pch
# Compiled Dynamic libraries
*.so
*.dylib
*.dll
# Fortran module files
*.mod
*.smod
# Compiled Static libraries
*.lai
*.la
*.a
*.lib
# Executables
*.exe
*.out
*.app
*.jar
cmake-build-debug
# Dependencies
node_modules
# rust
target/debug
target/package
================================================
FILE: Algorithms-Illuminated/Algorithms-Illuminated.iml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<module type="RUST_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
<excludeFolder url="file://$MODULE_DIR$/target" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>
================================================
FILE: Algorithms-Illuminated/Cargo.toml
================================================
[package]
name = "Algorithms-Illuminated"
version = "0.1.1"
edition = "2021"
description = "https://github.com/claytonjwong/Algorithms-Illuminated"
license = "MIT"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
================================================
FILE: Algorithms-Illuminated/README.md
================================================
# Algorithms Illuminated
* [algorithmsilluminated.org](https://www.algorithmsilluminated.org/)
Coincidentally, my [algorithm learning journey](https://github.com/claytonjwong/Algorithms) which began in 2017 has occurred in parallel with the publication of Tim Roughgarden's (TR) 4-book series about algorithms and data structures. Over these years, I've purchased, studied, and provided feedback on TR's books. I was totally stoked when TR sent me a free copy of his 4th book for review before publication in 2020! I'm amazed by what can be done in near-linear time (ie. the amount of time to perform an algorithm is on the order of time to simply read the input), and it's awesome we can leverage these "for-free primitives" based upon computationally tractable problems as "building blocks" towards more complex solutions to computationally intractable (NP-Hard) problems via selective compromise on generality, correctness, and speed (ie. pick 2 of 3). [`💡` Can we do better?](https://en.wikipedia.org/wiki/Millennium_Prize_Problems#P_versus_NP)
<p>
<img src="images/TR1.png" height="256" width="400" />
<img src="images/TR2.png" height="256" width="400" />
</p>
---
# Part 1: The Basics
<br/>
<a href="https://www.amazon.com/dp/0999282905" target="_blank">
<img src="images/ai1large.jpg" />
</a>
---
### Merge Sort
<details><summary>📚 Lectures</summary>
<br/>
* [MergeSort: Motivation and Example](https://www.youtube.com/watch?v=kiyRJ7GVWro&list=PLEGCF-WLh2RLHqXx6-GZr_w7LgqKDXxN_&index=4) (Section 1.4, part 1)
* [MergeSort: Pseudocode](https://www.youtube.com/watch?v=rBd5w0rQaFo&list=PLEGCF-WLh2RLHqXx6-GZr_w7LgqKDXxN_&index=5) (Section 1.4, part 2)
* [MergeSort: Analysis](https://www.youtube.com/watch?v=8ArtRiTkYEw&list=PLEGCF-WLh2RLHqXx6-GZr_w7LgqKDXxN_&index=6) (Section 1.5)
</details>
<details><summary>🎯 Solutions</summary>
<br/>
*Kotlin*
```java
fun sort(A: IntArray): IntArray {
fun merge(A: IntArray, B: IntArray): IntArray {
var C = mutableListOf<Int>()
var i = 0
var j = 0
while (i < A.size && j < B.size)
if (A[i] < B[j])
C.add(A[i++])
else
C.add(B[j++])
A.slice(i..A.lastIndex).forEach { C.add(it) }
B.slice(j..B.lastIndex).forEach { C.add(it) }
return C.toIntArray()
}
fun go(A: IntArray): IntArray {
var N = A.size
if (N < 2)
return A
var half = Math.floor(N / 2.0).toInt()
var first = go(A.slice(0 until half).toIntArray())
var second = go(A.slice(half until N).toIntArray())
return merge(first, second)
}
return go(A)
}
fun main(args: Array<String>) {
sort(intArrayOf(5,3,8,9,1,7,0,2,6,4)).forEach { print("$it ") } // 0 1 2 3 4 5 6 7 8 9
println()
}
```
*Javascript*
```javascript
let sort = A => {
let go = A => {
let N = A.length;
if (N < 2)
return A;
let half = Math.floor(N / 2);
let first = go([...A.slice(0, half)]),
second = go([...A.slice(half, N)]);
return merge(first, second);
};
let merge = (A, B, C = []) => {
let M = A.length,
N = B.length;
let i = 0,
j = 0;
while (i < M && j < N)
C.push(A[i] < B[j] ? A[i++] : B[j++]);
C.push(...A.slice(i, M));
C.push(...B.slice(j, N));
return C;
};
return go(A);
};
console.log(sort([5,3,8,9,1,7,0,2,6,4])); // (10) [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
```
*Python3*
```python
from math import floor
def sort(A):
def go(A):
N = len(A)
if N < 2:
return A
half = floor(N / 2)
first = go(A[:half])
second = go(A[half:])
return merge(first, second)
def merge(A, B):
C = []
i = 0
j = 0
while i < len(A) and j < len(B):
if A[i] < B[j]:
C.append(A[i]); i += 1
else:
C.append(B[j]); j += 1
C.extend(A[i:])
C.extend(B[j:])
return C
return go(A)
print(sort([5,3,8,9,1,7,0,2,6,4])) # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
```
*C++*
```cpp
#include <iostream>
#include <vector>
using namespace std;
class Solution {
public:
using VI = vector<int>;
VI mergesort(VI& A) {
return go(move(A));
}
private:
VI go(VI&& A) {
auto N = A.size();
if( N < 2 )
return A;
auto half = A.begin() + (N / 2);
auto first = go({ A.begin(), half }),
second = go({ half, A.end() });
return merge(first, second);
}
VI merge(VI& A, VI& B, VI C = {}) {
auto i{ 0 },
j{ 0 };
while (i < A.size() && j < B.size())
C.push_back(A[i] < B[j] ? A[i++] : B[j++]);
C.insert(C.end(), A.begin() + i, A.end());
C.insert(C.end(), B.begin() + j, B.end());
return C;
}
};
int main() {
Solution::VI A{ 3,5,7,1,3,9,2,0 };
auto ans = Solution().mergesort(A);
copy(ans.begin(), ans.end(), ostream_iterator<int>(cout, " ")), cout << endl; // 0 1 2 3 4 5 6 7 8 9
return 0;
}
```
</details>
---
### Counting Inversions
<details><summary>📚 Lectures</summary>
<br/>
* [The Divide-and-Conquer Paradigm](https://www.youtube.com/watch?v=7_AJfusC6UQ&list=PLEGCF-WLh2RLHqXx6-GZr_w7LgqKDXxN_&index=13) (Section 3.1; part 1 of Section 3.2)
* [Counting Inversions in O(n log n)](https://www.youtube.com/watch?v=I6ygiW8xN7Y&list=PLEGCF-WLh2RLHqXx6-GZr_w7LgqKDXxN_&index=14) Time (Section 3.2, part 2)
</details>
<details><summary>🎯 Solutions</summary>
<br/>
*Kotlin*
```java
import java.io.File
fun sort(A: IntArray): Pair<IntArray, Long> {
fun merge(A: IntArray, B: IntArray): Pair<IntArray, Long> {
var C = mutableListOf<Int>()
var inv: Long = 0
var i = 0
var j = 0
while (i < A.size && j < B.size)
if (A[i] < B[j]) {
C.add(A[i++])
} else {
inv += A.size - i // ⭐️ B[j] comes before all remaining A[i...], thus all remaining A[i...] are inversions
C.add(B[j++])
}
A.slice(i..A.lastIndex).forEach { C.add(it) }
B.slice(j..B.lastIndex).forEach { C.add(it) }
return Pair(C.toIntArray(), inv)
}
fun go(A: IntArray): Pair<IntArray, Long> {
var N = A.size
if (N < 2)
return Pair(A, 0)
var half = Math.floor(N / 2.0).toInt()
var (first, inv1) = go(A.slice(0 until half).toIntArray())
var (second, inv2) = go(A.slice(half until N).toIntArray())
var (third, inv3) = merge(first, second)
return Pair(third, inv1 + inv2 + inv3)
}
return go(A)
}
fun run(filename: String): Long {
var A = mutableListOf<Int>()
File(filename).forEachLine { A.add(it.toInt()) }
var (_, inv) = sort(A.toIntArray())
return inv
}
fun main() {
println("problem3.5test.txt: " + run("problem3.5test.txt")) // problem3.5test.txt: 28
println("problem3.5.txt: " + run("problem3.5.txt")) // problem3.5.txt: 2407905288
}
```
*Javascript*
```javascript
let sort = A => {
let go = A => {
let N = A.length;
if (N < 2)
return [A, 0];
let half = Math.floor(N / 2);
let [first, inv1] = go([...A.slice(0, half)]),
[second, inv2] = go([...A.slice(half, N)]),
[third, inv3] = merge(first, second);
return [third, inv1 + inv2 + inv3];
};
let merge = (A, B, C = [], inv = 0) => {
let M = A.length,
N = B.length;
let i = 0,
j = 0;
while (i < M && j < N)
if (A[i] < B[j])
C.push(A[i++]);
else
inv += M - i, // ⭐️ B[j] comes before all remaining A[i...], thus all remaining A[i...] are inversions
C.push(B[j++]);
C.push(...A.slice(i, M));
C.push(...B.slice(j, N));
return [C, inv];
};
return go(A);
};
let run = filename => {
let A = [];
require('fs').readFileSync(filename, 'utf-8').split(/\r?\n/).forEach(line => A.push(Number(line)));
let [_, inv] = sort(A);
return inv;
}
console.log(`problem3.5test.txt: ${run('problem3.5test.txt')}`); // problem3.5test.txt: 28
console.log(`problem3.5.txt: ${run('problem3.5.txt')}`); // problem3.5.txt: 2407905288
```
*Python3*
```python
from math import floor
def sort(A):
def go(A):
N = len(A)
if N < 2:
return [A, 0]
half = floor(N / 2)
first, inv1 = go(A[:half])
second, inv2 = go(A[half:])
third, inv3 = merge(first, second)
return [third, inv1 + inv2 + inv3]
def merge(A, B, inv = 0):
C = []
i = 0
j = 0
while i < len(A) and j < len(B):
if A[i] < B[j]:
C.append(A[i]); i += 1
else:
inv += len(A) - i # ⭐️ B[j] comes before all remaining A[i...], thus all remaining A[i...] are inversions
C.append(B[j]); j += 1
C.extend(A[i:])
C.extend(B[j:])
return [C, inv]
return go(A)
def run(filename):
A = []
with open(filename) as fin:
while True:
line = fin.readline()
if not line:
break
A.append(int(line))
_, inv = sort(A)
return inv
print(f"problem3.5test.txt: {run('problem3.5test.txt')}") # problem3.5test.txt: 28
print(f"problem3.5.txt: {run('problem3.5.txt')}") # problem3.5.txt: 2407905288
```
*C++*
```cpp
#include <iostream>
#include <vector>
#include <fstream>
using namespace std;
class Solution {
public:
using VL = vector<long>;
using Pair = pair<VL, long>;
using fun = function<Pair(VL&&)>;
Pair merge(VL& A, VL& B, VL C = {}, long inv = 0) {
auto i = 0,
j = 0;
while (i < A.size() && j < B.size()) {
if (A[i] < B[j]) {
C.push_back(A[i++]);
} else {
inv += A.size() - i; // ⭐️ B[j] comes before all remaining A[i...], thus all remaining A[i...] are inversions
C.push_back(B[j++]);
}
}
C.insert(C.end(), A.begin() + i, A.end());
C.insert(C.end(), B.begin() + j, B.end());
return { C, inv };
}
Pair inversions(VL& A) {
fun go = [&](VL&& A) -> Pair {
int N = A.size();
if (N < 2)
return { A, 0 };
int half = N / 2;
auto [first, inv1] = go({ A.begin(), A.begin() + half });
auto [second, inv2] = go({ A.begin() + half, A.end() });
auto [third, inv3] = merge(first, second);
return { third, inv1 + inv2 + inv3 };
};
return go(move(A));
}
};
long run(string filename) {
Solution solution;
Solution::VL A;
fstream fin{ filename };
for (string line; fin >> line; A.push_back(stol(line)));
auto [_, inv] = solution.inversions(A);
return inv;
}
int main() {
cout << "problem3.5test.txt: " << run("problem3.5test.txt") << endl // problem3.5test.txt: 28
<< "problem3.5.txt: " << run("problem3.5.txt") << endl; // problem3.5.txt: 2407905288
return 0;
}
```
</details>
---
### Quick Sort
<details><summary>📚 Lectures</summary>
<br/>
* [QuickSort: Overview](https://www.youtube.com/watch?v=ETo1cpLN7kk&list=PLEGCF-WLh2RLHqXx6-GZr_w7LgqKDXxN_&index=24) (Section 5.1)
* [Partitioning Around a Pivot Element](https://www.youtube.com/watch?v=LYzdRN5iFdA&list=PLEGCF-WLh2RLHqXx6-GZr_w7LgqKDXxN_&index=25) (Section 5.2)
* [Choosing a Good Pivot](https://www.youtube.com/watch?v=kqO46FOUTbI&list=PLEGCF-WLh2RLHqXx6-GZr_w7LgqKDXxN_&index=26) (Sections 5.3 and 5.4)
* [QuickSort Analysis (Part 1)](https://www.youtube.com/watch?v=sToWtKSYlMw&list=PLEGCF-WLh2RLHqXx6-GZr_w7LgqKDXxN_&index=27) (Section 5.5, part 1)
* [QuickSort Analysis (Part 2)](https://www.youtube.com/watch?v=4t_Y-aGLkok&list=PLEGCF-WLh2RLHqXx6-GZr_w7LgqKDXxN_&index=28) (Section 5.5, part 2)
* [QuickSort Analysis (Part 3)](https://www.youtube.com/watch?v=IBTvneWhFsA&list=PLEGCF-WLh2RLHqXx6-GZr_w7LgqKDXxN_&index=29) (Section 5.5, part 3)
* [Sorting Requires Omega(n log n) Comparisons](https://www.youtube.com/watch?v=aFveIyII5D4&list=PLEGCF-WLh2RLHqXx6-GZr_w7LgqKDXxN_&index=30) (Section 5.6)
* [Proofs by Induction and the Correctness of QuickSort](https://www.youtube.com/watch?v=Colb_4jAy8A&list=PLEGCF-WLh2RLHqXx6-GZr_w7LgqKDXxN_&index=36) (Appendix A)
* [Quick Review of Discrete Probability](https://www.youtube.com/watch?v=uLeIMwMHX5U&list=PLEGCF-WLh2RLHqXx6-GZr_w7LgqKDXxN_&index=37) (Appendix B)
</details>
<details><summary>🎯 Solutions</summary>
<br/>
*Kotlin*
```java
import java.io.File
typealias PivotFunc = (A: MutableList<Int>, L: Int, R: Int) -> (Int)
var pivotLeft: PivotFunc = { _: MutableList<Int>, L: Int, _: Int -> L }
var pivotRight: PivotFunc = { _: MutableList<Int>, _: Int, R: Int -> R }
fun _pivotMedian(A: MutableList<Int>, L: Int, R: Int): Int {
var M = L + (R - L) / 2
var cand = intArrayOf(A[L], A[M], A[R])
cand.sort()
var target = cand[1]
if (target == A[L]) return L
if (target == A[M]) return M
if (target == A[R]) return R
return -1
}
var pivotMedian: PivotFunc = { A: MutableList<Int>, L: Int, R: Int -> _pivotMedian(A, L, R) }
fun partition(A: MutableList<Int>, L: Int, R: Int, choosePivot: (A: MutableList<Int>, L: Int, R: Int) -> (Int)): Int {
var i = L + 1
var j = L + 1
var k = choosePivot(A, L, R)
A[k] = A[L].also { A[L] = A[k] } // swap pivot A[k] with first element of subarray A[L]
while (j <= R) {
if (A[j] < A[L]) { // maintain loop invariant A[i] < pivot < A[j]
A[i] = A[j].also { A[j] = A[i] }
++i
}
++j
}
A[L] = A[i - 1].also { A[i - 1] = A[L] } // swap pivot A[L] with last value less-than pivot A[i - 1]
return i - 1
}
fun quicksort(A: MutableList<Int>, L: Int, R: Int, choosePivot: (A: MutableList<Int>, L: Int, R: Int) -> (Int)): Int {
if (R <= L)
return 0
var k = partition(A, L, R, choosePivot)
return (R - L) + quicksort(A, L, k - 1, choosePivot) + quicksort(A, k + 1, R, choosePivot)
}
fun run(filename: String, choosePivot: (A: MutableList<Int>, L: Int, R: Int) -> (Int)): Int {
var A = mutableListOf<Int>()
File(filename).forEachLine { A.add(it.toInt()) }
return quicksort(A, 0, A.size - 1, choosePivot)
}
fun main() {
var filename = "problem5.6.txt"
println(" left: ${run(filename, pivotLeft)}") // left: 162085
println(" right: ${run(filename, pivotRight)}") // right: 164123
println("median: ${run(filename, pivotMedian)}") // median: 138382
}
```
*Javascript*
```javascript
let pivotLeft = (A, L, R) => L;
let pivotRight = (A, L, R) => R;
let pivotMedian = (A, L, R) => {
let M = L + Math.floor((R - L) / 2);
let cand = [A[L], A[M], A[R]].sort((a, b) => a - b),
target = cand[1];
if (target == A[L]) return L;
if (target == A[M]) return M;
if (target == A[R]) return R;
};
let partition = (A, L, R, choosePivot) => {
let i = L + 1,
j = L + 1,
k = choosePivot(A, L, R);
[A[L], A[k]] = [A[k], A[L]]; // swap pivot A[k] with first element of subarray A[L]
while (j <= R) {
if (A[j] < A[L]) { // maintain loop invariant A[i] < pivot < A[j]
[A[i], A[j]] = [A[j], A[i]];
++i;
}
++j;
}
[A[L], A[i - 1]] = [A[i - 1], A[L]]; // swap pivot A[L] with last value less-than pivot A[i - 1]
return i - 1;
};
let quicksort = (A, L, R, choosePivot) => {
if (R <= L)
return 0;
let k = partition(A, L, R, choosePivot);
return (R - L) + quicksort(A, L, k - 1, choosePivot)
+ quicksort(A, k + 1, R, choosePivot);
};
let run = (filename, choosePivot) => {
let A = [];
let LineByLine = require("n-readlines");
let input = new LineByLine(filename);
for (let line; line = input.next(); A.push(Number(line)));
return quicksort(A, 0, A.length - 1, choosePivot);
}
let filename = 'problem5.6.txt';
console.log(` left: ${run(filename, pivotLeft)}`); // left: 162085
console.log(` right: ${run(filename, pivotRight)}`); // right: 164123
console.log(`median: ${run(filename, pivotMedian)}`); // median: 138382
```
*Python3*
```python
def pivotLeft(A, L, R): return L
def pivotRight(A, L, R): return R
def pivotMedian(A, L, R):
M = L + (R - L) // 2
cand = sorted([A[L], A[M], A[R]])
target = cand[1]
if target == A[L]: return L
if target == A[M]: return M
if target == A[R]: return R
def partition(A, L, R, choosePivot):
i = L + 1
j = L + 1
k = choosePivot(A, L, R)
A[L], A[k] = A[k], A[L] # swap pivot A[k] with first element of subarray A[L]
while j <= R:
if A[j] < A[L]: # maintain loop invariant A[i] < pivot < A[j]
A[i], A[j] = A[j], A[i]
i += 1
j += 1
A[L], A[i - 1] = A[i - 1], A[L] # swap pivot A[L] with last value less-than pivot A[i - 1]
return i - 1
def quicksort(A, L, R, choosePivot):
if R <= L:
return 0
k = partition(A, L, R, choosePivot)
return (R - L) + quicksort(A, L, k - 1, choosePivot) + quicksort(A, k + 1, R, choosePivot)
def run(filename, choosePivot):
A = []
with open(filename) as fin:
while True:
line = fin.readline()
if not line:
break
A.append(int(line))
return quicksort(A, 0, len(A) - 1, choosePivot)
filename = 'problem5.6.txt'
print(f' left: {run(filename, pivotLeft)}') # left: 162085
print(f' right: {run(filename, pivotRight)}') # right: 164123
print(f'median: {run(filename, pivotMedian)}') # median: 138382
```
*C++*
```cpp
#include <iostream>
#include <fstream>
#include <vector>
using namespace std;
using VI = vector<int>;
using fun = function<int(VI&, int, int)>;
fun pivotLeft = [](VI& A, int L, int R) { return L; };
fun pivotRight = [](VI& A, int L, int R) { return R; };
fun pivotMedian = [](VI& A, int L, int R) {
auto M = L + (R - L) / 2;
VI cand{ A[L], A[M], A[R] };
sort(cand.begin(), cand.end());
auto target = cand[1];
if (target == A[L]) return L;
if (target == A[M]) return M;
if (target == A[R]) return R;
};
int partition(VI& A, int L, int R, fun choosePivot) {
auto i = L + 1,
j = L + 1,
k = choosePivot(A, L, R);
swap(A[L], A[k]); // swap pivot A[k] with first element of the subarray A[L]
while (j <= R) {
if (A[j] < A[L]) { // maintain loop invariant A[i] < pivot < A[j]
swap(A[i], A[j]);
++i;
}
++j;
}
swap(A[L], A[i - 1]); // swap pivot A[L] with last value less-than pivot A[i - 1]
return i - 1;
}
int quicksort(VI& A, int L, int R, fun choosePivot) {
if (R <= L)
return 0;
auto k = partition(A, L, R, choosePivot);
return (R - L) + quicksort(A, L, k - 1, choosePivot)
+ quicksort(A, k + 1, R, choosePivot);
}
int run(string& filename, fun choosePivot) {
VI A;
fstream fin{ filename };
for (string line; fin >> line; A.push_back(stoi(line)));
int N = A.size();
return quicksort(A, 0, N - 1, choosePivot);
}
int main() {
string filename{ "problem5.6.txt" };
cout << " left: " << run(filename, pivotLeft) << endl // left: 162085
<< " right: " << run(filename, pivotRight) << endl // right: 164123
<< "median: " << run(filename, pivotMedian) << endl; // median: 138382
return 0;
}
```
</details>
---
### Randomized Linear-Time Selection
<details><summary>📚 Lectures</summary>
<br/>
* [Randomized Linear-Time Selection](https://www.youtube.com/watch?v=nFw6x7DoYbs&list=PLEGCF-WLh2RLHqXx6-GZr_w7LgqKDXxN_&index=31) (Section 6.1)
* [Randomized Linear-Time Selection (Analysis)](https://www.youtube.com/watch?v=rX2u2CnpveQ&list=PLEGCF-WLh2RLHqXx6-GZr_w7LgqKDXxN_&index=32) (Section 6.2)
</details>
<details><summary>🎯 Solutions</summary>
<br/>
*Kotlin*
```java
import java.io.File
import kotlin.random.Random
fun partition(A: MutableList<Int>, L: Int, R: Int): Int {
var i = L + 1
var j = L + 1
var k = Random.nextInt(L, R + 1) // +1 for L..R inclusive
A[L] = A[k].also { A[k] = A[L] } // swap pivot A[k] with first element of subarray A[L]
while (j <= R) {
if (A[j] < A[L]) { // maintain loop invariant A[i] < pivot < A[j]
A[i] = A[j].also { A[j] = A[i] }
++i
}
++j
}
A[L] = A[i - 1].also { A[i - 1] = A[L] } // swap pivot A[L] with last value less-than pivot A[i - 1]
return i - 1
}
fun rselect(A: MutableList<Int>, i: Int, L_: Int, R_: Int): Int {
var L = L_
var R = R_
var k = partition(A, L, R)
if (i == k)
return A[k] // 🎯 lucky guess
if (i < k)
R = k - 1
else
L = k + 1
return rselect(A, i, L, R)
}
fun run(filename: String, i: Int): Int {
var A = mutableListOf<Int>()
File(filename).forEachLine { A.add(it.toInt()) }
var N = A.size
return rselect(A, i - 1, 0 , N - 1) // -1 for 0-based indexing
}
fun main() {
println("problem6.5test1.txt: " + run("problem6.5test1.txt", 5)) // problem6.5test1.txt: 5469
println("problem6.5test2.txt: " + run("problem6.5test2.txt", 50)) // problem6.5test2.txt: 4715
}
```
*Javascript*
```javascript
let random = (L, R) => Math.floor(Math.random() * (R + 1 - L) + L); // +1 for L..R inclusive
let partition = (A, L, R) => {
let i = L + 1,
j = L + 1,
k = random(L, R);
[A[L], A[k]] = [A[k], A[L]]; // swap pivot A[k] with first element of subarray A[L]
while (j <= R) {
if (A[j] < A[L]) { // maintain loop invariant A[i] < pivot < A[j]
[A[i], A[j]] = [A[j], A[i]];
++i;
}
++j;
}
[A[L], A[i - 1]] = [A[i - 1], A[L]]; // swap pivot A[L] with last value less-than pivot A[i - 1]
return i - 1;
};
let rselect = (A, i, L, R) => {
let k = partition(A, L, R);
if (i == k)
return A[k]; // 🎯 lucky guess
if (i < k)
R = k - 1;
else
L = k + 1;
return rselect(A, i, L, R);
}
let run = (filename, i) => {
let A = [];
let LineByLine = require("n-readlines");
let input = new LineByLine(filename);
for (let line; line = input.next(); A.push(Number(line)));
let N = A.length;
return rselect(A, i - 1, 0, N - 1); // -1 for 0-based indexing
};
console.log(`problem6.5test1.txt: ${run('problem6.5test1.txt', 5)}`); // problem6.5test1.txt: 5469
console.log(`problem6.5test2.txt: ${run('problem6.5test2.txt', 50)}`); // problem6.5test2.txt: 4715
```
*Python3*
```python
from random import uniform
from math import floor
def partition(A, L, R):
i = L + 1
j = L + 1
k = floor(uniform(L, R))
A[L], A[k] = A[k], A[L] # swap pivot A[k] with first element of subarray A[L]
while j <= R:
if A[j] < A[L]: # maintain loop invariant A[i] < pivot < A[j]
A[i], A[j] = A[j], A[i]
i += 1
j += 1
A[L], A[i - 1] = A[i - 1], A[L] # swap pivot A[L] with last value less-than pivot A[i - 1]
return i - 1
def rselect(A, i, L, R):
k = partition(A, L, R)
if i == k:
return A[k] # 🎯 lucky guess
if i < k:
R = k - 1
else:
L = k + 1
return rselect(A, i, L, R)
def run(filename, i):
A = []
with open(filename) as fin:
while True:
line = fin.readline()
if not line:
break
A.append(int(line))
N = len(A)
return rselect(A, i - 1, 0, N - 1) # -1 for 0-based indexing
print('problem6.5test1.txt:', run('problem6.5test1.txt', 5)) # problem6.5test1.txt: 5469
print('problem6.5test2.txt:', run('problem6.5test2.txt', 50)) # problem6.5test2.txt: 4715
```
*C++*
```cpp
#include <iostream>
#include <fstream>
#include <vector>
#include <random>
using namespace std;
using VI = vector<int>;
int random(int L, int R) {
random_device rd;
mt19937 gen{ rd() };
uniform_int_distribution dist(L, R);
return dist(gen);
}
int partition(VI& A, int L, int R) {
auto i = L + 1,
j = L + 1,
k = random(L, R);
swap(A[L], A[k]); // swap pivot A[k] with first element of the subarray A[L]
while (j <= R) {
if (A[j] < A[L]) // maintain loop invariant A[i] < pivot < A[j]
swap(A[i++], A[j]);
++j;
}
swap(A[L], A[i - 1]); // swap pivot A[L] with last value less-than pivot A[i - 1]
return i - 1;
}
int rselect(VI& A, int i, int L, int R) {
auto k = partition(A, L, R);
if (i == k)
return A[k]; // 🎯 lucky guess
if (i < k)
R = k - 1;
else
L = k + 1;
return rselect(A, i, L, R);
}
int run(string filename, int i, VI A = {}) {
fstream fin{ filename };
for (string line; fin >> line; A.push_back(stoi(line)));
int N = A.size();
return rselect(A, i - 1, 0, N - 1); // -1 for 0-based indexing
}
int main() {
cout << "problem6.5test1.txt: " << run("problem6.5test1.txt", 5) << endl; // problem6.5test1.txt: 5469
cout << "problem6.5test2.txt: " << run("problem6.5test2.txt", 50) << endl; // problem6.5test2.txt: 4715
return 0;
}
```
</details>
---
# Part 2: Graph Algorithms and Data Structures
<br/>
<a href="https://www.amazon.com/dp/0999282921" target="_blank">
<img src="images/ai2large.jpg" />
</a>
---
### Topological Sort
<details><summary>📚 Lectures</summary>
<br/>
* [Graphs: The Basics (from 2:06 to 6:39)](https://www.youtube.com/watch?v=4Ih3UhVuEtw&list=PLEGCF-WLh2RJ5W-pt-KE9GUArTDzVwL1P&index=1) (Sections 7.1 and 7.2)
* [Graph Representations](https://www.youtube.com/watch?v=b-Mfu8dPv9U&list=PLEGCF-WLh2RJ5W-pt-KE9GUArTDzVwL1P&index=2) (Sections 7.3 and 7.4)
* [Graph Search Overview](https://www.youtube.com/watch?v=SW6jwg7WS48&list=PLEGCF-WLh2RJ5W-pt-KE9GUArTDzVwL1P&index=3) (Section 8.1)
* [Breadth-First Search](https://www.youtube.com/watch?v=73qCvXsYkfk&list=PLEGCF-WLh2RJ5W-pt-KE9GUArTDzVwL1P&index=4) (Section 8.2, Part 1)
* [Depth-First Search](https://www.youtube.com/watch?v=73qCvXsYkfk&list=PLEGCF-WLh2RJ5W-pt-KE9GUArTDzVwL1P&index=4) (Section 8.4)
* [Topological Sort](https://www.youtube.com/watch?v=ozso3xxkVGU&list=PLEGCF-WLh2RJ5W-pt-KE9GUArTDzVwL1P&index=8) (Section 8.5)
</details>
<details><summary>🎯 Solutions</summary>
<br/>
*Kotlin*
```java
import java.util.Queue
import java.util.LinkedList
class Solution(val adj: MutableMap<Char, List<Char>>) {
var N: Int
var color: Int
var m = mutableMapOf<Char, Int>()
var seen = mutableSetOf<Char>()
init {
N = adj.size
color = 0
}
fun init(start: Int) {
color = start
m.clear()
seen.clear()
}
fun topoSortBFS(): String {
init(1) // 👉 color forward from 1..N
bfs()
return toString()
}
fun topoSortDFS(): String {
init(N) // 👈 color reverse from N..1 (as the recursive stack unwinds)
adj.forEach{ (u, _) -> dfs(u) }
return toString()
}
fun bfs() {
var degree = mutableMapOf<Char, Int>()
adj.forEach{ (_, neighbors) ->
neighbors.forEach{ v ->
degree[v] = 1 + degree.getOrDefault(v, 0)
}
}
var q: Queue<Char> = LinkedList(adj.map{ (u, _) -> u }.filter{ !degree.contains(it) })
while (0 < q.size) {
var u = q.poll()
m[u] = color++
adj[u]!!.forEach{ v ->
degree[v] = degree[v]!!.minus(1)
if (degree[v] == 0 && !seen.contains(v)) {
q.add(v); seen.add(v)
}
}
}
}
fun dfs(u: Char) {
if (seen.contains(u))
return
seen.add(u)
adj[u]!!.forEach{ v ->
dfs(v)
}
m[u] = color--
}
override fun toString(): String {
var s = mutableListOf<String>()
adj.forEach{ (u, _) ->
s.add("$u: ${m[u]}")
}
return s.joinToString("\n")
}
}
fun main() {
var adj = mutableMapOf<Char, List<Char>>(
's' to listOf<Char>('v', 'w'),
'v' to listOf<Char>('t'),
'w' to listOf<Char>('t'),
't' to listOf<Char>()
)
var solution = Solution(adj)
println("BFS:\n${solution.topoSortBFS()}\n\nDFS:\n${solution.topoSortDFS()}")
// BFS:
// s: 1
// v: 2
// w: 3
// t: 4
// DFS:
// s: 1
// v: 3
// w: 2
// t: 4
}
```
*Javascript*
```javascript
class Solution {
constructor(adj) {
this.adj = adj;
this.N = this.adj.size;
}
init(start) {
this.color = start;
this.seen = new Set();
this.m = new Map();
}
topo_sort_bfs() {
this.init(1); // 👉 color forward from 1..N
this.bfs();
return this.to_string();
}
topo_sort_dfs() {
this.init(this.N); // 👈 color reverse from N..1 (as the recursive stack unwinds)
for (let [u, _] of [...this.adj])
this.dfs(u);
return this.to_string();
}
bfs() {
let degree = new Map();
for (let [u, _] of [...this.adj]) {
degree.set(u, (degree.get(u) || 0));
for (let v of this.adj.get(u))
degree.set(v, 1 + (degree.get(v) || 0));
}
let q = [...this.adj].map(([u, _]) => u).filter(u => !degree.get(u));
let seen = new Set(q);
while (q.length) {
let u = q.shift();
this.m.set(u, this.color++);
for (let v of this.adj.get(u)) {
degree.set(v, -1 + degree.get(v));
if (!degree.get(v) && !seen.has(v))
q.push(v), seen.add(v);
}
}
}
dfs(u) {
if (this.seen.has(u))
return;
this.seen.add(u);
for (let v of this.adj.get(u))
if (!this.seen.has(v))
this.dfs(v);
this.m.set(u, this.color--);
}
to_string() {
let s = [];
for (let [u, color] of [...this.m])
s.push(`${u}: ${color}`);
return s.join('\n');
}
}
let adj = new Map();
adj.set('s', ['v', 'w']);
adj.set('v', ['t']);
adj.set('w', ['t']);
adj.set('t', []);
let solution = new Solution(adj);
console.log(`BFS:\n${solution.topo_sort_bfs()}\n\nDFS:\n${solution.topo_sort_dfs()}`);
// BFS:
// s: 1
// v: 2
// w: 3
// t: 4
// DFS:
// t: 4
// v: 3
// w: 2
// s: 1
```
*Python3*
```python
from collections import deque
class Solution:
def __init__(self, adj):
self.adj = adj
self.N = len(adj)
self.seen = set()
self.m = {}
def init(self, start):
self.color = start
self.seen.clear()
self.m.clear()
def topo_sort_bfs(self):
self.init(1) # 👉 color forward from 1..N
self.bfs()
return self.to_string()
def topo_sort_dfs(self):
self.init(self.N) # 👈 color reverse from N..1 (as the recursive stack unwinds)
for u, _ in self.adj.items():
self.dfs(u)
return self.to_string()
def bfs(self):
degree = {}
for _, neighbors in self.adj.items():
for v in neighbors:
degree[v] = 1 + (degree[v] if v in degree else 0)
q = deque(u for u, _ in self.adj.items() if u not in degree)
self.seen.update(*q)
while q:
u = q.popleft()
self.m[u] = self.color; self.color += 1
for v in adj[u]:
degree[v] -= 1
if not degree[v] and v not in self.seen:
q.append(v); self.seen.add(v)
def dfs(self, u):
if u in self.seen:
return
self.seen.add(u)
for v in adj[u]:
self.dfs(v)
self.m[u] = self.color; self.color -= 1
def to_string(self):
s = []
for u, color in self.m.items():
s.append(f'{u}: {color}')
return '\n'.join(s)
#
# graph from Quiz 8.3 on page 45 of Algorithms Illuminated: Part 2
#
adj = {
's': ['v', 'w'],
'v': ['t'],
'w': ['t'],
't': []
}
solution = Solution(adj)
print(f'BFS:\n{solution.topo_sort_bfs()}\n\nDFS:\n{solution.topo_sort_dfs()}')
# BFS:
# s: 1
# v: 2
# w: 3
# t: 4
# DFS:
# t: 4
# v: 3
# w: 2
# s: 1
```
*C++*
```cpp
#include <iostream>
#include <sstream>
#include <vector>
#include <unordered_map>
#include <unordered_set>
#include <queue>
using namespace std;
using VI = vector<int>;
using AdjList = unordered_map<char, VI>;
using Set = unordered_set<char>;
using Map = unordered_map<char, int>;
using Queue = queue<char>;
using fun = function<void(char)>;
class Solution {
private:
AdjList adj;
const int N;
Map m;
Set seen;
int color;
public:
Solution(AdjList& adj) : adj{ adj }, N{ int(adj.size()) } {
}
void init(int start) {
m.clear();
seen.clear();
color = start;
}
string topo_sort_bfs() {
init(1); // 👉 color forward from 1..N
bfs();
return to_string();
}
string topo_sort_dfs() {
init(N); // 👈 color reverse from N..1 (as the recursive stack unwinds)
for (auto [u, _]: adj)
dfs(u);
return to_string();
}
void bfs() {
Map degree;
for (auto [_, neighbors]: adj)
for (auto v: neighbors)
++degree[v];
Queue q;
for (auto [u, _]: adj)
if (!degree[u] && seen.insert(u).second)
q.push(u);
while (q.size()) {
auto u = q.front(); q.pop();
m[u] = color++;
for (auto v: adj[u])
if (!--degree[v] && seen.insert(v).second)
q.push(v);
}
}
void dfs(char start) {
fun go = [&](auto u) {
if (!seen.insert(u).second)
return;
for (auto v: adj[u])
go(v);
m[u] = color--;
};
go(start);
}
string to_string() {
ostringstream os;
for (auto [u, color]: m)
os << u << ": " << color << endl;
return os.str();
}
};
int main() {
//
// graph from Quiz 8.3 on page 45 of Algorithms Illuminated: Part 2
//
AdjList adj{
{ 's', { 'v', 'w' } },
{ 'v', { 't' } },
{ 'w', { 't' } },
{ 't', {} }
};
Solution solution{ adj };
cout << "BFS:" << endl << solution.topo_sort_bfs() << endl
<< "DFS:" << endl << solution.topo_sort_dfs() << endl;
// BFS:
// t: 4
// w: 3
// v: 2
// s: 1
//
// DFS:
// s: 1
// w: 2
// v: 3
// t: 4
return 0;
}
```
</details>
---
### Kosaraju
<details><summary>📚 Lectures</summary>
<br/>
* [Computing Strongly Connected Components (Part 1) (Section 8.6, Part 1)](https://www.youtube.com/watch?v=O98hLTYVN3c&list=PLEGCF-WLh2RJ5W-pt-KE9GUArTDzVwL1P&index=9)
* [Computing Strongly Connected Components (Part 2) (Section 8.6, Part 2)](https://www.youtube.com/watch?v=gbs3UNRJIYk&list=PLEGCF-WLh2RJ5W-pt-KE9GUArTDzVwL1P&index=10)
* [The Structure of the Web (Section 8.7)](https://www.youtube.com/watch?v=7YodysGShlo&list=PLEGCF-WLh2RJ5W-pt-KE9GUArTDzVwL1P&index=11)
</details>
<details><summary>🎯 Solutions</summary>
<br/>
*Kotlin*
```java
import java.util.Stack
import java.io.File
class RecursiveSolution(var adj: MutableMap<Int, MutableList<Int>>, var rev: MutableMap<Int, MutableList<Int>>) {
fun topo_sort(): MutableList<Int> {
var list = mutableListOf<Int>()
var seen = mutableSetOf<Int>()
fun go(u: Int) {
if (seen.contains(u))
return
seen.add(u)
for (v in rev[u]!!)
go(v)
list.add(0, u)
}
for ((u, _) in rev)
go(u)
return list
}
fun kosaraju(): MutableList<List<Int>> {
var lists = mutableListOf<List<Int>>()
var seen = mutableSetOf<Int>()
fun go(u: Int, list: MutableList<Int>) {
if (seen.contains(u))
return
list.add(u); seen.add(u)
for (v in adj[u]!!)
go(v, list)
}
for (u in topo_sort()) {
if (seen.contains(u))
continue
var list = mutableListOf<Int>()
go(u, list)
lists.add(list.toList())
}
return lists
}
}
class IterativeSolution(var adj: MutableMap<Int, MutableList<Int>>, var rev: MutableMap<Int, MutableList<Int>>) {
fun topo_sort(): MutableList<Int> {
var list = mutableListOf<Int>()
var seen = mutableSetOf<Int>()
for ((u, _) in rev) {
if (seen.contains(u))
continue
var stack = Stack<Int>()
stack.push(u); seen.add(u)
while (!stack.empty()) {
var u = stack.last()
for (v in rev[u]!!) {
if (!seen.contains(v)) {
stack.push(v); seen.add(v)
}
}
if (u == stack.last())
list.add(0, stack.pop())
}
}
return list
}
fun kosaraju(): MutableList<List<Int>> {
var lists = mutableListOf<List<Int>>()
var seen = mutableSetOf<Int>()
for (u in topo_sort()) {
if (seen.contains(u))
continue
var list = mutableListOf<Int>()
var stack = Stack<Int>()
stack.push(u); seen.add(u)
while (!stack.empty()) {
var u = stack.last()
for (v in adj[u]!!) {
if (!seen.contains(v)) {
stack.push(v); seen.add(v)
}
}
if (u == stack.last())
list.add(stack.pop())
}
lists.add(list.toList())
}
return lists
}
}
fun run(filename: String) {
var adj = mutableMapOf<Int, MutableList<Int>>()
var rev = mutableMapOf<Int, MutableList<Int>>()
File(filename).forEachLine {
var (u, v) = it.trim().split(" ").map{ it.toInt() }
if (!adj.contains(u)) adj[u] = mutableListOf(); if (!adj.contains(v)) adj[v] = mutableListOf()
if (!rev.contains(u)) rev[u] = mutableListOf(); if (!rev.contains(v)) rev[v] = mutableListOf()
adj[u]!!.add(v)
rev[v]!!.add(u)
}
// var solution = RecursiveSolution(adj, rev)
var solution = IterativeSolution(adj, rev)
var A = solution.kosaraju()
A.sortWith(Comparator{ a: List<Int>, b: List<Int> -> b.size - a.size })
println(filename + ": " + A.map{ it.size }.slice(0 until Math.min(A.size, 5)).joinToString(" "))
}
fun main() {
run("section8.6.5page64.txt"); // Graph from section 8.6.5 on page 64 of Algorithms Illuminated: Part 2
run("problem8.10test1.txt"); // Test case #1: A 9-vertex 11-edge graph. Top 5 SCC sizes: 3,3,3,0,0
run("problem8.10test2.txt"); // Test case #2: An 8-vertex 14-edge graph. Top 5 SCC sizes: 3,3,2,0,0
run("problem8.10test3.txt"); // Test case #3: An 8-vertex 9-edge graph. Top 5 SCC sizes: 3,3,1,1,0
run("problem8.10test4.txt"); // Test case #4: An 8-vertex 11-edge graph. Top 5 SCC sizes: 7,1,0,0,0
run("problem8.10test5.txt"); // Test case #5: A 12-vertex 20-edge graph. Top 5 SCC sizes: 6,3,2,1,0
run("problem8.10.txt"); // Challenge data set: Vertices are labeled as positive integers from 1 to 875714
// section8.6.5page64.txt: 4 3 3 1
// problem8.10test1.txt: 3 3 3
// problem8.10test2.txt: 3 3 2
// problem8.10test3.txt: 3 3 1 1
// problem8.10test4.txt: 7 1
// problem8.10test5.txt: 6 3 2 1
// problem8.10.txt: 434821 968 459 313 211
}
```
*Javascript*
```javascript
class BaseSolution {
constructor(adj, rev) {
this.adj = adj;
this.rev = rev;
}
}
class RecursiveSolution extends BaseSolution {
constructor(adj, rev) {
super(adj, rev);
}
topo_sort() {
let list = [];
let seen = new Set();
let go = u => {
if (seen.has(u))
return;
seen.add(u);
for (let v of [...this.rev.get(u)])
go(v);
list.unshift(u);
};
for (let [u, _] of [...this.rev])
go(u);
return list;
}
kosaraju() {
let lists = [];
let seen = new Set();
let go = (u, list) => {
if (seen.has(u))
return;
seen.add(u);
list.push(u);
for (let v of [...this.adj.get(u)])
go(v, list);
};
for (let u of this.topo_sort()) {
let list = [];
go(u, list);
lists.push([...list]);
}
lists.sort((a, b) => b.length - a.length);
return lists;
}
}
class IterativeSolution extends BaseSolution {
constructor(adj, rev) {
super(adj, rev);
}
topo_sort() {
let list = [];
let seen = new Set();
for (let [u, _] of [...this.rev]) {
if (seen.has(u))
continue;
let stack = [ u ]; seen.add(u);
stack.back = () => stack[stack.length - 1];
while (stack.length) {
let u = stack.back();
for (let v of [...this.rev.get(u)])
if (!seen.has(v))
stack.push(v), seen.add(v);
if (u == stack.back())
list.unshift(stack.pop());
}
}
return list;
}
kosaraju() {
let lists = [];
let seen = new Set();
for (let u of this.topo_sort()) {
if (seen.has(u))
continue;
let list = [];
let stack = [ u ]; seen.add(u);
stack.back = () => stack[stack.length - 1];
while (stack.length) {
let u = stack.back();
for (let v of [...this.adj.get(u)])
if (!seen.has(v))
stack.push(v), seen.add(v);
if (u == stack.back())
list.push(stack.pop());
}
lists.push([...list]);
}
lists.sort((a, b) => b.length - a.length);
return lists;
}
}
let run = filename => {
let adj = new Map(),
rev = new Map();
let LineByLine = require('n-readlines');
let input = new LineByLine(filename);
let line;
while (line = input.next()) {
let [u, v] = String.fromCharCode(...line).split(' ').map(Number);
if (!adj.has(u)) adj.set(u, []); if (!adj.has(v)) adj.set(v, []);
if (!rev.has(u)) rev.set(u, []); if (!rev.has(v)) rev.set(v, []);
adj.get(u).push(v);
rev.get(v).push(u);
}
// let A = new RecursiveSolution(adj, rev).kosaraju();
let A = new IterativeSolution(adj, rev).kosaraju();
console.log(`${filename}: ${A.slice(0, Math.min(A.length, 5)).map(scc => scc.length).join(' ')}`);
};
run('section8.6.5page64.txt') // Graph from section 8.6.5 on page 64 of Algorithms Illuminated: Part 2
run('problem8.10test1.txt') // Test case #1: A 9-vertex 11-edge graph. Top 5 SCC sizes: 3,3,3,0,0
run('problem8.10test2.txt') // Test case #2: An 8-vertex 14-edge graph. Top 5 SCC sizes: 3,3,2,0,0
run('problem8.10test3.txt') // Test case #3: An 8-vertex 9-edge graph. Top 5 SCC sizes: 3,3,1,1,0
run('problem8.10test4.txt') // Test case #4: An 8-vertex 11-edge graph. Top 5 SCC sizes: 7,1,0,0,0
run('problem8.10test5.txt') // Test case #5: A 12-vertex 20-edge graph. Top 5 SCC sizes: 6,3,2,1,0
run('problem8.10.txt') // Challenge data set: Vertices are labeled as positive integers from 1 to 875714
// section8.6.5page64.txt: 4 3 3 1
// problem8.10test1.txt: 3 3 3
// problem8.10test2.txt: 3 3 2
// problem8.10test3.txt: 3 3 1 1
// problem8.10test4.txt: 7 1
// problem8.10test5.txt: 6 3 2 1
// problem8.10.txt: 434821 968 459 313 211
```
*Python3*
```python
from collections import deque
from functools import cmp_to_key
class BaseSolution:
def __init__(self, adj, rev):
self.adj = adj
self.rev = rev
class RecursiveSolution(BaseSolution):
def topo_sort(self):
list = deque()
seen = set()
def go(u):
if u in seen:
return
seen.add(u)
for v in self.rev[u]:
go(v)
list.appendleft(u)
for u in self.rev.keys():
go(u)
return list
def kosaraju(self):
lists = []
seen = set()
def go(u, list):
if u in seen:
return
seen.add(u)
list.append(u)
for v in self.adj[u]:
go(v, list)
for u in self.topo_sort():
list = []
go(u, list)
lists.append(list.copy())
lists.sort(key = cmp_to_key(lambda a, b: len(b) - len(a)))
return lists
class IterativeSolution(BaseSolution):
def topo_sort(self):
list = deque()
seen = set()
for u in self.rev.keys():
if u in seen:
continue
stack = [ u ]; seen.add(u)
while len(stack):
u = stack[-1]
for v in self.rev[u]:
if v not in seen:
stack.append(v); seen.add(v)
if u == stack[-1]:
list.appendleft(stack.pop())
return list
def kosaraju(self):
lists = []
seen = set()
for u in self.topo_sort():
if u in seen:
continue
list = deque()
stack = [ u ]; seen.add(u)
while len(stack):
u = stack[-1]
for v in self.adj[u]:
if v not in seen:
stack.append(v); seen.add(v)
if u == stack[-1]:
list.appendleft(stack.pop())
lists.append(list.copy())
lists.sort(key = cmp_to_key(lambda a, b: len(b) - len(a)))
return lists
def run(filename):
adj, rev = {}, {}
with open(filename) as fin:
while True:
line = fin.readline().strip()
if not line:
break
u, v = [int(x) for x in line.split()]
if u not in adj: adj[u] = []
if v not in adj: adj[v] = []
if u not in rev: rev[u] = []
if v not in rev: rev[v] = []
adj[u].append(v)
rev[v].append(u)
# solution = RecursiveSolution(adj, rev)
solution = IterativeSolution(adj, rev)
A = solution.kosaraju()
print(filename + ': ' + ' '.join(str(len(scc)) for scc in A[:5]))
run('section8.6.5page64.txt') # Graph from section 8.6.5 on page 64 of Algorithms Illuminated: Part 2
run('problem8.10test1.txt') # Test case #1: A 9-vertex 11-edge graph. Top 5 SCC sizes: 3,3,3,0,0
run('problem8.10test2.txt') # Test case #2: An 8-vertex 14-edge graph. Top 5 SCC sizes: 3,3,2,0,0
run('problem8.10test3.txt') # Test case #3: An 8-vertex 9-edge graph. Top 5 SCC sizes: 3,3,1,1,0
run('problem8.10test4.txt') # Test case #4: An 8-vertex 11-edge graph. Top 5 SCC sizes: 7,1,0,0,0
run('problem8.10test5.txt') # Test case #5: A 12-vertex 20-edge graph. Top 5 SCC sizes: 6,3,2,1,0
run('problem8.10.txt') # Challenge data set: Vertices are labeled as positive integers from 1 to 875714
# section8.6.5page64.txt: 4 3 3 1
# problem8.10test1.txt: 3 3 3
# problem8.10test2.txt: 3 3 2
# problem8.10test3.txt: 3 3 1 1
# problem8.10test4.txt: 7 1
# problem8.10test5.txt: 6 3 2 1
# problem8.10.txt: 434821 968 459 313 211
```
*C++*
```cpp
#include <iostream>
#include <fstream>
#include <vector>
#include <unordered_map>
#include <unordered_set>
#include <queue>
using namespace std;
using List = deque<int>;
using Lists = deque<List>;
using AdjList = unordered_map<int, List>;
using Set = unordered_set<int>;
using Map = unordered_map<int, int>;
namespace Base {
class Solution {
protected:
AdjList adj, rev;
public:
Solution(AdjList& adj, AdjList& rev) : adj{ adj }, rev{ rev } {}
};
}
namespace Recursive {
struct Solution : public Base::Solution {
Solution(AdjList& adj, AdjList& rev) : Base::Solution{ adj, rev } {}
Lists kosaraju() {
Lists lists;
Set seen;
using fun = function<void(int, List&)>;
fun go = [&](auto u, auto& list) {
if (!seen.insert(u).second)
return;
list.push_back(u);
for (auto v: adj[u])
go(v, list);
};
for (auto u: topo_sort()) {
List list;
go(u, list);
lists.emplace_back(list);
}
sort(lists.begin(), lists.end(), [](auto& a, auto& b) { return b.size() < a.size(); });
return lists;
}
List topo_sort() {
List list;
Set seen;
using fun = function<void(int)>;
fun go = [&](auto u) {
if (!seen.insert(u).second)
return;
for (auto v: rev[u])
go(v);
list.push_front(u);
};
for (auto [u, _]: rev)
go(u);
return list;
}
};
}
namespace Iterative {
struct Solution : public Base::Solution {
Solution(AdjList& adj, AdjList& rev) : Base::Solution{ adj, rev } {}
Lists kosaraju() {
Lists lists;
Set seen;
for (auto u: topo_sort()) {
if (seen.find(u) != seen.end())
continue;
List list;
List stack{ u }; seen.insert(u);
while (stack.size()) {
auto u = stack.back();
for (auto v: adj[u])
if (seen.insert(v).second)
stack.push_back(v);
if (u == stack.back())
list.push_back(u), stack.pop_back();
}
lists.emplace_back(list);
}
sort(lists.begin(), lists.end(), [](auto& a, auto& b) { return b.size() < a.size(); });
return lists;
}
List topo_sort() {
List list;
Set seen;
for (auto [u, _]: rev) {
if (seen.find(u) != seen.end())
continue;
List stack{ u }; seen.insert(u);
while (stack.size()) {
auto u = stack.back();
for (auto v: rev[u])
if (seen.insert(v).second)
stack.push_back(v);
if (u == stack.back())
list.push_front(stack.back()), stack.pop_back();
}
}
return list;
}
};
}
void run(string filename) {
int u, v;
AdjList adj, rev;
fstream fin{ filename };
for (string line; fin >> u >> v;) {
adj[u].push_back(v);
rev[v].push_back(u);
}
auto A = Iterative::Solution{ adj, rev }.kosaraju();
A.resize(min(A.size(), size_t(5)));
cout << filename << ": ";
for (auto i{ 0 }; i < A.size(); cout << A[i++].size() << " ");
cout << endl;
}
int main() {
run("section8.6.5page64.txt"); // Graph from section 8.6.5 on page 64 of Algorithms Illuminated: Part 2
run("problem8.10test1.txt"); // Test case #1: A 9-vertex 11-edge graph. Top 5 SCC sizes: 3,3,3,0,0
run("problem8.10test2.txt"); // Test case #2: An 8-vertex 14-edge graph. Top 5 SCC sizes: 3,3,2,0,0
run("problem8.10test3.txt"); // Test case #3: An 8-vertex 9-edge graph. Top 5 SCC sizes: 3,3,1,1,0
run("problem8.10test4.txt"); // Test case #4: An 8-vertex 11-edge graph. Top 5 SCC sizes: 7,1,0,0,0
run("problem8.10test5.txt"); // Test case #5: A 12-vertex 20-edge graph. Top 5 SCC sizes: 6,3,2,1,0
run("problem8.10.txt"); // Challenge data set: Vertices are labeled as positive integers from 1 to 875714
// section8.6.5page64.txt: 4 3 3 1
// problem8.10test1.txt: 3 3 3
// problem8.10test2.txt: 3 3 2
// problem8.10test3.txt: 3 3 1 1
// problem8.10test4.txt: 7 1
// problem8.10test5.txt: 6 3 2 1
// problem8.10.txt: 434821 968 459 313 211
return 0;
}
```
</details>
---
### Dijkstra
<details><summary>📚 Lectures</summary>
<br/>
* [Shortest Paths and Dijkstra's Algorithm (Sections 9.1 and 9.2, Part 1)](https://www.youtube.com/watch?v=jRlNVmRjdRk&list=PLEGCF-WLh2RJ5W-pt-KE9GUArTDzVwL1P&index=12)
* [Dijkstra's Algorithm: Examples (Section 9.2, Part 2)](https://www.youtube.com/watch?v=ahYhIzLklYo&list=PLEGCF-WLh2RJ5W-pt-KE9GUArTDzVwL1P&index=13)
* [Correctness of Dijkstra's Algorithm (Section 9.3)](https://www.youtube.com/watch?v=sb7j3EW055M&list=PLEGCF-WLh2RJ5W-pt-KE9GUArTDzVwL1P&index=14)
* [Implementation and Running Time of Dijkstra's Algorithm (0:00-4:30) (Section 9.4)](https://www.youtube.com/watch?v=00LtSn_PQjc&list=PLEGCF-WLh2RJ5W-pt-KE9GUArTDzVwL1P&index=15)
* [Data Structures Overview (Section 10.1)](https://www.youtube.com/watch?v=cMrQxxrKg8I&list=PLEGCF-WLh2RJ5W-pt-KE9GUArTDzVwL1P&index=16)
* [Heaps: Operations and Applications (Sections 10.2 and 10.3)](https://www.youtube.com/watch?v=mNYHDv7SbDI&list=PLEGCF-WLh2RJ5W-pt-KE9GUArTDzVwL1P&index=17)
* [Speeding Up Dijkstra's Algorithm With Heaps (4:30-26:27) (Section 10.4)](https://www.youtube.com/watch?v=00LtSn_PQjc&list=PLEGCF-WLh2RJ5W-pt-KE9GUArTDzVwL1P&index=15)
* [Heaps: Implementation Details (Section 10.5)](https://www.youtube.com/watch?v=6VI5kJu8Mv4&list=PLEGCF-WLh2RJ5W-pt-KE9GUArTDzVwL1P&index=19&t=0s)
</details>
<details><summary>🎯 Solutions</summary>
<br/>
*Kotlin*
```java
import java.io.File
import java.util.PriorityQueue
var INF = (1e9 + 7).toInt()
interface BaseSolution {
fun run(filename: String, queries: Array<Int>): String
}
class NaiveSolution : BaseSolution {
fun dijkstra(E: List<Triple<Int, Int, Int>>): MutableMap<Int, Int> {
var dist = mutableMapOf<Int, Int>()
var seen = mutableSetOf<Int>()
var start = 1
dist[start] = 0; seen.add(start)
var found: Boolean;
do {
found = false
var best_v = INF
var best_w = INF
for ((u, v, w) in E) {
if (!seen.contains(u) || seen.contains(v))
continue
found = true
if (best_w > dist[u]!! + w) {
best_v = v
best_w = dist[u]!! + w
}
}
var v = best_v
var w = best_w
dist[v] = w; seen.add(v)
} while (found)
return dist
}
override fun run(filename: String, queries: Array<Int>): String {
var E = mutableListOf<Triple<Int, Int, Int>>()
File(filename).forEachLine {
var words = it.trim().split("\t")
var u = words[0].toInt()
for (i in 1 until words.size) {
var (v, w) = words[i].split(",").map{ it.toInt() }
E.add(Triple(u, v, w))
}
}
var dist = dijkstra(E.toList())
return queries.map{ dist[it] }.joinToString(" ")
}
}
class HeapSolution : BaseSolution {
fun dijkstra(adj: MutableMap<Int, MutableList<Pair<Int, Int>>>): MutableMap<Int, Int> {
var dist = mutableMapOf<Int, Int>()
var seen = mutableSetOf<Int>()
var start = 1
dist[start] = 0
var q = PriorityQueue<Pair<Int, Int>>(Comparator{ a: Pair<Int, Int>, b: Pair<Int, Int> -> a.first.compareTo(b.first) })
q.add(Pair(0, start))
while (0 < q.size) {
var (cost, u) = q.poll()
if (seen.contains(u))
continue
dist[u] = cost; seen.add(u)
for ((w, v) in adj[u]!!) {
if (seen.contains(v))
continue
q.add(Pair(cost + w, v))
}
}
return dist
}
override fun run(filename: String, queries: Array<Int>): String {
var adj = mutableMapOf<Int, MutableList<Pair<Int, Int>>>()
File(filename).forEachLine {
var words = it.trim().split("\t")
var u = words[0].toInt()
if (!adj.contains(u))
adj[u] = mutableListOf()
for (i in 1 until words.size) {
var (v, w) = words[i].split(",").map{ it.toInt() }
adj[u]!!.add(Pair(w, v))
}
}
var dist = dijkstra(adj)
return queries.map{ dist[it] }.joinToString(" ")
}
}
fun run(solution: BaseSolution) {
println(solution.run("problem9.8test.txt", arrayOf(1, 2, 3, 4, 5, 6, 7, 8)))
println(solution.run("problem9.8.txt", arrayOf(7, 37, 59, 82, 99, 115, 133, 165, 188, 197)))
}
fun main() {
run(NaiveSolution())
// 0 1 2 3 4 4 3 2
// 2599 2610 2947 2052 2367 2399 2029 2442 2505 3068
run(HeapSolution())
// 0 1 2 3 4 4 3 2
// 2599 2610 2947 2052 2367 2399 2029 2442 2505 3068
}
```
*Javascript*
```javascript
let LineByLine = require('n-readlines');
let INF = Number(1e9 + 7);
class NaiveSolution {
dijkstra(E) {
let dist = new Map();
let seen = new Set();
let start = 1;
dist[start] = 0; seen.add(start);
for (;;) {
let found = false;
let best_v = INF,
best_w = INF;
for (let [u, v, w] of E) {
if (!seen.has(u) || seen.has(v))
continue;
found = true;
if (best_w > dist[u] + w)
best_v = v,
best_w = dist[u] + w;
}
if (!found)
break;
let [v, w] = [best_v, best_w];
dist[v] = w; seen.add(v);
}
return dist;
}
run(filename, queries) {
let E = [];
let input = new LineByLine(filename);
let line;
while (line = input.next()) {
let words = String.fromCharCode(...line).trim().split(/\s+/);
let u = Number(words[0]);
for (let i = 1; i < words.length; ++i) {
let [v, w] = words[i].split(',').map(Number);
E.push([ u, v, w ]);
}
}
let dist = this.dijkstra(E);
return queries.map(x => dist[x]).join(' ');
}
}
let heapkey = x => Array.isArray(x) ? x[0] : x;
let heappush = (A, x, f = Math.min) => {
let P = i => Math.floor((i - 1) / 2); // parent
A.push(x);
let N = A.length,
i = N - 1;
while (0 < i && heapkey(A[i]) == f(heapkey(A[i]), heapkey(A[P(i)]))) {
[A[i], A[P(i)]] = [A[P(i)], A[i]];
i = P(i);
}
};
let heappop = (A, f = Math.min) => {
let L = i => 2 * i + 1, // children
R = i => 2 * i + 2;
let N = A.length,
i = 0;
let top = A[0];
[A[0], A[N - 1]] = [A[N - 1], A[0]], A.pop(), --N;
let ok = true;
do {
ok = true;
let left = f == Math.min ? Infinity : -Infinity,
right = left;
if (L(i) < N && heapkey(A[i]) != f(heapkey(A[i]), heapkey(A[L(i)]))) ok = false, left = heapkey(A[L(i)]);
if (R(i) < N && heapkey(A[i]) != f(heapkey(A[i]), heapkey(A[R(i)]))) ok = false, right = heapkey(A[R(i)]);
if (!ok) {
let j = left == f(left, right) ? L(i) : R(i);
[A[i], A[j]] = [A[j], A[i]];
i = j;
}
} while (!ok);
return top;
};
class HeapSolution {
dijkstra(adj) {
let dist = {};
let seen = new Set();
let start = 1;
let q = [[ 0, start ]];
while (q.length) {
let [cost, u] = heappop(q);
if (seen.has(u))
continue;
dist[u] = cost, seen.add(u);
for (let [w, v] of (adj[u] || []))
heappush(q, [ dist[u] + w, v ]);
}
return dist;
}
run(filename, queries) {
let adj = {};
let input = new LineByLine(filename);
let line;
while (line = input.next()) {
let words = String.fromCharCode(...line).trim().split('\t');
let u = Number(words[0]);
if (!(u in adj))
adj[u] = [];
for (let i = 1; i < words.length; ++i) {
let [v, w] = words[i].split(',').map(Number);
adj[u].push([ w, v ]);
}
}
let dist = this.dijkstra(adj);
return queries.map(x => dist[x]).join(' ');
}
}
let run = solution => {
console.log(solution.run('problem9.8test.txt', [1, 2, 3, 4, 5, 6, 7, 8]));
console.log(solution.run('problem9.8.txt', [7, 37, 59, 82, 99, 115, 133, 165, 188, 197]));
};
run(new NaiveSolution());
// 0 1 2 3 4 4 3 2
// 2599 2610 2947 2052 2367 2399 2029 2442 2505 3068
run(new HeapSolution());
// 0 1 2 3 4 4 3 2
// 2599 2610 2947 2052 2367 2399 2029 2442 2505 3068
```
*Python3*
```python
from abc import ABC, abstractmethod
from heapq import heappush, heappop
INF = int(1e9 + 7)
class BaseSolution(ABC):
@abstractmethod
def run(self, filename, queries):
raise NotImplementedError
class NaiveSolution(BaseSolution):
def dijkstra(self, E):
dist = {}
seen = set()
start = 1
dist[start] = 0; seen.add(start)
while True:
found = False
best_v = INF
best_w = INF
for u, v, w in E:
if u not in seen or v in seen:
continue
found = True
if best_w > dist[u] + w:
best_v = v
best_w = dist[u] + w
if not found:
break
v, w = best_v, best_w
dist[v] = w; seen.add(v)
return dist
def run(self, filename, queries):
E = []
with open(filename) as fin:
while True:
line = fin.readline()
if not line:
break
words = line.split()
u = int(words[0])
for i in range(1, len(words)):
v, w = map(int, words[i].split(','))
E.append([ u, v, w ])
dist = self.dijkstra(E)
return ' '.join(str(dist[x]) for x in queries)
class HeapSolution(BaseSolution):
def dijkstra(self, adj, start = 1):
dist = {}
seen = set()
q = [[ 0, start ]]
while len(q):
cost, u = heappop(q)
if u in seen:
continue
dist[u] = cost; seen.add(u)
for w, v in adj[u]:
if v not in seen:
heappush(q, [ dist[u] + w, v ])
return dist
def run(self, filename, queries):
adj = {}
with open(filename) as fin:
while True:
line = fin.readline()
if not line:
break
words = line.split()
u = int(words[0])
if u not in adj:
adj[u] = []
for i in range(1, len(words)):
v, w = map(int, words[i].split(','))
adj[u].append([ w, v ])
dist = self.dijkstra(adj)
return ' '.join(str(dist[x]) for x in queries)
def run(solution):
print(solution.run('problem9.8test.txt', [1, 2, 3, 4, 5, 6, 7, 8]))
print(solution.run('problem9.8.txt', [7, 37, 59, 82, 99, 115, 133, 165, 188, 197]))
run(NaiveSolution())
# 0 1 2 3 4 4 3 2
# 2599 2610 2947 2052 2367 2399 2029 2442 2505 3068
run(HeapSolution())
# 0 1 2 3 4 4 3 2
# 2599 2610 2947 2052 2367 2399 2029 2442 2505 3068
```
*C++*
```cpp
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <unordered_map>
#include <unordered_set>
#include <queue>
using namespace std;
using Queries = vector<int>;
using Distance = unordered_map<int, int>;
using Set = unordered_set<int>;
class BaseSolution {
protected:
static constexpr auto INF = int(1e9 + 7);
public:
virtual string run(string filename, Queries&& queries) = 0;
};
class NaiveSolution : public BaseSolution {
using Edge = tuple<int, int, int>;
using Edges = vector<Edge>;
public:
Distance dijkstra(Edges& E) {
Distance dist;
Set seen;
auto start{ 1 };
dist[start] = 0, seen.insert(start);
for (;;) {
auto found = false;
auto best_v = INF,
best_w = INF;
for (auto [u, v, w]: E) {
if (seen.find(u) == seen.end() || seen.find(v) != seen.end())
continue;
found = true;
if (best_w > dist[u] + w)
best_v = v,
best_w = dist[u] + w;
}
if (!found)
break;
auto [v, w] = tie(best_v, best_w);
dist[v] = w, seen.insert(v);
}
return dist;
}
string run(string filename, Queries&& queries) {
Edges E;
fstream fin{ filename };
string line;
int u, v, w;
char _;
while (getline(fin, line)) {
istringstream is{ line };
for (is >> u; is >> v >> _ >> w; E.push_back({ u, v, w }));
}
auto dist = dijkstra(E);
ostringstream os;
transform(queries.begin(), queries.end(), ostream_iterator<int>(os, " "), [&](auto x) { return dist[x]; });
return os.str();
}
};
class HeapSolution : public BaseSolution {
using Pair = pair<int, int>;
using Pairs = vector<Pair>;
using AdjList = unordered_map<int, Pairs>;
priority_queue<Pair, Pairs, std::greater<Pair>> q;
public:
Distance dijkstra(AdjList& adj) {
Distance dist;
Set seen;
for (auto [u, _]: adj)
dist[u] = INF;
auto start{ 1 };
q.push({ 0, start });
while (q.size()) {
auto [cost, u] = q.top(); q.pop();
if (!seen.insert(u).second)
continue;
dist[u] = cost;
for (auto [w, v]: adj[u])
if (seen.find(v) == seen.end())
q.push({ dist[u] + w, v });
}
return dist;
}
string run(string filename, Queries&& queries) {
AdjList adj;
fstream fin{ filename };
string line;
int u, v, w;
char _;
while (getline(fin, line)) {
istringstream is{ line };
for (is >> u; is >> v >> _ >> w; adj[u].push_back({ w, v }));
}
auto dist = dijkstra(adj);
ostringstream os;
transform(queries.begin(), queries.end(), ostream_iterator<int>(os, " "), [&](auto x) { return dist[x]; });
return os.str();
}
};
void run(BaseSolution&& solution) {
cout << "problem9.8test.txt: " << solution.run("problem9.8test.txt", Queries{1, 2, 3, 4, 5, 6, 7, 8 }) << endl
<< "problem9.8.txt " << solution.run("problem9.8.txt", Queries{7, 37, 59, 82, 99, 115, 133, 165, 188, 197 }) << endl;
}
int main() {
run(NaiveSolution());
// problem9.8test.txt: 0 1 2 3 4 4 3 2
// problem9.8.txt 2599 2610 2947 2052 2367 2399 2029 2442 2505 3068
run(HeapSolution());
// problem9.8test.txt: 0 1 2 3 4 4 3 2
// problem9.8.txt 2599 2610 2947 2052 2367 2399 2029 2442 2505 3068
return 0;
}
```
</details>
---
# Part 3: Greedy Algorithms and Dynamic Programming
<br/>
<a href="https://www.amazon.com/dp/0999282948" target="_blank">
<img src="images/ai3large.jpg" />
</a>
---
### Greedy Scheduling
<details><summary>📚 Lectures</summary>
<br/>
* [Introduction to Greedy Algorithms (Section 13.1)](https://www.youtube.com/watch?v=NTFmxA3qgoo&list=PLXFMmlk03Dt5EMI2s2WQBsLsZl7A5HEK6&index=4&t=0s)
* [A Scheduling Problem (Section 13.2)](https://www.youtube.com/watch?v=jK3d_GLzKQA&list=PLXFMmlk03Dt5EMI2s2WQBsLsZl7A5HEK6&index=5)
* [Developing a Greedy Algorithm (Section 13.3)](https://www.youtube.com/watch?v=AJOTgHYjL6A&list=PLXFMmlk03Dt5EMI2s2WQBsLsZl7A5HEK6&index=6)
* [Scheduling: Correctness Proof (Part 1) (Section 13.4, Part 1)](https://www.youtube.com/watch?v=oyLxydVkQwo&list=PLXFMmlk03Dt5EMI2s2WQBsLsZl7A5HEK6&index=7)
* [Scheduling: Correctness Proof (Part 2) (Section 13.4, Part 2)](https://www.youtube.com/watch?v=fmXVPYxmFsU&list=PLXFMmlk03Dt5EMI2s2WQBsLsZl7A5HEK6&index=8)
* [Scheduling: Correctness Proof (Part 3) (Section 13.4, Part 3)](https://www.youtube.com/watch?v=aBFG5PghX68&list=PLXFMmlk03Dt5EMI2s2WQBsLsZl7A5HEK6&index=9)
</details>
<details><summary>🎯 Solutions</summary>
<br/>
*Kotlin*
```kotlin
import java.io.File
data class Job(val weight: Long, val length: Long)
class Solution {
fun minSum(jobs: Array<Job>): Pair<Long, Long> {
class Diff: Comparator<Job> {
override fun compare(a: Job?, b: Job?): Int {
if (a == null || b == null)
return 0
var first = a.weight - a.length
var second = b.weight - b.length
return if (first == second) b.weight.compareTo(a.weight) else second.compareTo(first) // sort by descending difference, break ties in favor of jobs with larger weights
}
}
class Ratio: Comparator<Job> {
override fun compare(a: Job?, b: Job?): Int {
if (a == null || b == null)
return 0
var first = a.weight.toDouble() / a.length
var second = b.weight.toDouble() / b.length
return if (first == second) b.weight.compareTo(a.weight) else second.compareTo(first) // sort by descending difference, break ties in favor of jobs with larger weights
}
}
return Pair(calcSum(jobs, Diff()), calcSum(jobs, Ratio()))
}
private fun calcSum(jobs: Array<Job>, comp: Comparator<Job>): Long {
jobs.sortWith(comp)
var time: Long = 0
var total: Long = 0
jobs.forEach { job ->
time += job.length
total += job.weight * time
}
return total
}
}
fun run(filename: String) {
var jobs = mutableListOf<Job>()
var first = true
File(filename).forEachLine {
if (!first) {
var words = it.trim().split(" ").map{ it.toLong() }
var (weight, length) = words
jobs.add(Job(weight, length))
} else {
first = false
}
}
var (diff, ratio) = Solution().minSum(jobs.toTypedArray())
println("$diff, $ratio") // sub-optimal, optimal
}
fun main() {
run("problem13.4test1.txt") // 23, 22
run("problem13.4test2.txt") // 68615, 67247
run("problem13.4.txt") // 69119377652, 67311454237
}
```
*Javascript*
```javascript
let LineByLine = require('n-readlines');
class Job {
constructor(weight, length) {
this.weight = weight;
this.length = length;
}
}
class Solution {
minSum(jobs) {
let diff = (a, b) => {
let first = a.weight - a.length,
second = b.weight - b.length;
return first == second ? b.weight - a.weight : second - first; // sort by descending difference, break ties in favor of jobs with larger weights
};
let ratio = (a, b) => {
let first = a.weight / a.length,
second = b.weight / b.length;
return first == second ? b.weight - a.weight : second - first; // sort by descending ratio, break ties in favor of jobs with larger weights
};
return [ this._calcSum(jobs, diff), this._calcSum(jobs, ratio) ];
}
_calcSum(jobs, comp, time = 0) {
jobs.sort((a, b) => comp(a, b));
return jobs.reduce((total, job) => total + job.weight * (time += job.length), 0);
}
}
let run = filename => {
let jobs = [];
let input = new LineByLine(filename);
let line = input.next(); // N
while (line = input.next()) {
let words = String.fromCharCode(...line).trim().split(' ');
let [weight, length] = words.map(Number);
jobs.push(new Job(weight, length));
}
let [diff, ratio] = new Solution().minSum(jobs);
console.log(`${diff}, ${ratio}`); // sub-optimal, optimal
};
run('problem13.4test1.txt'); // 23, 22
run('problem13.4test2.txt'); // 68615, 67247
run('problem13.4.txt'); // 69119377652, 67311454237
```
*Python3*
```python
from functools import cmp_to_key
class Job:
def __init__(self, weight, length):
self.weight = weight
self.length = length
class Solution:
def minSum(self, jobs):
def diff(a, b):
first = a.weight - a.length
second = b.weight - b.length
return b.weight - a.weight if first == second else second - first # sort by descending difference, break ties in favor of jobs with larger weights
def ratio(a, b):
first = a.weight / a.length
second = b.weight / b.length
return b.weight - a.weight if first == second else second - first # sort by descending difference, break ties in favor of jobs with larger weights
return [ self._calcSum(jobs, diff), self._calcSum(jobs, ratio) ]
def _calcSum(self, jobs, comp, time = 0, total = 0):
jobs.sort(key = cmp_to_key(lambda a, b: comp(a, b)))
for job in jobs:
time += job.length
total += job.weight * time
return total
def run(filename):
jobs = []
with open(filename) as fin:
line = fin.readline() # N
while True:
line = fin.readline().strip()
if not line:
break
words = line.split()
weight, length = [int(x) for x in words]
jobs.append(Job(weight, length))
diff, ratio = Solution().minSum(jobs)
print(f'{diff}, {ratio}') # sub-optimal, optimal
run('problem13.4test1.txt') # 23, 22
run('problem13.4test2.txt') # 68615, 67247
run('problem13.4.txt') # 69119377652, 67311454237
```
*C++*
```cpp
#include <iostream>
#include <fstream>
#include <vector>
#include <numeric>
using namespace std;
using LL = long long;
struct Job {
LL weight, length;
Job(LL weight, LL length) : weight{ weight }, length{ length } {}
};
using Jobs = vector<Job>;
class Solution {
public:
using Pair = pair<LL, LL>; // sub-optimal, optimal
Pair minSum(Jobs& jobs) {
auto diff = [](auto& a, auto& b) {
auto first = a.weight - a.length,
second = b.weight - b.length;
return first == second ? b.weight < a.weight : second < first; // sort by descending difference, break ties in favor of jobs with larger weights
};
auto ratio = [](auto& a, auto& b) {
auto first = double(a.weight) / a.length,
second = double(b.weight) / b.length;
return first == second ? b.weight < a.weight : second < first; // sort by descending ratio, break ties in favor of jobs with larger weights
};
return { calcSum(jobs, diff), calcSum(jobs, ratio) };
}
private:
template<typename Comp>
LL calcSum(Jobs& jobs, Comp comp, LL time = 0LL) {
sort(jobs.begin(), jobs.end(), comp);
return accumulate(jobs.begin(), jobs.end(), 0LL, [&](LL total, auto& job) {
return total += job.weight * (time += job.length);
});
}
};
void run(const string& filename) {
Jobs jobs;
LL N, weight, length;
fstream fin{ filename };
for (fin >> N; fin >> weight >> length; jobs.emplace_back(Job{ weight, length }));
auto [diff, ratio] = Solution().minSum(jobs);
cout << diff << ", " << ratio << endl;
}
int main() {
run("problem13.4test1.txt"); // 23, 22
run("problem13.4test2.txt"); // 68615, 67247
run("problem13.4.txt"); // 69119377652, 67311454237
return 0;
}
```
</details>
---
### Huffman Codes
<details><summary>📚 Lectures</summary>
<br/>
* [Codes (Section 14.1)](https://www.youtube.com/watch?v=K3WZhFZT6Y0&list=PLXFMmlk03Dt5EMI2s2WQBsLsZl7A5HEK6&index=33)
* [Codes as Trees (Section 14.2)](https://www.youtube.com/watch?v=HESrV5VDu8c&list=PLXFMmlk03Dt5EMI2s2WQBsLsZl7A5HEK6&index=34)
* [Huffman's Greedy Algorithm (Part 1) (Section 14.3, Part 1)](https://www.youtube.com/watch?v=NM6FZB7IfS8&list=PLXFMmlk03Dt5EMI2s2WQBsLsZl7A5HEK6&index=35)
* [Huffman's Greedy Algorithm (Part 2) (Section 14.3, Part 2)](https://www.youtube.com/watch?v=PPmn9osMDyI&list=PLXFMmlk03Dt5EMI2s2WQBsLsZl7A5HEK6&index=36)
* [Huffman's Algorithm: Correctness Proof (Part 1) (Section 14.4, Part 1)](https://www.youtube.com/watch?v=jibgSDjWxdI&list=PLXFMmlk03Dt5EMI2s2WQBsLsZl7A5HEK6&index=37)
* [Huffman's Algorithm: Correctness Proof (Part 2) (Section 14.4, Part 2)](https://www.youtube.com/watch?v=dAjCcqZKYf4&list=PLXFMmlk03Dt5EMI2s2WQBsLsZl7A5HEK6&index=38)
</details>
<details><summary>🎯 Solutions</summary>
<br/>
*Kotlin*
```kotlin
import java.io.File
import java.util.PriorityQueue
import java.util.Queue
import java.util.LinkedList
var INF = (1e9 + 7).toInt()
data class Tree(val weight: Int, val left: Tree? = null, val right: Tree? = null)
/*
fun encode(A: List<Int>): Tree {
var q = PriorityQueue<Tree>(Comparator{ a: Tree, b: Tree -> a.weight.compareTo(b.weight) })
for (weight in A)
q.add(Tree(weight))
while (1 < q.size) {
var a = q.poll()
var b = q.poll()
var c = Tree(a.weight + b.weight, a, b)
q.add(c)
}
return q.poll()
}
*/
/*
* Problem 14.5: Give an implementation of Huffman's greedy algorithm that uses a single invocation
* of a sorting subroutine, followed by a linear amount of additional work.
*/
fun encode(A: MutableList<Int>): Tree {
A.sort()
var first: Queue<Tree> = LinkedList<Tree>(A.map{ weight -> Tree(weight) }.toList())
var second: Queue<Tree> = LinkedList<Tree>()
var next = mutableListOf<Tree>()
while (1 < first.size + second.size) {
next.clear()
do {
if (0 < first.size && 0 < second.size) {
if (first.peek().weight < second.peek().weight) next.add(first.poll()) else next.add(second.poll())
}
else if (0 < first.size) next.add(first.poll())
else if (0 < second.size) next.add(second.poll())
} while (next.size < 2)
var (a, b) = next
var c = Tree(a.weight + b.weight, a, b)
second.add(c)
}
return second.poll()
}
fun run(filename: String): Pair<Int, Int> {
var A = mutableListOf<Int>()
var first = true
File(filename).forEachLine {
if (!first) {
var weight = it.trim().toInt()
A.add(weight)
} else {
first = false
}
}
var tree = encode(A.toMutableList())
var lo = INF
var hi = -INF
fun go(root: Tree? = tree, depth: Int = 0) {
if (root == null)
return
var isLeaf = { node: Tree? -> node?.left == null && node?.right == null }
if (isLeaf(root)) {
lo = Math.min(lo, depth)
hi = Math.max(hi, depth)
} else {
go(root.left, depth + 1)
go(root.right, depth + 1)
}
}
go()
return Pair(lo, hi)
}
fun main() {
for (filename in listOf("problem14.6test1.txt", "problem14.6test2.txt", "problem14.6.txt")) {
var (lo, hi) = run(filename)
println("$filename: $lo, $hi") // min, max encoding length in the corresponding optimal prefix-free tree
}
}
// problem14.6test1.txt: 2, 5
// problem14.6test2.txt: 3, 6
// problem14.6.txt: 9, 19
```
*Javascript*
```javascript
let LineByLine = require('n-readlines');
class Tree {
constructor(weight, left = null, right = null) {
this.weight = weight;
this.left = left;
this.right = right;
}
}
/*
let key = x => Array.isArray(x) ? x[0] : x;
let heappush = (A, x, f = Math.min) => {
let P = i => Math.floor((i - 1) / 2); // parent
A.push(x);
let N = A.length,
i = N - 1;
while (0 < i && key(A[i]) == f(key(A[i]), key(A[P(i)]))) {
[A[i], A[P(i)]] = [A[P(i)], A[i]];
i = P(i);
}
};
let heappop = (A, f = Math.min) => {
let L = i => 2 * i + 1, // children
R = i => 2 * i + 2;
let N = A.length,
i = 0;
let top = A[0];
[A[0], A[N - 1]] = [A[N - 1], A[0]], A.pop(), --N;
let ok;
do {
ok = true;
let left = f == Math.min ? Infinity : -Infinity,
right = left;
if (L(i) < N && key(A[i]) != f(key(A[i]), key(A[L(i)]))) ok = false, left = key(A[L(i)]);
if (R(i) < N && key(A[i]) != f(key(A[i]), key(A[R(i)]))) ok = false, right = key(A[R(i)]);
if (!ok) {
let j = left == f(left, right) ? L(i) : R(i);
[A[i], A[j]] = [A[j], A[i]];
i = j;
}
} while (!ok);
return top;
};
let encode = A => {
let T = [];
for (let weight of A)
heappush(T, [ weight, new Tree(weight) ]);
while (1 < T.length) {
let [ a, b ] = [ heappop(T), heappop(T) ];
let c = [ a[0] + b[0], new Tree(a[0] + b[0], a[1], b[1]) ];
heappush(T, c);
}
return T[0][1];
};
*/
/*
* Problem 14.5: Give an implementation of Huffman's greedy algorithm that uses a single invocation
* of a sorting subroutine, followed by a linear amount of additional work.
*/
let encode = A => {
A.sort((a, b) => a - b)
let first = A.map(weight => new Tree(weight)),
second = [];
while (1 < first.length + second.length) {
let next = [];
while (next.length < 2) {
if (first.length && second.length) {
next.push(first[0].weight < second[0].weight ? first.shift() : second.shift());
}
else if (first.length) next.push(first.shift());
else if (second.length) next.push(second.shift());
}
let [a, b] = next;
let c = new Tree(a.weight + b.weight, a, b);
second.push(c);
}
return second.shift();
};
let run = filename => {
let A = [];
let input = new LineByLine(filename);
let line;
line = input.next(); // N
while (line = input.next()) {
let weight = Number(String.fromCharCode(...line).trim());
A.push(weight);
}
let tree = encode(A);
let [lo, hi] = [Infinity, -Infinity];
let go = (root = tree, depth = 0) => {
if (!root)
return;
let isLeaf = root => !root.left && !root.right;
if (isLeaf(root))
lo = Math.min(lo, depth),
hi = Math.max(hi, depth);
else
go(root.left, depth + 1),
go(root.right, depth + 1);
};
go();
return [ lo, hi ];
}
for (let filename of [ 'problem14.6test1.txt', 'problem14.6test2.txt', 'problem14.6.txt' ]) {
let [lo, hi] = run(filename);
console.log(`${filename}: ${lo}, ${hi}`); // min, max encoding length in the corresponding optimal prefix-free tree
}
// problem14.6test1.txt: 2, 5
// problem14.6test2.txt: 3, 6
// problem14.6.txt: 9, 19
```
*Python3*
```python
class Tree:
def __init__(self, weight, left = None, right = None):
self.weight = weight
self.left = left
self.right = right
def __lt__(self, other):
return self.weight < other.weight
#
# priority queue
#
# from heapq import heappush
# from heapq import heappop
# def encode(A):
# T = []
# for weight in A:
# heappush(T, Tree(weight))
# while 1 < len(T):
# a, b = heappop(T), heappop(T)
# c = Tree(a.weight + b.weight, a, b)
# heappush(T, c)
# return heappop(T)
#
# Problem 14.5: Give an implementation of Huffman's greedy algorithm that uses a single invocation
# of a sorting subroutine, followed by a linear amount of additional work.
#
from collections import deque
def encode(A):
A.sort()
first, second = deque([Tree(weight) for weight in A]), deque()
while 1 < len(first) + len(second):
next = []
while len(next) < 2:
if len(first) and len(second):
next.append(first.popleft() if first[0].weight < second[0].weight else second.popleft())
elif len(first): next.append(first.popleft())
elif len(second): next.append(second.popleft())
a, b = next
c = Tree(a.weight + b.weight, a, b)
second.append(c)
return second.popleft()
def run(filename):
A = []
with open(filename) as fin:
N = int(fin.readline())
while True:
line = fin.readline()
if not line:
break
weight = int(line.strip())
A.append(weight)
tree = encode(A)
lo, hi = float('inf'), float('-inf')
def go(root = tree, depth = 0):
nonlocal lo, hi
if not root:
return
isLeaf = lambda root: not root.left and not root.right
if isLeaf(root):
lo = min(lo, depth)
hi = max(hi, depth)
else:
go(root.left, depth + 1)
go(root.right, depth + 1)
go()
return [ lo, hi ]
for filename in [ 'problem14.6test1.txt', 'problem14.6test2.txt', 'problem14.6.txt' ]:
lo, hi = run(filename)
print(f'{filename}: {lo}, {hi}') # min, max encoding length in the corresponding optimal prefix-free tree
# problem14.6test1.txt: 2, 5
# problem14.6test2.txt: 3, 6
# problem14.6.txt: 9, 19
```
*C++*
```cpp
#include <iostream>
#include <fstream>
#include <vector>
#include <queue>
#include <list>
#define PRIORITY_QUEUE // O(N * logN)
#ifndef PRIORITY_QUEUE
#define TWO_QUEUES // O(N)
#endif
using namespace std;
using LL = long long;
using Weight = LL;
using Weights = vector<Weight>;
struct Tree;
using TreePtr = shared_ptr<Tree>;
struct Tree {
Weight weight;
TreePtr left, right;
Tree(Weight weight, TreePtr left = nullptr, TreePtr right = nullptr) :
weight{ weight }, left{ left }, right{ right } {}
};
using TreePtrs = vector<TreePtr>;
#ifdef PRIORITY_QUEUE
struct Comp {
size_t operator()(const TreePtr& a, const TreePtr& b) const {
return b->weight < a->weight;
}
};
using Queue = priority_queue<TreePtr, TreePtrs, Comp>;
TreePtr encode(const Weights& A, Queue q = {}) {
for (auto weight: A)
q.emplace(make_shared<Tree>(weight));
while (1 < q.size()) {
auto a = q.top(); q.pop();
auto b = q.top(); q.pop();
auto c = make_shared<Tree>(a->weight + b->weight, a, b);
q.emplace(c);
}
return q.top();
}
#else // TWO_QUEUES
/*
* Problem 14.5: Give an implementation of Huffman's greedy algorithm that uses a single invocation
* of a sorting subroutine, followed by a linear amount of additional work.
*/
using Queue = queue<TreePtr>;
TreePtr encode(Weights& A, Queue first = {}, Queue second = {}) {
sort(A.begin(), A.end());
for (auto weight: A)
first.push(make_shared<Tree>(weight));
TreePtrs next;
auto takeFirst = [&]() { next.push_back(first.front()), first.pop(); };
auto takeSecond = [&]() { next.push_back(second.front()), second.pop(); };
while (1 < first.size() + second.size()) {
next.clear();
do {
if (first.size() && second.size()) {
if (first.front()->weight < second.front()->weight) takeFirst(); else takeSecond();
}
else if (first.size()) takeFirst();
else if (second.size()) takeSecond();
} while (next.size() < 2);
auto [a, b] = tie(next[0], next[1]);
auto c = make_shared<Tree>(a->weight + b->weight, a, b);
second.emplace(c);
}
return second.front();
}
#endif
using MinMax = pair<LL, LL>;
constexpr auto Min = numeric_limits<LL>::min();
constexpr auto Max = numeric_limits<LL>::max();
MinMax run(const string& filename) {
Weights A; // weight of each symbol
fstream fin{ filename };
LL N, weight;
for (fin >> N; fin >> weight; A.push_back(weight));
auto tree = encode(A);
LL lo = Max,
hi = Min;
using fun = function<void(TreePtr, int)>;
fun go = [&](auto root, LL depth) {
if (!root)
return;
auto isLeaf = [](auto root) { return !root->left && !root->right; };
if (isLeaf(root))
lo = min(lo, depth),
hi = max(hi, depth);
else
go(root->left, depth + 1),
go(root->right, depth + 1);
};
go(tree, 0);
return make_pair(lo, hi);
}
int main() {
for (auto& filename: { "problem14.6test1.txt", "problem14.6test2.txt", "problem14.6.txt" }) {
auto [lo, hi] = run(filename);
cout << filename << ": " << lo << ", " << hi << endl; // min, max encoding length in the corresponding optimal prefix-free tree
}
// problem14.6test1.txt: 2, 5
// problem14.6test2.txt: 3, 6
// problem14.6.txt: 9, 19
return 0;
}
```
</details>
---
### Prim's MST
<details><summary>📚 Lectures</summary>
<br/>
* [Minimum Spanning Trees: Problem Definition (Section 15.1)](https://www.youtube.com/watch?v=tDj9BkaQDO8&list=PLXFMmlk03Dt5EMI2s2WQBsLsZl7A5HEK6&index=10)
* [Prim's MST Algorithm (Section 15.2)](https://www.youtube.com/watch?v=jsvOPssDVJA&list=PLXFMmlk03Dt5EMI2s2WQBsLsZl7A5HEK6&index=11)
* [Speeding Up Prim's Algorithm via Heaps (Part 1) (Section 15.3, Part 1)](https://www.youtube.com/watch?v=cDtQnXMZGtg&list=PLXFMmlk03Dt5EMI2s2WQBsLsZl7A5HEK6&index=15)
* [Speeding Up Prim's Algorithm via Heaps (Part 2) (Section 15.3, Part 2)](https://www.youtube.com/watch?v=jGR_LAwGLGk&list=PLXFMmlk03Dt5EMI2s2WQBsLsZl7A5HEK6&index=16)
* [Prim's Algorithm: Correctness Proof (Part 1) (Section 15.4, Part 1) [Note: this video provides an alternative treatment to that in the book.]](https://www.youtube.com/watch?v=pGUzn3S7bp4&list=PLXFMmlk03Dt5EMI2s2WQBsLsZl7A5HEK6&index=12)
* [Prim's Algorithm: Correctness Proof (Part 2) (Section 15.4, Part 2) [Note: this video provides an alternative treatment to that in the book.]](https://www.youtube.com/watch?v=199ItGt4mE8&list=PLXFMmlk03Dt5EMI2s2WQBsLsZl7A5HEK6&index=13)
</details>
<details><summary>🎯 Solutions</summary>
<br/>
*Kotlin*
```kotlin
import java.io.File
import java.util.PriorityQueue
import java.util.Random
fun prim(N: Int, adj: MutableMap<Int, MutableList<Pair<Int, Int>>>): Int {
var total: Int = 0
var start = Random().nextInt(N) + 1
var q = PriorityQueue<Pair<Int, Int>>(Comparator{ a: Pair<Int, Int>, b: Pair<Int, Int> -> a.first.compareTo(b.first) })
var seen = mutableSetOf<Int>(start)
for ((w, v) in adj[start]!!)
q.add(Pair(w, v))
while (0 < q.size) {
var (cost, u) = q.poll()
if (seen.contains(u))
continue
total += cost; seen.add(u)
for ((w, v) in adj[u]!!)
if (!seen.contains(v))
q.add(Pair(w, v))
}
return total
}
fun run(filename: String) {
var N: Int = 0
var adj = mutableMapOf<Int, MutableList<Pair<Int, Int>>>()
var first = true
File(filename).forEachLine { line ->
if (!first) {
var (u, v, w) = line.split(" ").map{ it.toInt() }
if (!adj.contains(u)) adj[u] = mutableListOf<Pair<Int, Int>>()
if (!adj.contains(v)) adj[v] = mutableListOf<Pair<Int, Int>>()
adj[u]!!.add(Pair(w, v))
adj[v]!!.add(Pair(w, u))
} else {
var (numVertex, _) = line.split(" ").map{ it.toInt() }
N = numVertex
first = false
}
}
var cost = prim(N, adj)
println("$filename: $cost")
}
fun main() {
run("problem15.9test.txt") // problem15.9test.txt: 14
run("problem15.9.txt") // problem15.9.txt: -3612829
}
```
*Javascript*
```javascript
let LineByLine = require('n-readlines');
let key = x => Array.isArray(x) ? x[0] : x;
let heappush = (A, x, f = Math.min) => {
let P = i => Math.floor((i - 1) / 2); // parent
A.push(x);
let N = A.length,
i = N - 1;
while (0 < i && key(A[i]) == f(key(A[i]), key(A[P(i)]))) {
[A[i], A[P(i)]] = [A[P(i)], A[i]];
i = P(i);
}
};
let heappop = (A, f = Math.min) => {
let L = i => 2 * i + 1, // children
R = i => 2 * i + 2;
let N = A.length,
i = 0;
let top = A[0];
[A[0], A[N - 1]] = [A[N - 1], A[0]], A.pop(), --N;
let ok;
do {
ok = true;
let left = f == Math.min ? Infinity : -Infinity,
right = left;
if (L(i) < N && key(A[i]) != f(key(A[i]), key(A[L(i)]))) ok = false, left = key(A[L(i)]);
if (R(i) < N && key(A[i]) != f(key(A[i]), key(A[R(i)]))) ok = false, right = key(A[R(i)]);
if (!ok) {
let j = left == f(left, right) ? L(i) : R(i);
[A[i], A[j]] = [A[j], A[i]];
i = j;
}
} while (!ok);
return top;
};
let prim = (N, adj, q = [], seen = new Set(), total = 0) => {
let start = Math.ceil(N * Math.random());
seen.add(start);
for (let [w, v] of adj.get(start))
heappush(q, [w, v]);
while (q.length) {
let [cost, u] = heappop(q);
if (seen.has(u))
continue;
total += cost; seen.add(u);
for (let [w, v] of adj.get(u))
if (!seen.has(v))
heappush(q, [w, v]);
}
return total;
};
let run = filename => {
let adj = new Map();
let input = new LineByLine(filename);
let line = input.next();
let [N, M] = String.fromCharCode(...line).trim().split(' ').map(Number);
while (line = input.next()) {
let [u, v, w] = String.fromCharCode(...line).trim().split(' ').map(Number);
if (!adj.has(u)) adj.set(u, []);
if (!adj.has(v)) adj.set(v, []);
adj.get(u).push([w, v]);
adj.get(v).push([w, u]);
}
let cost = prim(N, adj);
console.log(`${filename}: ${cost}`);
}
run('problem15.9test.txt') // problem15.9test.txt: 14
run('problem15.9.txt') // problem15.9.txt: -3612829
```
*Python3*
```python
from random import randint
from heapq import heappush, heappop
def prim(N, adj, total = 0):
q = []
seen = set()
start = randint(1, N); seen.add(start)
for w, v in adj[start]:
heappush(q, [w, v])
while q:
cost, u = heappop(q)
if u in seen:
continue
total += cost; seen.add(u)
for w, v in adj[u]:
if v not in seen:
heappush(q, [w, v])
return total
def run(filename):
adj = {}
first = True
with open(filename) as fin:
while True:
line = fin.readline()
if not line:
break
words = line.split()
if not first:
u, v, w = [int(x) for x in words]
if u not in adj: adj[u] = []
if v not in adj: adj[v] = []
adj[u].append([w, v])
adj[v].append([w, u])
else:
N, M = [int(x) for x in words]
first = False
cost = prim(N, adj)
print(f'{filename}: {cost}')
run('problem15.9test.txt') # problem15.9test.txt: 14
run('problem15.9.txt') # problem15.9.txt: -3612829
```
*C++*
```cpp
#include <iostream>
#include <fstream>
#include <unordered_set>
#include <unordered_map>
#include <queue>
#include <random>
using namespace std;
using Pair = pair<int, int>;
using Pairs = vector<Pair>;
using AdjList = unordered_map<int, Pairs>;
using Queue = priority_queue<Pair, Pairs, std::greater<Pair>>;
using Set = unordered_set<int>;
constexpr auto INF = int(1e9 + 7);
int getRandom(int N) {
default_random_engine generator;
uniform_int_distribution<int> distribution(1, N);
return distribution(generator);
}
int prim(int N, AdjList& adj, Queue q = {}, Set seen = {}, int total = 0) {
auto start = getRandom(N);
seen.insert(start);
for (auto [w, v]: adj[start])
q.push({ w, v });
while (q.size()) {
auto [cost, u] = q.top(); q.pop();
if (!seen.insert(u).second)
continue;
total += cost;
for (auto [w, v]: adj[u])
if (seen.find(v) == seen.end())
q.push({ w, v });
}
return total;
}
void run(const string& filename) {
AdjList adj;
fstream fin{ filename };
int N, M; fin >> N >> M; // N vertices and M edges
int u, v, w; // edge u -> v of weight w
while (fin >> u >> v >> w) {
adj[u].emplace_back(w, v);
adj[v].emplace_back(w, u);
}
auto cost = prim(N, adj);
cout << filename << ": " << cost << endl;
}
int main() {
run("problem15.9test.txt"); // problem15.9test.txt: 14
run("problem15.9.txt"); // problem15.9.txt: -3612829
return 0;
}
```
</details>
---
### Kruskal's MST
<details><summary>📚 Lectures</summary>
<br/>
* [Minimum Spanning Trees: Problem Definition (Section 15.1)](https://www.youtube.com/watch?v=tDj9BkaQDO8&list=PLXFMmlk03Dt5EMI2s2WQBsLsZl7A5HEK6&index=10)
* [Kruskal's MST Algorithm (Section 15.5)](https://www.youtube.com/watch?v=SZuCspj5AJc&list=PLXFMmlk03Dt5EMI2s2WQBsLsZl7A5HEK6&index=17)
* [Speeding Up Kruskal's Algorithm via Union-Find (Part 1) (Section 15.6, Part 1)](https://www.youtube.com/watch?v=fItEZEVyJKE&list=PLXFMmlk03Dt5EMI2s2WQBsLsZl7A5HEK6&index=19)
* [Speeding Up Kruskal's Algorithm via Union-Find (Part 2) (Section 15.6, Part 2) [Note: this video provides an alternative treatment to that in the book.]](https://www.youtube.com/watch?v=jY-vY6d18W4&list=PLXFMmlk03Dt5EMI2s2WQBsLsZl7A5HEK6&index=20)
* [Lazy Unions (Section 15.6, Part 3) [Note: this video is closer to the union-find implementation in the book.]](https://www.youtube.com/watch?v=bRwTSPIEI9k&list=PLXFMmlk03Dt5EMI2s2WQBsLsZl7A5HEK6&index=24)
* [Kruskal's Algorithm: Correctness Proof (Section 15.7) [Note: this video provides an alternative treatment to that in the book.]](https://www.youtube.com/watch?v=AjLjL0Rp10g&list=PLXFMmlk03Dt5EMI2s2WQBsLsZl7A5HEK6&index=18)
* [Application: Single-Link Clustering (Section 15.8)](https://www.youtube.com/watch?v=MSSzOs1X4K8&list=PLXFMmlk03Dt5EMI2s2WQBsLsZl7A5HEK6&index=22)
</details>
<details><summary>🎯 Solutions</summary>
<br/>
*Kotlin*
```kotlin
import java.io.File
fun kruskal(E: MutableList<Triple<Int, Int, Int>>): Int {
var total: Int = 0
var M = E.size
var P = IntArray(M) { it } // 🙂 parent representatives of 1..M disjoint sets
fun find(x: Int): Int {
P[x] = if (P[x] == x) x else find(P[x])
return P[x]
}
fun union(a: Int, b: Int): Boolean {
var x = find(a)
var y = find(b)
if (x == y)
return false
P[x] = y // 🎲 arbitrary choice
return true
}
E.sortWith(Comparator{ a, b -> a.third.compareTo(b.third) }) // sort edges by nondecreasing weight
for ((u, v, w) in E)
if (union(u, v))
total += w
return total
}
fun run(filename: String) {
var E = mutableListOf<Triple<Int, Int, Int>>()
var first = true
File(filename).forEachLine { line ->
if (!first) {
var (u, v, w) = line.trim().split(" ").map{ it.toInt() }
E.add(Triple(u, v, w))
} else {
first = false // ignore first line with N vertices and M edges
}
}
var cost = kruskal(E)
println("$filename: $cost")
}
fun main() {
run("problem15.9test.txt") // problem15.9test.txt: 14
run("problem15.9.txt") // problem15.9.txt: -3612829
}
```
*Javascipt*
```javascript
let LineByLine = require('n-readlines');
let kruskal = E => {
let total = 0;
let M = E.length;
let P = [...Array(M).keys()]; // 🙂 parent representatives of 1..M disjoint sets
let find = x => P[x] = P[x] == x ? x : find(P[x]);
let union = (a, b) => {
a = find(a);
b = find(b);
if (a == b)
return false;
P[a] = b; // 🎲 arbitrary choice
return true;
};
E.sort((first, second) => { // sort edges by nondecreasing weight
let [u1, v1, w1] = first,
[u2, v2, w2] = second;
return w1 - w2;
});
for (let [u, v, w] of E)
if (union(u, v))
total += w;
return total;
};
let run = filename => {
let E = [];
let input = new LineByLine(filename);
let line = input.next(); // ignore first line with N vertices and M edges
while (line = input.next()) {
let [u, v, w] = String.fromCharCode(...line).trim().split(' ').map(Number);
E.push([ u, v, w ]);
}
let cost = kruskal(E);
console.log(`${filename}: ${cost}`);
};
run('problem15.9test.txt'); // problem15.9test.txt: 14
run('problem15.9.txt'); // problem15.9.txt: -3612829
```
*Python3*
```python
from functools import cmp_to_key
def kruskal(E, total = 0):
M = len(E)
P = [i for i in range(M)] # 🙂 parent representatives of 1..M disjoint sets
def find(x):
P[x] = P[x] if P[x] == x else find(P[x])
return P[x]
def union(a, b):
a = find(a)
b = find(b)
if a == b:
return False
P[a] = b # 🎲 arbitary choice
return True
E.sort(key = cmp_to_key(lambda first, second: first[2] - second[2])) # sort edges by nondecreasing weight
for u, v, w in E:
if union(u, v):
total += w
return total
def run(filename):
E = []
first = True
with open(filename) as fin:
while True:
line = fin.readline()
if not line:
break
values = [int(x) for x in line.strip().split()]
if not first:
u, v, w = values # edge u -> v of weight w
E.append([ u, v, w ])
else:
first = False # ignore first line with N vertices and M edges
cost = kruskal(E)
print(f'{filename}: {cost}')
run('problem15.9test.txt') # problem15.9test.txt: 14
run('problem15.9.txt') # problem15.9.txt: -3612829
```
*C++*
```cpp
#include <iostream>
#include <fstream>
#include <vector>
#include <unordered_map>
#include <numeric>
using namespace std;
using Edge = tuple<int, int, int>;
using Edges = vector<Edge>;
using Parents = vector<int>;
using fun = function<int(int)>;
int kruskal(Edges& E, int total = 0) {
auto M = E.size();
Parents P(M); iota(P.begin(), P.end(), 0); // 🙂 parent representatives of 1..M disjoint sets
fun find = [&](auto x) {
return P[x] = P[x] == x ? x : find(P[x]);
};
auto join = [&](auto a, auto b) {
a = find(a);
b = find(b);
if (a == b)
return false;
P[a] = b; // 🎲 arbitrary choice
return true;
};
sort(E.begin(), E.end(), [](auto& first, auto& second) { // sort edges by nondecreasing weight
auto [u1, v1, w1] = first;
auto [u2, v2, w2] = second;
return w1 < w2;
});
for (auto [u, v, w]: E)
if (join(u, v))
total += w;
return total;
}
void run(const string& filename, Edges E = {}) {
fstream fin{ filename };
int N, M; fin >> N >> M; // ignore first line with N vertices and M edges
int u, v, w; // edge u -> v of weight w
while (fin >> u >> v >> w)
E.emplace_back(u, v, w);
auto cost = kruskal(E);
cout << filename << ": " << cost << endl;
}
int main() {
run("problem15.9test.txt"); // problem15.9test.txt: 14
run("problem15.9.txt"); // problem15.9.txt: -3612829
return 0;
}
```
</details>
---
### Weighted Independent Set
<details><summary>📚 Lectures</summary>
<br/>
* [The Weighted Independent Set Problem (Section 16.1)](https://www.youtube.com/watch?v=0awkct8SkxA&list=PLXFMmlk03Dt5EMI2s2WQBsLsZl7A5HEK6&index=39)
* [A Linear-Time Algorithm for WIS in Path Graphs (Part 1) (Section 16.2, Part 1)](https://www.youtube.com/watch?v=pLOkbHGRsv0&list=PLXFMmlk03Dt5EMI2s2WQBsLsZl7A5HEK6&index=40)
* [A Linear-Time Algorithm for WIS in Path Graphs (Part 2) (Section 16.2, Part 2)](https://www.youtube.com/watch?v=Im_zjFkZDCY&list=PLXFMmlk03Dt5EMI2s2WQBsLsZl7A5HEK6&index=41)
* [A Reconstruction Algorithm (Section 16.3)](https://www.youtube.com/watch?v=W2ncNfksRqo&list=PLXFMmlk03Dt5EMI2s2WQBsLsZl7A5HEK6&index=42)
</details>
<details><summary>🎯 Solutions</summary>
<br/>
*Kotlin*
```kotlin
import java.io.File
fun topDown(A: MutableList<Long>): Long {
var N = A.size
var m = mutableMapOf<Int, Long>()
fun go(i: Int = N - 1): Long {
if (m.contains(i)) // 🤔 memo
return m[i]!!
if (i < 0) { // 🛑 empty set
m[i] = 0
return 0
}
if (i == 0) { // 🛑 single set
m[i] = A[0]
return A[0]
}
var include = go(i - 2) + A[i] // ✅ include A[i]
var exclude = go(i - 1) // 🚫 exclude A[i]
m[i] = Math.max(include, exclude) // 🎯 best
return m[i]!!
}
return go()
}
fun bottomUp(A: MutableList<Long>): Long {
var N = A.size
var dp = LongArray(N + 1) // 🤔 memo
dp[0] = 0 // 🛑 empty set
dp[1] = A[0] // 🛑 single set
for (i in 2..N) {
var include = dp[i - 2] + A[i - 1] // ✅ include A[i] (use A[i - 1] since dp[i] is offset by 1 for explicit 🛑 empty set at index 0, ie. index -1 doesn't exist)
var exclude = dp[i - 1] // 🚫 exclude A[i]
dp[i] = Math.max(include, exclude) // 🎯 best
}
return dp[N]
}
fun bottomUpMemOpt(A: MutableList<Long>): Long {
var N = A.size
var a: Long = 0 // 🤔 memo + 🛑 empty set
var b: Long = A[0] // 🤔 memo + 🛑 single set
var c: Long = -1
for (i in 2..N) {
var include = a + A[i - 1] // ✅ include A[i] (use A[i - 1] since dp[i] is offset by 1 for explicit 🛑 empty set at index 0, ie. index -1 doesn't exist)
var exclude = b // 🚫 exclude A[i]
c = Math.max(include, exclude) // 🎯 best
a = b; b = c // 👈 slide window
}
return c
}
fun run(filename: String) {
var A = mutableListOf<Long>()
var first = true
File(filename).forEachLine { line ->
if (!first) {
A.add(line.toLong())
} else {
first = false
}
}
var a = topDown(A)
var b = bottomUp(A)
var c = bottomUpMemOpt(A)
assert(a == b && b == c) // 💩 sanity check
println("$filename: $a")
}
fun main() {
run("problem16.6test.txt") // problem16.6test.txt: 2617
run("problem16.6.txt") // problem16.6.txt: 2955353732
}
```
*Javascript*
```javascript
const assert = require('assert');
const LineByLine = require('n-readlines');
let top_down = (A, m = {}) => {
let N = A.length;
let go = (i = N - 1) => {
if (m[i]) // 🤔 memo
return m[i];
if (i < 0) return m[i] = 0; // 🛑 empty set
if (!i) return m[i] = A[0]; // 🛑 single set
let include = go(i - 2) + A[i], // ✅ include A[i]
exclude = go(i - 1); // 🚫 exclude A[i]
return m[i] = Math.max(include, exclude); // 🎯 best
};
return go();
};
let bottom_up = A => {
let N = A.length;
let dp = Array(N + 1); // 🤔 memo
dp[0] = 0; // 🛑 empty set
dp[1] = A[0]; // 🛑 single set
for (let i = 2; i <= N; ++i) {
let include = dp[i - 2] + A[i - 1], // ✅ include A[i] (use A[i - 1] since dp[i] is offset by 1 for explicit 🛑 empty set at index 0, ie. index -1 doesn't exist)
exclude = dp[i - 1]; // 🚫 exclude A[i]
dp[i] = Math.max(include, exclude); // 🎯 best
}
return dp[N];
};
let bottom_up_memopt = A => {
let N = A.length;
let a = 0, // 🤔 memo + 🛑 empty set
b = A[0], // 🤔 memo + 🛑 single set
c = -1;
for (let i = 2; i <= N; ++i) {
let include = a + A[i - 1], // ✅ include A[i] (use A[i - 1] since dp[i] is offset by 1 for explicit 🛑 empty set at index 0, ie. index -1 doesn't exist)
exclude = b; // 🚫 exclude A[i]
c = Math.max(include, exclude); // 🎯 best
a = b, b = c; // 👈 slide window
}
return c;
};
let run = filename => {
let A = [];
let input = new LineByLine(filename);
let line;
let first = true;
while (line = input.next()) {
if (!first) {
A.push(Number(line.toString('ascii')));
} else {
first = false;
}
}
let a = top_down(A),
b = bottom_up(A),
c = bottom_up_memopt(A);
assert(a == b && b == c); // 💩 sanity check
console.log(`${filename}: ${a}`);
};
run('problem16.6test.txt'); // problem16.6test.txt: 2617
run('problem16.6.txt'); // problem16.6.txt: 2955353732
```
*Python3*
```python
from functools import lru_cache
def top_down(A):
N = len(A)
@lru_cache # 🤔 memo
def go(i = N - 1):
if i < 0: return 0 # 🛑 empty set
if i == 0: return A[0] # 🛑 single set
include = go(i - 2) + A[i] # ✅ include A[i]
exclude = go(i - 1) # 🚫 exclude A[i]
return max(include, exclude) # 🎯 best
return go()
def bottom_up(A):
N = len(A)
dp = [0] * (N + 1) # 🤔 memo
dp[0] = 0 # 🛑 empty set
dp[1] = A[0] # 🛑 single set
for i in range(2, N + 1):
include = dp[i - 2] + A[i - 1] # ✅ include A[i] (use A[i - 1] since dp[i] is offset by 1 for explicit 🛑 empty set at index 0, ie. index -1 doesn't exist)
exclude = dp[i - 1] # 🚫 exclude A[i]
dp[i] = max(include, exclude) # 🎯 best
return dp[N]
def bottom_up_memopt(A):
N = len(A)
a = 0 # 🤔 memo + 🛑 empty set
b = A[0] # 🤔 memo + 🛑 single set
c = -1
for i in range(2, N + 1):
include = a + A[i - 1] # ✅ include A[i] (use A[i - 1] since dp[i] is offset by 1 for explicit 🛑 empty set at index 0, ie. index -1 doesn't exist)
exclude = b # 🚫 exclude A[i]
c = max(include, exclude) # 🎯 best
a = b; b = c # 👈 slide window
return c
def run(filename):
A = []
with open(filename) as fin:
first = True
while True:
line = fin.readline()
if not line:
break
x = int(line)
if not first:
A.append(x)
else:
first = False
N = x
a = top_down(A)
b = bottom_up(A)
c = bottom_up_memopt(A)
assert(a == b and b == c) # 💩 sanity check
print(f'{filename}: {a}')
run('problem16.6test.txt') # problem16.6test.txt: 2617
run('problem16.6.txt') # problem16.6.txt: 2955353732
```
*C++*
```cpp
#include <cassert>
#include <iostream>
#include <fstream>
#include <vector>
#include <unordered_map>
using namespace std;
using LL = long long;
using List = vector<LL>;
using Map = unordered_map<int, LL>;
namespace TopDown {
LL best(List& A, Map m = {}) {
int N = A.size();
using fun = function<LL(int)>;
fun go = [&](auto i) {
if (m[i]) return m[i]; // 🤔 memo
if (i < 0) return m[i] = 0LL; // 🛑 empty set
if (!i) return m[i] = A[0]; // 🛑 single set
auto include = go(i - 2) + A[i], // ✅ include A[i]
exclude = go(i - 1); // 🚫 exclude A[i]
return m[i] = max(include, exclude); // 🎯 best
};
return go(N - 1);
}
}
namespace BottomUp {
LL best(List& A, Map m = {}) {
int N = A.size();
List dp(N + 1); // 🤔 memo
dp[0] = 0LL; // 🛑 empty set
dp[1] = A[0]; // 🛑 single set
for (auto i{ 2 }; i <= N; ++i) {
auto include = dp[i - 2] + A[i - 1], // ✅ include A[i] (use A[i - 1] since dp[i] is offset by 1 for explicit 🛑 empty set at index 0, ie. index -1 doesn't exist)
exclude = dp[i - 1]; // 🚫 exclude A[i]
dp[i] = max(include, exclude); // 🎯 best
}
return dp[N];
}
}
namespace BottomUpMemOpt {
LL best(List& A) {
int N = A.size();
LL a = 0LL, // 🤔 memo + 🛑 empty set
b = A[0], // 🤔 memo + 🛑 single set
c = -1;
for (auto i{ 2 }; i <= N; ++i) {
auto include = a + A[i - 1], // ✅ include A[i] (use A[i - 1] since dp[i] is offset by 1 for explicit 🛑 empty set at index 0, ie. index -1 doesn't exist)
exclude = b; // 🚫 exclude A[i]
c = max(include, exclude); // 🎯 best
a = b, b = c; // 👈 slide window
}
return c;
}
}
void run(const string& filename) {
List A;
fstream fin{ filename };
int N; fin >> N;
copy_n(istream_iterator<LL>(fin), N, back_inserter(A));
auto a = TopDown::best(A),
b = BottomUp::best(A),
c = BottomUpMemOpt::best(A);
assert(a == b && b == c); // 💩 sanity check
cout << filename << ": " << a << endl;
}
int main() {
run("problem16.6test.txt"); // problem16.6test.txt: 2617
run("problem16.6.txt"); // problem16.6.txt: 2955353732
return 0;
}
```
</details>
---
### Knapsack
<details><summary>📚 Lectures</summary>
<br/>
* [Principles of Dynamic Programming (Section 16.4)](https://www.youtube.com/watch?v=27nK8C-GCPM&list=PLXFMmlk03Dt5EMI2s2WQBsLsZl7A5HEK6&index=43)
* [The Knapsack Problem (Part 1) (Section 16.5, Part 1)](https://www.youtube.com/watch?v=jlHIBaUizOU&list=PLXFMmlk03Dt5EMI2s2WQBsLsZl7A5HEK6&index=44)
* [The Knapsack Problem (Part 2) (Section 16.5, Part 2)](https://www.youtube.com/watch?v=KX_6OF8X6HQ&list=PLXFMmlk03Dt5EMI2s2WQBsLsZl7A5HEK6&index=45)
* [The Knapsack Problem (Part 3) (Section 16.5, Part 3)](https://www.youtube.com/watch?v=1dRUOZKcvYs&list=PLXFMmlk03Dt5EMI2s2WQBsLsZl7A5HEK6&index=46)
</details>
<details><summary>🎯 Solutions</summary>
<br/>
*Kotlin*
```kotlin
import java.io.File
var INF = (1e9 + 7).toInt()
fun top_down(A: List<Pair<Int, Int>>, K: Int): Int {
var N = A.size
var m = mutableMapOf<String, Int>()
fun go(i: Int = 0, k: Int = K): Int {
if (i == N) // 🛑 empty set
return 0
var key = "$i,$k"
if (m.contains(key)) // 🤔 memo
return m[key]!!
var (value, weight) = A[i]
var include = if (0 <= k - weight) go(i + 1, k - weight) + value else -INF // ✅ include A[i]
var exclude = go(i + 1, k) // 🚫 exclude A[i]
m[key] = Math.max(include, exclude) // 🎯 best
return m[key]!!
}
return go()
}
fun bottom_up(A: List<Pair<Int, Int>>, K: Int): Int {
var N = A.size
var dp = Array(N + 1){ Array(K + 1){ -INF } } // 🤔 memo
for (k in 0..K) // 🛑 empty set
dp[0][k] = 0
for (i in 1..N) {
for (k in 0..K) {
var (value, weight) = A[i - 1]
var include = if (0 <= k - weight) dp[i - 1][k - weight] + value else -INF // ✅ include A[i]
var exclude = dp[i - 1][k] // 🚫 exclude A[i]
dp[i][k] = Math.max(include, exclude) // 🎯 best
}
}
return dp[N][K]
}
fun bottom_up_memopt(A: List<Pair<Int, Int>>, K: Int): Int {
var N = A.size
var pre = Array(K + 1) { 0 } // 🤔 memo + 🛑 empty set
for (i in 1..N) {
var cur = Array(K + 1) { -INF }
for (k in 0..K) {
var (value, weight) = A[i - 1]
var include = if (0 <= k - weight) pre[k - weight] + value else -INF // ✅ include A[i]
var exclude = pre[k] // 🚫 exclude A[i]
cur[k] = Math.max(include, exclude) // 🎯 best
}
pre = cur.also { cur = pre }
}
return pre[K]
}
fun run(filename: String) {
var A = mutableListOf<Pair<Int, Int>>()
var K = 0
var N = 0
var first = true
File(filename).forEachLine { line ->
if (!first) {
var (value, weight) = line.trim().split(" ").map{ it -> it.toInt() }
A.add(Pair(value, weight))
} else {
var (a, b) = line.trim().split(" ").map{ it -> it.toInt() }
K = a
N = b
first = false
}
}
var a = top_down(A, K)
var b = bottom_up(A, K)
var c = bottom_up_memopt(A, K)
assert(a == b && b == c) // 💩 sanity check
println("$filename: $a")
}
fun main() {
run("problem16.7test.txt") // problem16.7test.txt: 2493893
}
```
*Javascript*
```javascript
const assert = require('assert');
const LineByLine = require('n-readlines');
let top_down = (A, K, m = new Map()) => {
let N = A.length;
let go = (i = 0, k = K) => {
if (i == N) // 🛑 empty set
return 0;
let key = `${i},${k}`;
if (m.has(key)) // 🤔 memo
return m.get(key);
let [value, weight] = A[i];
let include = 0 <= k - weight ? go(i + 1, k - weight) + value : -Infinity, // ✅ include A[i]
exclude = go(i + 1, k); // 🚫 exclude A[i]
return m.set(key, Math.max(include, exclude)) // 🎯 best
.get(key);
};
return go();
};
let bottom_up = (A, K) => {
let N = A.length;
let dp = [...Array(N + 1)].map(_ => Array(K + 1).fill(-Infinity)); // 🤔 memo
for (let k = 0; k < K; dp[0][k++] = 0); // 🛑 empty set
for (let i = 1; i <= N; ++i) {
for (let k = 0; k <= K; ++k) {
let [value, weight] = A[i - 1];
let include = 0 <= k - weight ? dp[i - 1][k - weight] + value : -Infinity, // ✅ include A[i]
exclude = dp[i - 1][k]; // 🚫 exclude A[i]
dp[i][k] = Math.max(include, exclude); // 🎯 best
}
}
return dp[N][K];
};
let bottom_up_memopt = (A, K) => {
let N = A.length;
let pre = Array(K + 1).fill(0); // 🤔 memo + 🛑 empty set
for (let i = 1; i <= N; ++i) {
let cur = Array(K + 1).fill(-Infinity);
for (let k = 0; k <= K; ++k) {
let [value, weight] = A[i - 1];
let include = 0 <= k - weight ? pre[k - weight] + value : -Infinity, // ✅ include A[i]
exclude = pre[k]; // 🚫 exclude A[i]
cur[k] = Math.max(include, exclude); // 🎯 best
}
[pre, cur] = [cur, pre];
}
return pre[K];
};
let run = filename => {
let A = [];
const input = new LineByLine(filename)
let [K, N] = input.next().toString().split(' ').map(Number); // K capacity, N items
let line;
while (line = input.next()) {
let [value, weight] = line.toString().split(' ').map(Number);
A.push([value, weight]);
}
let a = top_down(A, K),
b = bottom_up(A, K),
c = bottom_up_memopt(A, K);
assert(a == b && b == c); // 💩 sanity check
console.log(`${filename}: ${a}`);
};
run('problem16.7test.txt') // problem16.7test.txt: 2493893
```
*Python3*
```python
from functools import lru_cache
def top_down(A, K):
N = len(A)
total = [0] * N
@lru_cache(maxsize = None) # 🤔 memo
def go(i = 0, k = K):
if i == N: # 🛑 empty set
return 0
value, weight = A[i]
include = go(i + 1, k - weight) + value if 0 <= k - weight else float('-inf') # ✅ include A[i]
exclude = go(i + 1, k) # 🚫 exclude A[i]
return max(include, exclude) # 🎯 best
return go()
def bottom_up(A, K):
N = len(A)
dp = [[float('-inf')] * (K + 1) for _ in range(N + 1)] # 🤔 memo
for j in range(K): # 🛑 empty set
dp[0][j] = 0
for i in range(1, N + 1):
for k in range(1, K + 1):
value, weight = A[i - 1]
include = dp[i - 1][k - weight] + value if 0 <= k - weight else float('-inf') # ✅ include A[i]
exclude = dp[i - 1][k] # 🚫 exclude A[i]
dp[i][k] = max(include, exclude) # 🎯 best
return dp[N][K]
def bottom_up_memopt(A, K):
N = len(A)
pre = [0] * (K + 1) # 🤔 memo + 🛑 empty set
for i in range(1, N + 1):
cur = [float('-inf')] * (K + 1)
for k in range(1, K + 1):
value, weight = A[i - 1]
include = pre[k - weight] + value if 0 <= k - weight else float('-inf') # ✅ include A[i]
exclude = pre[k] # 🚫 exclude A[i]
cur[k] = max(include, exclude) # 🎯 best
pre, cur = cur, pre
return pre[K]
def run(filename):
A = []
with open(filename) as fin:
line = fin.readline()
[K, N] = [int(word) for word in line.split()] # K capacity, N items
while True:
line = fin.readline()
if not line:
break
value, weight = [int(word) for word in line.split()]
A.append([value, weight])
a = top_down(A, K)
b = bottom_up(A, K)
c = bottom_up_memopt(A, K)
assert(a == b and b == c) # 💩 sanity check
print(f'{filename}: {a}')
run('problem16.7test.txt') # problem16.7test.txt: 2493893
```
*C++*
```cpp
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <unordered_map>
using namespace std;
using Pair = pair<int, int>; // value, weight
using Pairs = vector<Pair>;
using fun = function<int(int, int)>;
using Map = unordered_map<string, int>;
int INF = 1e9 + 7;
int top_down(Pairs& A, int K, Map m = {}) {
auto N = A.size();
fun go = [&](auto i, auto k) {
if (i == N) // 🛑 empty set
return 0;
stringstream key; key << i << "," << k;
if (m.find(key.str()) != m.end()) // 🤔 memo
return m[key.str()];
auto [value, weight] = A[i];
auto include = 0 <= k - weight ? go(i + 1, k - weight) + value : -INF, // ✅ include A[i]
exclude = go(i + 1, k); // 🚫 exclude A[i]
return m[key.str()] = max(include, exclude); // 🎯 best
};
return go(0, K);
}
int bottom_up(Pairs& A, int K) {
auto N = A.size();
using VI = vector<int>;
using VVI = vector<VI>;
VVI dp(N + 1, VI(K + 1, -INF)); // 🤔 memo
for (auto k{ 0 }; k < K; dp[0][k++] = 0); // 🛑 empty set
for (auto i{ 1 }; i <= N; ++i) {
for (auto k{ 0 }; k <= K; ++k) {
auto [value, weight] = A[i - 1];
auto include = 0 <= k - weight ? dp[i - 1][k - weight] +value : -INF, // ✅ include A[i]
exclude = dp[i - 1][k]; // 🚫 exclude A[i]
dp[i][k] = max(include, exclude); // 🎯 best
}
}
return dp[N][K];
}
int bottom_up_memopt(Pairs& A, int K) {
auto N = A.size();
using VI = vector<int>;
VI pre(K + 1, 0); // 🤔 memo + 🛑 empty set
for (auto i{ 1 }; i <= N; ++i) {
VI cur(K + 1, -INF);
for (auto k{ 0 }; k <= K; ++k) {
auto [value, weight] = A[i - 1];
auto include = 0 <= k - weight ? pre[k - weight] +value : -INF, // ✅ include A[i]
exclude = pre[k]; // 🚫 exclude A[i]
cur[k] = max(include, exclude); // 🎯 best
}
swap(pre, cur);
}
return pre[K];
}
void run(const string& filename) {
Pairs A;
fstream fin{ filename };
int K, N; // K capacity, N items
fin >> K >> N;
for (int value, weight; fin >> value >> weight; A.emplace_back(value, weight));
auto a = top_down(A, K),
b = bottom_up(A, K),
c = bottom_up_memopt(A, K);
assert(a == b && b == c); // 💩 sanity check
cout << filename << ": " << a << endl;
}
int main() {
run("problem16.7test.txt"); // problem16.7test.txt: 2493893
return 0;
}
```
</details>
---
### Bellman-Ford
<details><summary>📚 Lectures</summary>
<br/>
* [Shortest Paths with Negative Edge Lengths (Section 18.1)](https://www.youtube.com/watch?v=oyHoqtxHiOs&list=PLXFMmlk03Dt5EMI2s2WQBsLsZl7A5HEK6&index=54)
* [The Bellman-Ford Algorithm (Part 1) (Section 18.2, Part 1)](https://www.youtube.com/watch?v=06OK99Aak60&list=PLXFMmlk03Dt5EMI2s2WQBsLsZl7A5HEK6&index=55)
* [The Bellman-Ford Algorithm (Part 2) (Section 18.2, Part 2)](https://www.youtube.com/watch?v=d1TWZa20Mkw&list=PLXFMmlk03Dt5EMI2s2WQBsLsZl7A5HEK6&index=56)
* [The Bellman-Ford Algorithm (Part 3) (Section 18.2, Part 3)](https://www.youtube.com/watch?v=HaXTsC8eOfo&list=PLXFMmlk03Dt5EMI2s2WQBsLsZl7A5HEK6&index=57)
* [The Bellman-Ford Algorithm (Part 4) (Section 18.2, Part 4)](https://www.youtube.com/watch?v=Otn0kbliRUQ&list=PLXFMmlk03Dt5EMI2s2WQBsLsZl7A5HEK6&index=58)
</details>
<details><summary>🎯 Solutions</summary>
<br/>
*Kotlin*
```kotlin
import java.io.File
import java.util.LinkedList
import java.util.Queue
// bellman-ford: N - 1 edge relaxations (u -> v of cost w) [ie. attempting to relax M edges N - 1 times] given N vertices
fun bell(E: Array<Triple<Int, Int, Int>>, N: Int, start: Int = 1, INF: Int = (1e6).toInt()): IntArray {
var dist = IntArray(N) { INF }
dist[start] = 0
var K = N - 1
while (0 < K--)
E.forEach{ (u, v, w) -> dist[v] = Math.min(dist[v], dist[u] + w)}
return dist
}
// shortest-paths faster algorithm: only attempt to relax candidate edges (note: adjacency list needed)
fun spfa(E: Array<Triple<Int, Int, Int>>, N: Int, start: Int = 1, INF: Int = (1e6).toInt()): IntArray {
var dist = IntArray(N) { INF }
dist[start] = 0
var adj = Array<MutableList<Pair<Int, Int>>>(N) { mutableListOf<Pair<Int, Int>>() }
for ((u, v, w) in E)
adj[u].add(Pair(v, w))
var q: Queue<Int> = LinkedList<Int>(listOf(start))
while (0 < q.size) {
var u = q.poll()
for ((v, w) in adj[u]) {
if (dist[v] > dist[u] + w) {
dist[v] = dist[u] + w; q.add(v)
}
}
}
return dist
}
fun run(filename: String): IntArray {
var N = 0
var E = mutableListOf<Triple<Int, Int, Int>>()
File(filename).forEachLine {
var A = ArrayDeque(it.trim().split("\t"))
var u = A.removeFirst().toInt()
for ((v, w) in A.map{ it.split(",").map{ it.toInt() } })
E.add(Triple(u, v, w))
++N;
}
var A = E.toTypedArray()
var a = bell(A, N + 1) // +1 for 1-based indexing
var b = spfa(A, N + 1)
assert(a == b) // 💩 sanity check: single source shortest paths are the same
return b
}
fun main() {
var dist = run("test.txt")
println(listOf(7, 37, 59, 82, 99, 115, 133, 165, 188, 197).map{ dist[it] }.joinToString(",")) // 2599,2610,2947,2052,2367,2399,2029,2442,2505,3068
}
```
*Javascript*
```javascript
const assert = require('assert');
const zip = require('lodash/zip');
const LineByLine = require('n-readlines');
// bellman-ford: N - 1 edge relaxations (u -> v of cost w) [ie. attempting to relax M edges N - 1 times] given N vertices
let bell = (E, N, start = 1, INF = Number(1e6)) => {
let dist = Array(N).fill(INF);
dist[start] = 0;
let K = N - 1;
while (K--)
E.forEach(([u, v, w]) => dist[v] = Math.min(dist[v], dist[u] + w));
return dist;
};
// shortest-paths faster algorithm: only attempt to relax candidate edges (note: adjacency list needed)
let spfa = (E, N, start = 1, INF = Number(1e6)) => {
let dist = Array(N).fill(INF);
dist[start] = 0;
let adj = [...Array(N)].map(_ => []);
E.forEach(([u, v, w]) => adj[u].push([v, w]));
let q = [start];
while (q.length) {
let u = q.shift();
for (let [v, w] of adj[u])
if (dist[v] > dist[u] + w)
dist[v] = dist[u] + w, q.push(v);
}
return dist;
};
let run = filename => {
let N = 0;
let E = [];
let input = new LineByLine(filename);
let line;
while (line = input.next()) {
let A = line.toString('ascii').split('\t').filter(it => it.length);
let u = Number(A.shift());
A.map(pair => pair.split(',').map(Number)).forEach(([v, w]) => E.push([u, v, w]));
++N;
}
let a = bell(E, N + 1), // +1 for 1-based indexing
b = spfa(E, N + 1);
zip(a, b).forEach(([x, y]) => assert(x == y)); // 💩 sanity check: single source shortest paths are the same
return a;
};
let dist = run('test.txt');
console.log([7, 37, 59, 82, 99, 115, 133, 165, 188, 197].map(x => dist[x]).join(',')); // 2599,2610,2947,2052,2367,2399,2029,2442,2505,3068
```
*Python3*
```python
from collections import deque
# bellman-ford: N - 1 edge relaxations (u -> v of cost w) [ie. attempting to relax M edges N - 1 times] given N vertices
def bell(E, N, start = 1, INF = int(1e6)):
dist = [INF] * N
dist[start] = 0
k = N - 1
while k:
for u, v, w in E:
dist[v] = min(dist[v], dist[u] + w)
k -= 1
return dist
# shortest-paths faster algorithm: only attempt to relax candidate edges (note: adjacency list needed)
def spfa(E, N, start = 1, INF = int(1e6)):
dist = [INF] * N
dist[start] = 0
adj = {i: [] for i in range(N)}
for u, v, w in E:
adj[u].append([v, w])
q = deque([start])
while q:
u = q.popleft()
for v, w in adj[u]:
if dist[v] > dist[u] + w:
dist[v] = dist[u] + w; q.append(v)
return dist
def run(filename):
E = []
N = 0
with open(filename) as fin:
while True:
line = fin.readline()
if not line:
break
line = line.strip()
A = [word for word in line.split('\t') if len(word)]
u = int(A[0])
for i in range(1, len(A)):
v, w = [int(x) for x in A[i].split(',')]
E.append([u, v, w])
N += 1
a = bell(E, N + 1) # +1 for 1-based indexing
b = spfa(E, N + 1)
assert(a == b)
return b
dist = run('test.txt')
print(','.join(str(dist[x]) for x in [7, 37, 59, 82, 99, 115, 133, 165, 188, 197])) # 2599,2610,2947,2052,2367,2399,2029,2442,2505,3068
```
*C++*
```cpp
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <unordered_map>
#include <queue>
using namespace std;
using VI = vector<int>;
using VS = vector<string>;
using Edge = tuple<int, int, int>;
using Edges = vector<Edge>;
using Pair = pair<int, int>;
using Pairs = vector<Pair>;
using AdjList = unordered_map<int, Pairs>;
using Queue = queue<int>;
// bellman-ford: N - 1 edge relaxations (u -> v of cost w) [ie. attempting to relax M edges N - 1 times] given N vertices
VI bell(Edges& E, int N, int start = 1, int INF = 1e6) {
VI dist(N, INF);
dist[start] = 0;
auto K = N - 1;
while (K--)
for (auto [u, v, w]: E)
dist[v] = min(dist[v], dist[u] + w);
return dist;
}
// shortest-paths faster algorithm: only attempt to relax candidate edges (note: adjacency list needed)
VI spfa(Edges& E, int N, int start = 1, int INF = 1e6, AdjList adj = {}) {
VI dist(N, INF);
dist[start] = 0;
for (auto [u, v, w]: E)
adj[u].emplace_back(v, w);
Queue q{{ start }};
while (q.size()) {
auto u = q.front(); q.pop();
for (auto [v, w]: adj[u])
if (dist[v] > dist[u] + w)
dist[v] = dist[u] + w, q.push(v);
}
return dist;
}
VI run(const string& filename) {
auto N = 0;
Edges E;
fstream fin{ filename };
VS A;
for (string line; getline(fin, line); A.emplace_back(line));
for (auto& s: A) {
transform(s.begin(), s.end(), s.begin(), [](auto c) { return c == ',' ? ' ' : c; });
stringstream ss{ s };
auto [u, v, w] = make_tuple(0, 0, 0);
ss >> u;
while (ss >> v >> w)
E.emplace_back(u, v, w);
++N;
}
auto a = bell(E, N + 1), // +1 for 1-based indexing
b = spfa(E, N + 1);
assert(a == b);
return b;
}
int main() {
auto dist = run("test.txt");
VI A{ 7, 37, 59, 82, 99, 115, 133, 165, 188, 197 };
transform(A.begin(), A.end(), A.begin(), [&](auto x) { return dist[x]; });
copy(A.begin(), A.end(), ostream_iterator<int>(cout, ",")); // 2599,2610,2947,2052,2367,2399,2029,2442,2505,3068,
return 0;
}
```
</details>
---
### Floyd-Warshall
<details><summary>📚 Lectures</summary>
<br/>
* [The All-Pairs Shortest Path Problem (Section 18.3)](https://www.youtube.com/watch?v=TENbWZPz3Ho&list=PLXFMmlk03Dt5EMI2s2WQBsLsZl7A5HEK6&index=62)
* [The Floyd-Warshall Algorithm (Part 1) (Section 18.4, Part 1)](https://www.youtube.com/watch?v=ogcvCr02gqM&list=PLXFMmlk03Dt5EMI2s2WQBsLsZl7A5HEK6&index=63)
* [The Floyd-Warshall Algorithm (Part 2) (Section 18.4, Part 2)](https://www.youtube.com/watch?v=3cBHwPjDZxg&list=PLXFMmlk03Dt5EMI2s2WQBsLsZl7A5HEK6&index=64)
</details>
<details><summary>🎯 Solutions</summary>
<br/>
*Kotlin*
```kotlin
import java.io.File
var key = { i: Int, j: Int -> "$i,$j" }
var INF = (1e9 + 7).toInt()
fun floyd_warshall(E: MutableMap<String, Int>, N: Int): Array<IntArray> {
var dp = Array(N + 1) { Array(N + 1) { IntArray(N + 1) { INF } } }
for (i in 0..N)
for (j in 0..N)
if (i == j)
dp[0][i][j] = 0
else
if (E.contains(key(i, j)))
dp[0][i][j] = E[key(i, j)]!!
for (k in 1..N)
for (i in 1..N)
for (j in 1..N)
dp[k][i][j] = Math.min(dp[k - 1][i][j], dp[k - 1][i][k] + dp[k - 1][k][j])
return dp[N]
}
fun floyd_warshall_memopt(E: MutableMap<String, Int>, N: Int): Array<IntArray> {
var pre = Array(N + 1) { IntArray(N + 1) { INF } }
for (i in 0..N)
for (j in 0..N)
if (i == j)
pre[i][j] = 0
else
if (E.contains(key(i, j)))
pre[i][j] = E[key(i, j)]!!
for (k in 1..N) {
var cur = Array(N + 1) { IntArray(N + 1) { INF } }
for (i in 1..N)
for (j in 1..N)
cur[i][j] = Math.min(pre[i][j], pre[i][k] + pre[k][j])
pre = cur.also{ cur = pre }
}
return pre
}
fun run(filename: String) {
var N = 0
var E = mutableMapOf<String, Int>()
var first = true
File(filename).forEachLine {
if (!first) {
var (u, v, w) = it.trim().split(" ").map{ it.toInt() }
E[key(u, v)] = w
} else {
N = it.trim().split(" ").map{ it.toInt() }[0]
first = false
}
}
var a = floyd_warshall_memopt(E, N)
var b = floyd_warshall(E, N)
for (i in 1..N)
for (j in 1..N)
assert(a[i][j] == b[i][j]) // 💩 sanity check
var cycle = false
for (i in 1..N)
if (a[i][i] < 0)
cycle = true
if (cycle) {
println("$filename: contains a negative cycle")
return
}
var best = INF
for (i in 1..N)
for (j in 1..N)
best = Math.min(best, a[i][j])
println("$filename: $best")
}
fun main() {
run("problem18.8test1.txt"); // problem18.8test1.txt: -2
run("problem18.8test2.txt"); // problem18.8test2.txt: contains a negative cycle
run("problem18.8file1.txt"); // problem18.8file1.txt: contains a negative cycle
run("problem18.8file2.txt"); // problem18.8file2.txt: contains a negative cycle
run("problem18.8file3.txt"); // problem18.8file3.txt: -19
// run("problem18.8file4.txt");
}
```
*Javascript*
```javascript
const LineByLine = require('n-readlines');
const assert = require('assert');
let key = (i, j) => `${i},${j}`;
let floyd_warshall = (E, N) => {
let dp = [...Array(N + 1)].map(_ => [...Array(N + 1)].map(_ => Array(N + 1).fill(Infinity)));
for (let i = 1; i <= N; ++i)
for (let j = 1; j <= N; ++j)
if (i == j)
dp[0][i][j] = 0;
else
if (E.has(key(i, j)))
dp[0][i][j] = E.get(key(i, j));
for (let k = 1; k <= N; ++k)
for (let i = 1; i <= N; ++i)
for (let j = 1; j <= N; ++j)
dp[k][i][j] = Math.min(dp[k - 1][i][j], dp[k - 1][i][k] + dp[k - 1][k][j]);
return dp[N];
};
let floyd_warshall_memopt = (E, N) => {
let pre = [...Array(N + 1)].map(_ => Array(N + 1).fill(Infinity));
for (let i = 1; i <= N; ++i)
for (let j = 1; j <= N; ++j)
if (i == j)
pre[i][j] = 0;
else
if (E.has(key(i, j)))
pre[i][j] = E.get(key(i, j));
for (let k = 1; k <= N; ++k) {
let cur = [...Array(N + 1)].map(_ => Array(N + 1).fill(Infinity));
for (let i = 1; i <= N; ++i)
for (let j = 1; j <= N; ++j)
cur[i][j] = Math.min(pre[i][j], pre[i][k] + pre[k][j]);
[pre, cur] = [cur, pre];
}
return pre;
};
let run = filename => {
let E = new Map();
let input = new LineByLine(filename);
let [N, _] = input.next().toString('ascii').split(' ').map(Number);
let line;
while (line = input.next()) {
let [u, v, w] = line.toString('ascii').split(' ').map(Number);
E.set(key(u, v), w);
}
let a = floyd_warshall_memopt(E, N),
b = floyd_warshall(E, N);
for (let i = 1; i <= N; ++i)
for (let j = 1; j <= N; ++j)
assert(a[i][j] == b[i][j]);
let cycle = false;
for (let i = 1; i <= N; ++i)
if (a[i][i] < 0)
cycle = true;
if (cycle) {
console.log(`${filename}: contains a negative cycle`);
return;
}
var best = Infinity;
for (row of a)
best = Math.min(best, ...row);
console.log(`${filename}: ${best}`);
};
run('problem18.8test1.txt'); // problem18.8test1.txt: -2
run('problem18.8test2.txt'); // problem18.8test2.txt: contains a negative cycle
run('problem18.8file1.txt'); // problem18.8file1.txt: contains a negative cycle
run('problem18.8file2.txt'); // problem18.8file2.txt: contains a negative cycle
run('problem18.8file3.txt'); // problem18.8file3.txt: -19
// run('problem18.8file4.txt');
```
*Python3*
```python
key = lambda i, j: f'{i},{j}'
def floyd_warshall(E, N):
dp = [[[float('inf')] * (N + 1) for _ in range(N + 1)] for _ in range(N + 1)]
for i in range(1, N + 1):
for j in range(1, N + 1):
if i == j:
dp[0][i][j] = 0
elif key(i, j) in E:
dp[0][i][j] = E[key(i, j)]
for k in range(1, N + 1):
for i in range(1, N + 1):
for j in range(1, N + 1):
dp[k][i][j] = min(dp[k - 1][i][j], dp[k - 1][i][k] + dp[k - 1][k][j])
return dp[N]
def floyd_warshall_memopt(E, N):
pre = [[float('inf')] * (N + 1) for _ in range(N + 1)]
for i in range(1, N + 1):
for j in range(1, N + 1):
if i == j:
pre[i][j] = 0
elif key(i, j) in E:
pre[i][j] = E[key(i, j)]
for k in range(1, N + 1):
cur = [[float('inf')] * (N + 1) for _ in range(N + 1)]
for i in range(1, N + 1):
for j in range(1, N + 1):
cur[i][j] = min(pre[i][j], pre[i][k] + pre[k][j])
pre, cur = cur, pre
return pre
def run(filename):
E = {}
N = 0
first = True
with open(filename) as fin:
while True:
line = fin.readline()
if not line:
break
if not first:
u, v, w = [int(x) for x in line.strip().split(' ')]
E[key(u, v)] = w
else:
N = [int(x) for x in line.strip().split(' ')][0]
first = False
a = floyd_warshall_memopt(E, N)
b = floyd_warshall(E, N)
for i in range(1, N + 1):
for j in range(1, N + 1):
assert(a[i][j] == b[i][j]) # 💩 sanity check
cycle = False
for i in range(1, N + 1):
if a[i][i] < 0:
cycle = True
if cycle:
print(f'{filename}: contains a negative cycle')
return
best = float('inf')
for row in a:
best = min(best, *row)
print(f'{filename}: {best}')
run('problem18.8test1.txt') # problem18.8test1.txt: -2
run('problem18.8test2.txt') # problem18.8test2.txt: contains a negative cycle
run('problem18.8file1.txt') # problem18.8file1.txt: contains a negative cycle
run('problem18.8file2.txt') # problem18.8file2.txt: contains a negative cycle
run('problem18.8file3.txt') # problem18.8file3.txt: -19
# run('problem18.8file4.txt')
```
*C++*
```cpp
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <unordered_map>
#define PERF_TEST
using namespace std;
using LL = long long;
using VL = vector<LL>;
using VVL = vector<VL>;
using VVVL = vector<VVL>;
using Edges = unordered_map<string, LL>;
LL INF = 1e9 + 7;
string key(int i, int j) {
stringstream ss; ss << i << "," << j;
return ss.str();
}
VVL floyd_warshall(Edges& E, int N) {
VVVL dp(N + 1, VVL(N + 1, VL(N + 1, INF)));
for (auto i{ 1 }; i <= N; ++i)
for (auto j{ 1 }; j <= N; ++j)
if (i == j)
dp[0][i][j] = 0;
else
if (E.find(key(i, j)) != E.end())
dp[0][i][j] = E[key(i, j)];
for (auto k{ 1 }; k <= N; ++k)
for (auto i{ 1 }; i <= N; ++i)
for (auto j{ 1 }; j <= N; ++j)
dp[k][i][j] = min(dp[k - 1][i][j], dp[k - 1][i][k] + dp[k - 1][k][j]);
return dp[N];
}
VVL floyd_warshall_memopt(Edges& E, int N) {
VVL pre(N + 1, VL(N + 1, INF));
for (auto i{ 1 }; i <= N; ++i)
for (auto j{ 1 }; j <= N; ++j)
if (i == j)
pre[i][j] = 0;
else
if (E.find(key(i, j)) != E.end())
pre[i][j] = E[key(i, j)];
for (auto k{ 1 }; k <= N; ++k) {
VVL cur(N + 1, VL(N + 1, INF));
for (auto i{ 1 }; i <= N; ++i)
for (auto j{ 1 }; j <= N; ++j)
cur[i][j] = min(pre[i][j], pre[i][k] + pre[k][j]);
swap(pre, cur);
}
return pre;
}
void run(const string& filename) {
Edges E;
fstream fin{ filename };
int N, M; fin >> N >> M;
for (int u, v, w; fin >> u >> v >> w; E[key(u, v)] = w);
#ifdef PERF_TEST
auto a = floyd_warshall_memopt(E, N);
#else
auto a = floyd_warshall_memopt(E, N),
b = floyd_warshall(E, N);
assert(a == b); // 💩 sanity check
#endif
auto cycle = false;
for (auto i{ 1 }; i <= N && !cycle; ++i)
cycle = a[i][i] < 0;
if (cycle) {
cout << filename << ": contains a negative cycle" << endl;
return;
}
auto best = INF;
for (auto& row: a)
best = min(best, *min_element(row.begin(), row.end()));
cout << filename << ": " << best << endl;
}
int main() {
run("problem18.8test1.txt"); // problem18.8test1.txt: -2
run("problem18.8test2.txt"); // problem18.8test2.txt: contains a negative cycle
run("problem18.8file1.txt"); // problem18.8file1.txt: contains a negative cycle
run("problem18.8file2.txt"); // problem18.8file2.txt: contains a negative cycle
run("problem18.8file3.txt"); // problem18.8file3.txt: -19
// run("problem18.8file4.txt");
return 0;
}
```
</details>
---
# Part 4: Algorithms for NP-Hard Problems
<br/>
<a href="https://www.amazon.com/dp/0999282964" target="_blank">
<img src="images/ai4large.jpg" />
</a>
---
================================================
FILE: Algorithms-Illuminated/src/main.rs
================================================
fn main() {
println!("Hello, world!");
}
================================================
FILE: Algorithms-Illuminated/target/.rustc_info.json
================================================
{"rustc_fingerprint":10491184667718451318,"outputs":{"17598535894874457435":{"success":true,"status":"","code":0,"stdout":"rustc 1.57.0 (f1edd0429 2021-11-29)\nbinary: rustc\ncommit-hash: f1edd0429582dd29cccacaf50fd134b05593bd9c\ncommit-date: 2021-11-29\nhost: x86_64-apple-darwin\nrelease: 1.57.0\nLLVM version: 13.0.0\n","stderr":""},"2797684049618456168":{"success":true,"status":"","code":0,"stdout":"___\nlib___.rlib\nlib___.dylib\nlib___.dylib\nlib___.a\nlib___.dylib\n","stderr":""},"931469667778813386":{"success":true,"status":"","code":0,"stdout":"___\nlib___.rlib\nlib___.dylib\nlib___.dylib\nlib___.a\nlib___.dylib\n/Users/clayton.wonng/.rustup/toolchains/stable-x86_64-apple-darwin\ndebug_assertions\nproc_macro\ntarget_arch=\"x86_64\"\ntarget_endian=\"little\"\ntarget_env=\"\"\ntarget_family=\"unix\"\ntarget_feature=\"fxsr\"\ntarget_feature=\"sse\"\ntarget_feature=\"sse2\"\ntarget_feature=\"sse3\"\ntarget_feature=\"ssse3\"\ntarget_os=\"macos\"\ntarget_pointer_width=\"64\"\ntarget_vendor=\"apple\"\nunix\n","stderr":""},"15537503139010883884":{"success":true,"status":"","code":0,"stdout":"___\nlib___.rlib\nlib___.dylib\nlib___.dylib\nlib___.a\nlib___.dylib\n","stderr":""}},"successes":{}}
================================================
FILE: Algorithms-Illuminated/target/CACHEDIR.TAG
================================================
Signature: 8a477f597d28d172789f06886806bc55
# This file is a cache directory tag created by cargo.
# For information about cache directory tags see https://bford.info/cachedir/
================================================
FILE: README.md
================================================
# Algorithms Illuminated
* [algorithmsilluminated.org](https://www.algorithmsilluminated.org/)
Coincidentally, my [algorithm learning journey](https://github.com/claytonjwong/Algorithms) which began in 2017 has occurred in parallel with the publication of Tim Roughgarden's (TR) 4-book series about algorithms and data structures. Over these years, I've purchased, studied, and provided feedback on TR's books. I was totally stoked when TR sent me a free copy of his 4th book for review before publication in 2020! I'm amazed by what can be done in near-linear time (ie. the amount of time to perform an algorithm is on the order of time to simply read the input), and it's awesome we can leverage these "for-free primitives" based upon computationally tractable problems as "building blocks" towards more complex solutions to computationally intractable (NP-Hard) problems via selective compromise on generality, correctness, and speed (ie. pick 2 of 3). [`💡` Can we do better?](https://en.wikipedia.org/wiki/Millennium_Prize_Problems#P_versus_NP)
<p>
<img src="images/TR1.png" height="256" width="400" />
<img src="images/TR2.png" height="256" width="400" />
</p>
---
# Part 1: The Basics
<br/>
<a href="https://www.amazon.com/dp/0999282905" target="_blank">
<img src="images/ai1large.jpg" />
</a>
<details><summary>⚡️ TL;DR ⚡️</summary>
<br/>
> A "fast algorithm" is an algorithm whose worst-case runtime grows slowly with the input size.
* **Recursion Tree:** each child node corresponds to recusive invocations by its parent node
* helpful visualization of work done by recursive algorithms to quantify aymptotic bounds
* **Algorithm Analysis Principles**
1. Worst-Case
* no input assumptions for general-purpose analysis
2. Big-Picture
* drop constant factors and lower-order terms for runtime upper bound
* mathematical tractability
* primitive operation runtime depends on environmental factors
* retains accurate predictions for slow/fast algorithms
3. Asymptotic Analysis
* focus on the runtime growth for large inputs
* only large inputs require algorithmic ingenuity
* **For-Free Primitives**
* Linear-time algorithms (the "holy grail")
* Runtime is proportional to the input size
* "Free" since the time to perform the algorithm is on the order of time to simply read the input
* **Asymptotic Notation**: suppress constant factors and lower-order terms to focus on how an algorithm's runtime scales with input size
* constant factors are too system dependent
* lower-order terms are irrelevant for large inputs
* **Big-O Notation:** `T(n) = O(f(n))` if-and-only-if `T(n)` is eventually bounded above by a constant multiple of `f(n)`
* `T(n) = O(f(n))` if-and-only-if there exists positive constants *c* and *n*<sub>0</sub> such that `T(n) <= c * O(f(n))` for all *n* greater-than-or-equal-to *n*<sub>0</sub>
</details>
---
### Integer Multiplication
<details><summary>📚 Lectures</summary>
<br/>
* [Introduction: Why Study Algorithms?](https://www.youtube.com/watch?v=yRM3sc57q0c&list=PLEGCF-WLh2RLHqXx6-GZr_w7LgqKDXxN_&index=1)
* [Introduction: Integer Multiplication](https://www.youtube.com/watch?v=6u0Vaj4nn54&list=PLEGCF-WLh2RLHqXx6-GZr_w7LgqKDXxN_&index=2)
</details>
<details><summary>🎯 Solutions</summary>
<br/>
*Python3*
```python
def go(x, y):
if x < 10 or y < 10:
return x * y
n = max(len(str(x)), len(str(y)))
if n & 1:
n += 1 # +1 is n is odd
m = 10 ** (n // 2)
a, b = x // m, x % m
c, d = y // m, y % m
p, q = a + b, c + d
ac = go(a, c)
bd = go(b, d)
pq = go(p, q)
adbc = pq - ac - bd
return 10**n * ac + 10**(n//2) * adbc + bd
x = 3141592653589793238462643383279502884197169399375105820974944592
y = 2718281828459045235360287471352662497757247093699959574966967627
assert(x * y == go(x, y))
```
*Julia*
```julia
using Test, Random
function go(x, y)
if x < 10 || y < 10
return x * y
end
n = max(length(string(x)), length(string(y)))
if isodd(n)
n += 1
end
m = 10 ^ div(n, 2) # middle decimal value
a, b = div(x, m), mod(x, m)
c, d = div(y, m), mod(y, m)
ac = go(a, c)
ad = go(a, d)
bc = go(b, c)
bd = go(b, d)
return 10^n * ac + 10^div(n, 2) * (ad + bc) + bd
end
Random.seed!(123456789)
@testset "Recursive Integer Multiplication tests" begin
for _ in 1:100
n = rand((1, 2, 4, 8, 16))
lo = 10^(n-1)
hi = 10^n - 1
x = rand(lo:hi)
y = rand(lo:hi)
expect, actual = x * y, go(x, y)
@test expect == actual
println("($x x $y)\nexpect: $expect\nactual: $actual\n")
end
end
```
</details>
---
### Karatsuba Multiplication
<details><summary>📚 Lectures</summary>
<br/>
* [Introduction: Karatsuba Multiplication](https://www.youtube.com/watch?v=JCbZayFr9RE&list=PLEGCF-WLh2RLHqXx6-GZr_w7LgqKDXxN_&index=3)
</details>
<details><summary>🎯 Solutions</summary>
<br/>
*Julia*
```julia
using Test, Random
function go(x, y)
if x < 10 || y < 10
return x * y
end
n = max(length(string(x)), length(string(y)))
if isodd(n)
n += 1
end
m = 10 ^ div(n, 2) # middle decimal value
a, b = div(x, m), mod(x, m)
c, d = div(y, m), mod(y, m)
p, q = a + b, c + d
ac = go(a, c)
bd = go(b, d)
pq = go(p, q)
adbc = pq - ac - bd
return 10^n * ac + 10^div(n, 2) * adbc + bd
end
Random.seed!(123456789)
@testset "Karatsuba Multiplication tests" begin
for _ in 1:100
n = rand((1, 2, 4, 8, 16))
lo = 10^(n-1)
hi = 10^n - 1
x = rand(lo:hi)
y = rand(lo:hi)
expect, actual = x * y, go(x, y)
@test expect == actual
println("($x x $y)\nexpect: $expect\nactual: $actual\n")
end
end
```
</details>
---
### Merge Sort
<details><summary>📚 Lectures</summary>
<br/>
* [MergeSort: Motivation and Example](https://www.youtube.com/watch?v=kiyRJ7GVWro&list=PLEGCF-WLh2RLHqXx6-GZr_w7LgqKDXxN_&index=4) (Section 1.4, part 1)
* [MergeSort: Pseudocode](https://www.youtube.com/watch?v=rBd5w0rQaFo&list=PLEGCF-WLh2RLHqXx6-GZr_w7LgqKDXxN_&index=5) (Section 1.4, part 2)
* [MergeSort: Analysis](https://www.youtube.com/watch?v=8ArtRiTkYEw&list=PLEGCF-WLh2RLHqXx6-GZr_w7LgqKDXxN_&index=6) (Section 1.5)
</details>
<details><summary>🎯 Solutions</summary>
<br/>
*Kotlin*
```java
fun sort(A: IntArray): IntArray {
fun merge(A: IntArray, B: IntArray): IntArray {
var C = mutableListOf<Int>()
var i = 0
var j = 0
while (i < A.size && j < B.size)
if (A[i] < B[j])
C.add(A[i++])
else
C.add(B[j++])
A.slice(i..A.lastIndex).forEach { C.add(it) }
B.slice(j..B.lastIndex).forEach { C.add(it) }
return C.toIntArray()
}
fun go(A: IntArray): IntArray {
var N = A.size
if (N < 2)
return A
var half = Math.floor(N / 2.0).toInt()
var first = go(A.slice(0 until half).toIntArray())
var second = go(A.slice(half until N).toIntArray())
return merge(first, second)
}
return go(A)
}
fun main(args: Array<String>) {
sort(intArrayOf(5,3,8,9,1,7,0,2,6,4)).forEach { print("$it ") } // 0 1 2 3 4 5 6 7 8 9
println()
}
```
*Javascript*
```javascript
let sort = A => {
let go = A => {
let N = A.length;
if (N < 2)
return A;
let half = Math.floor(N / 2);
let first = go([...A.slice(0, half)]),
second = go([...A.slice(half, N)]);
return merge(first, second);
};
let merge = (A, B, C = []) => {
let M = A.length,
N = B.length;
let i = 0,
j = 0;
while (i < M && j < N)
C.push(A[i] < B[j] ? A[i++] : B[j++]);
C.push(...A.slice(i, M));
C.push(...B.slice(j, N));
return C;
};
return go(A);
};
console.log(sort([5,3,8,9,1,7,0,2,6,4])); // (10) [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
```
```julia
function go(A)
N = length(A)
if N < 2
return A
end
half = div(N, 2)
first = go(A[begin:half])
second = go(A[half+1:end])
return merge(first, second)
end
function merge(A, B)
C = []
i, j, k = 1, 1, 1
while i <= length(A) && j <= length(B)
if A[i] < B[j]
push!(C, A[i])
i += 1
else
push!(C, B[j])
j += 1
end
k += 1
end
append!(C, @view A[i:end])
append!(C, @view B[j:end])
return C
end
println(go([5, 3, 8, 9, 1, 7, 0, 2, 6, 4])) # Any[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
```
*Python3*
```python
from math import floor
def sort(A):
def go(A):
N = len(A)
if N < 2:
return A
half = floor(N / 2)
first = go(A[:half])
second = go(A[half:])
return merge(first, second)
def merge(A, B):
C = []
i = 0
j = 0
while i < len(A) and j < len(B):
if A[i] < B[j]:
C.append(A[i]); i += 1
else:
C.append(B[j]); j += 1
C.extend(A[i:])
C.extend(B[j:])
return C
return go(A)
print(sort([5,3,8,9,1,7,0,2,6,4])) # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
```
*C++*
```cpp
#include <iostream>
#include <vector>
using namespace std;
class Solution {
public:
using VI = vector<int>;
VI mergesort(VI& A) {
return go(move(A));
}
private:
VI go(VI&& A) {
auto N = A.size();
if( N < 2 )
return A;
auto half = A.begin() + (N / 2);
auto first = go({ A.begin(), half }),
second = go({ half, A.end() });
return merge(first, second);
}
VI merge(VI& A, VI& B, VI C = {}) {
auto i{ 0 },
j{ 0 };
while (i < A.size() && j < B.size())
C.push_back(A[i] < B[j] ? A[i++] : B[j++]);
C.insert(C.end(), A.begin() + i, A.end());
C.insert(C.end(), B.begin() + j, B.end());
return C;
}
};
int main() {
Solution::VI A{ 3,5,7,1,3,9,2,0 };
auto ans = Solution().mergesort(A);
copy(ans.begin(), ans.end(), ostream_iterator<int>(cout, " ")), cout << endl; // 0 1 2 3 4 5 6 7 8 9
return 0;
}
```
</details>
---
### Counting Inversions
<details><summary>📚 Lectures</summary>
<br/>
* [The Divide-and-Conquer Paradigm](https://www.youtube.com/watch?v=7_AJfusC6UQ&list=PLEGCF-WLh2RLHqXx6-GZr_w7LgqKDXxN_&index=13) (Section 3.1; part 1 of Section 3.2)
* [Counting Inversions in O(n log n)](https://www.youtube.com/watch?v=I6ygiW8xN7Y&list=PLEGCF-WLh2RLHqXx6-GZr_w7LgqKDXxN_&index=14) Time (Section 3.2, part 2)
</details>
<details><summary>🎯 Solutions</summary>
<br/>
*Kotlin*
```java
import java.io.File
fun sort(A: IntArray): Pair<IntArray, Long> {
fun merge(A: IntArray, B: IntArray): Pair<IntArray, Long> {
var C = mutableListOf<Int>()
var inv: Long = 0
var i = 0
var j = 0
while (i < A.size && j < B.size)
if (A[i] < B[j]) {
C.add(A[i++])
} else {
inv += A.size - i // ⭐️ B[j] comes before all remaining A[i...], thus all remaining A[i...] are inversions
C.add(B[j++])
}
A.slice(i..A.lastIndex).forEach { C.add(it) }
B.slice(j..B.lastIndex).forEach { C.add(it) }
return Pair(C.toIntArray(), inv)
}
fun go(A: IntArray): Pair<IntArray, Long> {
var N = A.size
if (N < 2)
return Pair(A, 0)
var half = Math.floor(N / 2.0).toInt()
var (first, inv1) = go(A.slice(0 until half).toIntArray())
var (second, inv2) = go(A.slice(half until N).toIntArray())
var (third, inv3) = merge(first, second)
return Pair(third, inv1 + inv2 + inv3)
}
return go(A)
}
fun run(filename: String): Long {
var A = mutableListOf<Int>()
File(filename).forEachLine { A.add(it.toInt()) }
var (_, inv) = sort(A.toIntArray())
return inv
}
fun main() {
println("problem3.5test.txt: " + run("problem3.5test.txt")) // problem3.5test.txt: 28
println("problem3.5.txt: " + run("problem3.5.txt")) // problem3.5.txt: 2407905288
}
```
*Javascript*
```javascript
let sort = A => {
let go = A => {
let N = A.length;
if (N < 2)
return [A, 0];
let half = Math.floor(N / 2);
let [first, inv1] = go([...A.slice(0, half)]),
[second, inv2] = go([...A.slice(half, N)]),
[third, inv3] = merge(first, second);
return [third, inv1 + inv2 + inv3];
};
let merge = (A, B, C = [], inv = 0) => {
let M = A.length,
N = B.length;
let i = 0,
j = 0;
while (i < M && j < N)
if (A[i] < B[j])
C.push(A[i++]);
else
inv += M - i, // ⭐️ B[j] comes before all remaining A[i...], thus all remaining A[i...] are inversions
C.push(B[j++]);
C.push(...A.slice(i, M));
C.push(...B.slice(j, N));
return [C, inv];
};
return go(A);
};
let run = filename => {
let A = [];
require('fs').readFileSync(filename, 'utf-8').split(/\r?\n/).forEach(line => A.push(Number(line)));
let [_, inv] = sort(A);
return inv;
}
console.log(`problem3.5test.txt: ${run('problem3.5test.txt')}`); // problem3.5test.txt: 28
console.log(`problem3.5.txt: ${run('problem3.5.txt')}`); // problem3.5.txt: 2407905288
```
*Julia*
```julia
function go(A)
N = length(A)
if N < 2
return A, 0
end
half = div(N, 2)
first, inv1 = go(A[begin:half])
second, inv2 = go(A[half+1:end])
third, inv3 = merge(first, second)
return third, inv1 + inv2 + inv3
end
function merge(A, B)
C, inv = [], 0
i, j, k = 1, 1, 1
while i <= length(A) && j <= length(B)
if A[i] < B[j]
push!(C, A[i])
i += 1
else
push!(C, B[j]); inv += length(A) - i + 1 # ⭐️ B[j] comes before all remaining A[i...], thus all remaining A[i...] are inversions
j += 1
end
k += 1
end
append!(C, @view A[i:end])
append!(C, @view B[j:end])
return C, inv
end
function run(filename)
input = Int[]
open(filename, "r") do file
for line in eachline(file)
push!(input, parse(Int, strip(line)))
end
end
_, inv = go(input)
return inv
end
println(run("problem3.5test.txt")) # 28
println(run("problem3.5.txt")) # 2407905288
```
*Python3*
```python
from math import floor
def sort(A):
def go(A):
N = len(A)
if N < 2:
return [A, 0]
half = floor(N / 2)
first, inv1 = go(A[:half])
second, inv2 = go(A[half:])
third, inv3 = merge(first, second)
return [third, inv1 + inv2 + inv3]
def merge(A, B, inv = 0):
C = []
i = 0
j = 0
while i < len(A) and j < len(B):
if A[i] < B[j]:
C.append(A[i]); i += 1
else:
inv += len(A) - i # ⭐️ B[j] comes before all remaining A[i...], thus all remaining A[i...] are inversions
C.append(B[j]); j += 1
C.extend(A[i:])
C.extend(B[j:])
return [C, inv]
return go(A)
def run(filename):
A = []
with open(filename) as fin:
while True:
line = fin.readline()
if not line:
break
A.append(int(line))
_, inv = sort(A)
return inv
print(f"problem3.5test.txt: {run('problem3.5test.txt')}") # problem3.5test.txt: 28
print(f"problem3.5.txt: {run('problem3.5.txt')}") # problem3.5.txt: 2407905288
```
*C++*
```cpp
#include <iostream>
#include <vector>
#include <fstream>
using namespace std;
class Solution {
public:
using VL = vector<long>;
using Pair = pair<VL, long>;
using fun = function<Pair(VL&&)>;
Pair merge(VL& A, VL& B, VL C = {}, long inv = 0) {
auto i = 0,
j = 0;
while (i < A.size() && j < B.size()) {
if (A[i] < B[j]) {
C.push_back(A[i++]);
} else {
inv += A.size() - i; // ⭐️ B[j] comes before all remaining A[i...], thus all remaining A[i...] are inversions
C.push_back(B[j++]);
}
}
C.insert(C.end(), A.begin() + i, A.end());
C.insert(C.end(), B.begin() + j, B.end());
return { C, inv };
}
Pair inversions(VL& A) {
fun go = [&](VL&& A) -> Pair {
int N = A.size();
if (N < 2)
return { A, 0 };
int half = N / 2;
auto [first, inv1] = go({ A.begin(), A.begin() + half });
auto [second, inv2] = go({ A.begin() + half, A.end() });
auto [third, inv3] = merge(first, second);
return { third, inv1 + inv2 + inv3 };
};
return go(move(A));
}
};
long run(string filename) {
Solution solution;
Solution::VL A;
fstream fin{ filename };
for (string line; fin >> line; A.push_back(stol(line)));
auto [_, inv] = solution.inversions(A);
return inv;
}
int main() {
cout << "problem3.5test.txt: " << run("problem3.5test.txt") << endl // problem3.5test.txt: 28
<< "problem3.5.txt: " << run("problem3.5.txt") << endl; // problem3.5.txt: 2407905288
return 0;
}
```
</details>
---
### Recursive Matrix Multiplication
<details><summary>📚 Lectures</summary>
<br/>
* [Strassen 's Subcubic Matrix Multiplication Algorithm](https://www.youtube.com/watch?v=ORrM-aSNZUs&list=PLEGCF-WLh2RLHqXx6-GZr_w7LgqKDXxN_&index=15)
</details>
<details><summary>🎯 Solutions</summary>
<br/>
* Assume N is a power of 2 for simplicity
#### Iterative Matrix Multiplication
* **Cubic runtime:** O(N<sup>3</sup>)
*Python3*
```python
N = 2
X = [[1, 2],
[3, 4]]
Y = [[5, 6],
[7, 8]]
Z = [[0] * N for _ in range(N)]
for i in range(N):
for j in range(N):
for k in range(N):
Z[i][j] += X[i][k] * Y[k][j]
```
#### Standard Recursive Matrix Multiplication
* **Cubic runtime:** O(N<sup>3</sup>)
*Python3*
```python
import numpy as np
def go(X, Y):
n = X.shape[0]
# Base case: 1x1 matrix
if n == 1:
return X * Y
# Divide: Partition A and B into n/2 x n/2 submatrices
k = n // 2
A, B = X[:k, :k], X[:k, k:]
C, D = X[k:, :k], X[k:, k:]
E, F = Y[:k, :k], Y[:k, k:]
G, H = Y[k:, :k], Y[k:, k:]
# Combine: Reconstruct the full matrix from quadrants
Z = np.zeros((n, n), dtype=X.dtype)
Z[:k, :k], Z[:k, k:] = go(A, E) + go(B, G), go(A, F) + go(B, H)
Z[k:, :k], Z[k:, k:] = go(C, E) + go(D, G), go(C, F) + go(D, H)
return Z
```
#### Strassen's Matrix Multiplication
* **Sub-cubic runtime:** O(N<sup>log<sub>2</sub>(7)</sup>)
*Python3*
```python
import numpy as np
def go(X, Y):
n = X.shape[0]
# Base case: 1x1 matrix
if n == 1:
return X * Y
# Divide: Partition A and B into n/2 x n/2 submatrices
k = n // 2
A, B = X[:k, :k], X[:k, k:]
C, D = X[k:, :k], X[k:, k:]
E, F = Y[:k, :k], Y[:k, k:]
G, H = Y[k:, :k], Y[k:, k:]
# Combine: Reconstruct the full matrix from quadrants
Z = np.zeros((n, n), dtype=X.dtype)
P1 = go(A, F - H)
P2 = go(A + B, H)
P3 = go(C + D, E)
P4 = go(D, G - E)
P5 = go(A + D, E + H)
P6 = go(B - D, G + H)
P7 = go(A - C, E + F)
Z[:k, :k], Z[:k, k:] = (P5 + P4 - P2 + P6), (P1 + P2)
Z[k:, :k], Z[k:, k:] = (P3 + P4), (P1 + P5 - P3 - P7)
return Z
```
#### Strassen's Matrix Multiplication (Variants)
* **Sub-cubic runtime:** O(N<sup>log<sub>2</sub>(7)</sup>)
*Python3*
```python
#
# Strassen-family Recursive Matrix Multiplication (2x2 block form)
#
# Methods:
# - "strassen" : classical Strassen scheme (7 recursive multiplies)
# - "winograd" : Winograd rearrangement of Strassen (7 multiplies, fewer additions)
# - "basis2017" : alternative-basis 7-multiply scheme (basis change + recombination)
#
# All methods:
# - Recursively split X and Y into 2x2 blocks
# - Perform 7 recursive multiplications
# - Recombine into result blocks
#
# Assumption: n is a power of 2
#
def go(X: np.ndarray, Y: np.ndarray, method: str = "strassen") -> np.ndarray:
n = X.shape[0]
# Base case: 1x1 matrix multiply
if n == 1:
return X * Y
k = n // 2
# Partition X into 2x2 block matrix
# X = [[A11 A12]
# [A21 A22]]
A11, A12 = X[:k, :k], X[:k, k:]
A21, A22 = X[k:, :k], X[k:, k:]
# Partition Y into 2x2 block matrix
# Y = [[B11 B12]
# [B21 B22]]
B11, B12 = Y[:k, :k], Y[:k, k:]
B21, B22 = Y[k:, :k], Y[k:, k:]
# Allocate result matrix
Z = np.zeros((n, n), dtype=X.dtype)
# ============================================================
# Classical Strassen scheme
# ============================================================
if method == "strassen":
# 7 recursive multiplications
M1 = go(A11 + A22, B11 + B22, method)
M2 = go(A21 + A22, B11, method)
M3 = go(A11, B12 - B22, method)
M4 = go(A22, B21 - B11, method)
M5 = go(A11 + A12, B22, method)
M6 = go(A21 - A11, B11 + B12, method)
M7 = go(A12 - A22, B21 + B22, method)
# Recombine into C blocks
C11 = M1 + M4 - M5 + M7
C12 = M3 + M5
C21 = M2 + M4
C22 = M1 - M2 + M3 + M6
Z[:k, :k] = C11
Z[:k, k:] = C12
Z[k:, :k] = C21
Z[k:, k:] = C22
return Z
# ============================================================
# Winograd rearrangement of Strassen
# Same 7 multiplies, different additive structure
# ============================================================
if method == "winograd":
# Core recursive products
t = go(A11, B11, method)
u = go(A21 - A11, B12 - B22, method)
v = go(A21 + A22, B12 - B11, method)
# w reuses t to reduce total additions
w = t + go(A21 + A22 - A11, B11 + B22 - B12, method)
# Recombine result blocks
C11 = t + go(A12, B21, method)
C12 = w + v + go(A11 + A12 - A21 - A22, B22, method)
C21 = w + u + go(A22, B21 + B12 - B11 - B22, method)
C22 = w + u + v
Z[:k, :k] = C11
Z[:k, k:] = C12
Z[k:, :k] = C21
Z[k:, k:] = C22
return Z
# ============================================================
# 2017 alternative-basis 7-multiply scheme
# Performs a basis change on A22 and B22 blocks
# ============================================================
if method == "basis2017":
# Basis transformation of lower-right blocks
A22p = A12 - A21 + A22
B22p = B12 - B21 + B22
# Linear combinations used by recursive multiplies
t1 = A21 + A22p
t2 = A22p - A12
t3 = A22p - A11
t4 = B22p - B11
t5 = B21 + B22p
t6 = B22p - B12
# 7 recursive multiplications
M1 = go(A11, B11, method)
M2 = go(A12, B21, method)
M3 = go(A21, t4, method)
M4 = go(A22p, B22p, method)
M5 = go(t1, t5, method)
M6 = go(t2, t6, method)
M7 = go(t3, B12, method)
# Initial recombination
C11 = M1 + M2
C12 = M5 - M7
C21 = M3 + M6
C22 = M5 + M6 - M2 - M4
# Final correction step required by basis change
C12 = C12 - C22
C21 = C22 - C21
Z[:k, :k] = C11
Z[:k, k:] = C12
Z[k:, :k] = C21
Z[k:, k:] = C22
return Z
```
</details>
---
### Closest Pair
<details><summary>📚 Lectures</summary>
<br/>
* [O(nlogn) Algorithm for Closest Pair: Part One](https://www.youtube.com/watch?v=3pUOv_ocJyA&list=PLEGCF-WLh2RLHqXx6-GZr_w7LgqKDXxN_&index=16)
* [O(nlogn) Algorithm for Closest Pair: Part Two](https://www.youtube.com/watch?v=7tiafUFrlBw&list=PLEGCF-WLh2RLHqXx6-GZr_w7LgqKDXxN_&index=17)
</details>
<details><summary>🎯 Solutions</summary>
<br/>
* **Input:** `n >= 2` points p<sub>1</sub>=(x<sub>1</sub>,y<sub>1</sub>), p2=(x<sub>2</sub>,y<sub>2</sub>), ... , p<sub>n</sub>=(x<sub>n</sub>,y<sub>n</sub>)
* **Output:** The pair p<sub>i</sub>,p<sub>j</sub> of distinct points with smallest Euclidean distance d(p<sub>i</sub>,p<sub>j</sub>)
#### Brute-Force
* **Quadratic runtime:** O(N<sup>2</sup>)
*Python3*
```python
P = [(1, 8), (2, 5), (4, 7), (6, 3)]
N = len(P)
INF = 1234567890 # arbitary choice for infinity
def dist(i, j):
x1, y1 = P[i]
x2, y2 = P[j]
return (x1 - x2) ** 2 \
+ (y1 - y2) ** 2
best_dist = 123456789
best_pair = None
D = [[INF] * N for _ in range(N)]
for i in range(N):
for j in range(N):
D[i][j] = dist(i, j)
if i != j: # candidates for best pair cannot be the same point
if best_dist >= D[i][j]:
best_dist = D[i][j]
best_pair = (i, j)
i, j = best_pair
print(f'best pair: {P[i]}, {P[j]}')
```
#### Recursive
* **Super-linear runtime:** O(NlogN)
```python
INF = 1234567890 # arbitary choice for infinity
def distance(a, b):
x1, y1 = a
x2, y2 = b
return (x1 - x2) ** 2 \
+ (y1 - y2) ** 2
def split(Px, Py, d):
# median x-coordinate
median = Px[len(Px) // 2][0]
# identify points near left/right boundary
Sy = [(x, y) for x, y in Py if median - d <= x <= median + d]
# return the best split pair (if it exists)
best_dist, best_pairs = INF, set()
for i in range(len(Sy) - 1):
for j in range(i + 1, min(i + 1 + 7, len(Sy))):
cand = distance(Sy[i], Sy[j])
if best_dist > cand:
best_dist = cand
best_pairs = set()
if best_dist == cand:
best_pairs.add((Sy[i], Sy[j]))
return best_dist, best_pairs
def best(P):
best_dist, best_pairs = INF, set()
for i in range(len(P)):
for j in range(i + 1, len(P)):
cand = distance(P[i], P[j]) if P[i] != P[j] else INF
if best_dist > cand:
best_dist = cand
best_pairs = set()
if best_dist == cand:
best_pairs.add((P[i], P[j]))
return best_dist, best_pairs
def go(Px, Py):
if len(Px) <= 3: # Base case
return best(Px)
Lx, Rx = Px[:len(Px) // 2], Px[len(Px) // 2:]
Ly, Ry = [], []
for x, y in Py:
if x <= Lx[-1][0]:
Ly.append((x, y))
else:
Ry.append((x, y))
dist_left, best_left = go(Lx, Ly)
dist_right, best_right = go(Rx, Ry)
dist_split, best_split = split(Px, Py, min(dist_left, dist_right))
cands = sorted([(dist_left, best_left), (dist_right, best_right), (dist_split, best_split)], key=lambda it: it[0])
best_dist, best_pairs = cands[0][0], set()
for dist, pairs in cands:
if dist == best_dist:
for a, b in pairs:
best_pairs.add((a, b) if a < b else (b, a))
return best_dist, best_pairs
def run(points, expected_best_pair):
Px = sorted(points, key=lambda it: it[0])
Py = sorted(points, key=lambda it: it[1])
_, best_pair = go(Px, Py)
print(f'points: {points}')
print(f'actual: {sorted(best_pair)}')
print(f'expect: {sorted(expected_best_pair)}')
print()
assert(sorted(best_pair) == sorted(expected_best_pair))
```
</details>
---
### Quick Sort
<details><summary>📚 Lectures</summary>
<br/>
* [QuickSort: Overview](https://www.youtube.com/watch?v=ETo1cpLN7kk&list=PLEGCF-WLh2RLHqXx6-GZr_w7LgqKDXxN_&index=24) (Section 5.1)
* [Partitioning Around a Pivot Element](https://www.youtube.com/watch?v=LYzdRN5iFdA&list=PLEGCF-WLh2RLHqXx6-GZr_w7LgqKDXxN_&index=25) (Section 5.2)
* [Choosing a Good Pivot](https://www.youtube.com/watch?v=kqO46FOUTbI&list=PLEGCF-WLh2RLHqXx6-GZr_w7LgqKDXxN_&index=26) (Sections 5.3 and 5.4)
* [QuickSort Analysis (Part 1)](https://www.youtube.com/watch?v=sToWtKSYlMw&list=PLEGCF-WLh2RLHqXx6-GZr_w7LgqKDXxN_&index=27) (Section 5.5, part 1)
* [QuickSort Analysis (Part 2)](https://www.youtube.com/watch?v=4t_Y-aGLkok&list=PLEGCF-WLh2RLHqXx6-GZr_w7LgqKDXxN_&index=28) (Section 5.5, part 2)
* [QuickSort Analysis (Part 3)](https://www.youtube.com/watch?v=IBTvneWhFsA&list=PLEGCF-WLh2RLHqXx6-GZr_w7LgqKDXxN_&index=29) (Section 5.5, part 3)
* [Sorting Requires Omega(n log n) Comparisons](https://www.youtube.com/watch?v=aFveIyII5D4&list=PLEGCF-WLh2RLHqXx6-GZr_w7LgqKDXxN_&index=30) (Section 5.6)
* [Proofs by Induction and the Correctness of QuickSort](https://www.youtube.com/watch?v=Colb_4jAy8A&list=PLEGCF-WLh2RLHqXx6-GZr_w7LgqKDXxN_&index=36) (Appendix A)
* [Quick Review of Discrete Probability](https://www.youtube.com/watch?v=uLeIMwMHX5U&list=PLEGCF-WLh2RLHqXx6-GZr_w7LgqKDXxN_&index=37) (Appendix B)
</details>
<details><summary>🎯 Solutions</summary>
<br/>
*Kotlin*
```java
import java.io.File
typealias PivotFunc = (A: MutableList<Int>, L: Int, R: Int) -> (Int)
var pivotLeft: PivotFunc = { _: MutableList<Int>, L: Int, _: Int -> L }
var pivotRight: PivotFunc = { _: MutableList<Int>, _: Int, R: Int -> R }
fun _pivotMedian(A: MutableList<Int>, L: Int, R: Int): Int {
var M = L + (R - L) / 2
var cand = intArrayOf(A[L], A[M], A[R])
cand.sort()
var target = cand[1]
if (target == A[L]) return L
if (target == A[M]) return M
if (target == A[R]) return R
return -1
}
var pivotMedian: PivotFunc = { A: MutableList<Int>, L: Int, R: Int -> _pivotMedian(A, L, R) }
fun partition(A: MutableList<Int>, L: Int, R: Int, choosePivot: (A: MutableList<Int>, L: Int, R: Int) -> (Int)): Int {
var i = L + 1
var j = L + 1
var k = choosePivot(A, L, R)
A[k] = A[L].also { A[L] = A[k] } // swap pivot A[k] with first element of subarray A[L]
while (j <= R) {
if (A[j] < A[L]) { // maintain loop invariant A[i] < pivot < A[j]
A[i] = A[j].also { A[j] = A[i] }
++i
}
++j
}
A[L] = A[i - 1].also { A[i - 1] = A[L] } // swap pivot A[L] with last value less-than pivot A[i - 1]
return i - 1
}
fun quicksort(A: MutableList<Int>, L: Int, R: Int, choosePivot: (A: MutableList<Int>, L: Int, R: Int) -> (Int)): Int {
if (R <= L)
return 0
var k = partition(A, L, R, choosePivot)
return (R - L) + quicksort(A, L, k - 1, choosePivot) + quicksort(A, k + 1, R, choosePivot)
}
fun run(filename: String, choosePivot: (A: MutableList<Int>, L: Int, R: Int) -> (Int)): Int {
var A = mutableListOf<Int>()
File(filename).forEachLine { A.add(it.toInt()) }
return quicksort(A, 0, A.size - 1, choosePivot)
}
fun main() {
var filename = "problem5.6.txt"
println(" left: ${run(filename, pivotLeft)}") // left: 162085
println(" right: ${run(filename, pivotRight)}") // right: 164123
println("median: ${run(filename, pivotMedian)}") // median: 138382
}
```
*Javascript*
```javascript
let pivotLeft = (A, L, R) => L;
let pivotRight = (A, L, R) => R;
let pivotMedian = (A, L, R) => {
let M = L + Math.floor((R - L) / 2);
let cand = [A[L], A[M], A[R]].sort((a, b) => a - b),
target = cand[1];
if (target == A[L]) return L;
if (target == A[M]) return M;
if (target == A[R]) return R;
};
let partition = (A, L, R, choosePivot) => {
let i = L + 1,
j = L + 1,
k = choosePivot(A, L, R);
[A[L], A[k]] = [A[k], A[L]]; // swap pivot A[k] with first element of subarray A[L]
while (j <= R) {
if (A[j] < A[L]) { // maintain loop invariant A[i] < pivot < A[j]
[A[i], A[j]] = [A[j], A[i]];
++i;
}
++j;
}
[A[L], A[i - 1]] = [A[i - 1], A[L]]; // swap pivot A[L] with last value less-than pivot A[i - 1]
return i - 1;
};
let quicksort = (A, L, R, choosePivot) => {
if (R <= L)
return 0;
let k = partition(A, L, R, choosePivot);
return (R - L) + quicksort(A, L, k - 1, choosePivot)
+ quicksort(A, k + 1, R, choosePivot);
};
let run = (filename, choosePivot) => {
let A = [];
let LineByLine = require("n-readlines");
let input = new LineByLine(filename);
for (let line; line = input.next(); A.push(Number(line)));
return quicksort(A, 0, A.length - 1, choosePivot);
}
let filename = 'problem5.6.txt';
console.log(` left: ${run(filename, pivotLeft)}`); // left: 162085
console.log(` right: ${run(filename, pivotRight)}`); // right: 164123
console.log(`median: ${run(filename, pivotMedian)}`); // median: 138382
```
*Python3*
```python
def pivotLeft(A, L, R): return L
def pivotRight(A, L, R): return R
def pivotMedian(A, L, R):
M = L + (R - L) // 2
cand = sorted([A[L], A[M], A[R]])
target = cand[1]
if target == A[L]: return L
if target == A[M]: return M
if target == A[R]: return R
def partition(A, L, R, choosePivot):
i = L + 1
j = L + 1
k = choosePivot(A, L, R)
A[L], A[k] = A[k], A[L] # swap pivot A[k] with first element of subarray A[L]
while j <= R:
if A[j] < A[L]: # maintain loop invariant A[i] < pivot < A[j]
A[i], A[j] = A[j], A[i]
i += 1
j += 1
A[L], A[i - 1] = A[i - 1], A[L] # swap pivot A[L] with last value less-than pivot A[i - 1]
return i - 1
def quicksort(A, L, R, choosePivot):
if R <= L:
return 0
k = partition(A, L, R, choosePivot)
return (R - L) + quicksort(A, L, k - 1, choosePivot) + quicksort(A, k + 1, R, choosePivot)
def run(filename, choosePivot):
A = []
with open(filename) as fin:
while True:
line = fin.readline()
if not line:
break
A.append(int(line))
return quicksort(A, 0, len(A) - 1, choosePivot)
filename = 'problem5.6.txt'
print(f' left: {run(filename, pivotLeft)}') # left: 162085
print(f' right: {run(filename, pivotRight)}') # right: 164123
print(f'median: {run(filename, pivotMedian)}') # median: 138382
```
*C++*
```cpp
#include <iostream>
#include <fstream>
#include <vector>
using namespace std;
using VI = vector<int>;
using fun = function<int(VI&, int, int)>;
fun pivotLeft = [](VI& A, int L, int R) { return L; };
fun pivotRight = [](VI& A, int L, int R) { return R; };
fun pivotMedian = [](VI& A, int L, int R) {
auto M = L + (R - L) / 2;
VI cand{ A[L], A[M], A[R] };
sort(cand.begin(), cand.end());
auto target = cand[1];
if (target == A[L]) return L;
if (target == A[M]) return M;
if (target == A[R]) return R;
};
int partition(VI& A, int L, int R, fun choosePivot) {
auto i = L + 1,
j = L + 1,
k = choosePivot(A, L, R);
swap(A[L], A[k]); // swap pivot A[k] with first element of the subarray A[L]
while (j <= R) {
if (A[j] < A[L]) { // maintain loop invariant A[i] < pivot < A[j]
swap(A[i], A[j]);
++i;
}
++j;
}
swap(A[L], A[i - 1]); // swap pivot A[L] with last value less-than pivot A[i - 1]
return i - 1;
}
int quicksort(VI& A, int L, int R, fun choosePivot) {
if (R <= L)
return 0;
auto k = partition(A, L, R, choosePivot);
return (R - L) + quicksort(A, L, k - 1, choosePivot)
+ quicksort(A, k + 1, R, choosePivot);
}
int run(string& filename, fun choosePivot) {
VI A;
fstream fin{ filename };
for (string line; fin >> line; A.push_back(stoi(line)));
int N = A.size();
return quicksort(A, 0, N - 1, choosePivot);
}
int main() {
string filename{ "problem5.6.txt" };
cout << " left: " << run(filename, pivotLeft) << endl // left: 162085
<< " right: " << run(filename, pivotRight) << endl // right: 164123
<< "median: " << run(filename, pivotMedian) << endl; // median: 138382
return 0;
}
```
</details>
---
### Randomized Linear-Time Selection
<details><summary>📚 Lectures</summary>
<br/>
* [Randomized Linear-Time Selection](https://www.youtube.com/watch?v=nFw6x7DoYbs&list=PLEGCF-WLh2RLHqXx6-GZr_w7LgqKDXxN_&index=31) (Section 6.1)
* [Randomized Linear-Time Selection (Analysis)](https://www.youtube.com/watch?v=rX2u2CnpveQ&list=PLEGCF-WLh2RLHqXx6-GZr_w7LgqKDXxN_&index=32) (Section 6.2)
</details>
<details><summary>🎯 Solutions</summary>
<br/>
*Kotlin*
```java
import java.io.File
import kotlin.random.Random
fun partition(A: MutableList<Int>, L: Int, R: Int): Int {
var i = L + 1
var j = L + 1
var k = Random.nextInt(L, R + 1) // +1 for L..R inclusive
A[L] = A[k].also { A[k] = A[L] } // swap pivot A[k] with first element of subarray A[L]
while (j <= R) {
if (A[j] < A[L]) { // maintain loop invariant A[i] < pivot < A[j]
A[i] = A[j].also { A[j] = A[i] }
++i
}
++j
}
A[L] = A[i - 1].also { A[i - 1] = A[L] } // swap pivot A[L] with last value less-than pivot A[i - 1]
return i - 1
}
fun rselect(A: MutableList<Int>, i: Int, L_: Int, R_: Int): Int {
var L = L_
var R = R_
var k = partition(A, L, R)
if (i == k)
return A[k] // 🎯 lucky guess
if (i < k)
R = k - 1
else
L = k + 1
return rselect(A, i, L, R)
}
fun run(filename: String, i: Int): Int {
var A = mutableListOf<Int>()
File(filename).forEachLine { A.add(it.toInt()) }
var N = A.size
return rselect(A, i - 1, 0 , N - 1) // -1 for 0-based indexing
}
fun main() {
println("problem6.5test1.txt: " + run("problem6.5test1.txt", 5)) // problem6.5test1.txt: 5469
println("problem6.5test2.txt: " + run("problem6.5test2.txt", 50)) // problem6.5test2.txt: 4715
}
```
*Javascript*
```javascript
let random = (L, R) => Math.floor(Math.random() * (R + 1 - L) + L); // +1 for L..R inclusive
let partition = (A, L, R) => {
let i = L + 1,
j = L + 1,
k = random(L, R);
[A[L], A[k]] = [A[k], A[L]]; // swap pivot A[k] with first element of subarray A[L]
while (j <= R) {
if (A[j] < A[L]) { // maintain loop invariant A[i] < pivot < A[j]
[A[i], A[j]] = [A[j], A[i]];
++i;
}
++j;
}
[A[L], A[i - 1]] = [A[i - 1], A[L]]; // swap pivot A[L] with last value less-than pivot A[i - 1]
return i - 1;
};
let rselect = (A, i, L, R) => {
let k = partition(A, L, R);
if (i == k)
return A[k]; // 🎯 lucky guess
if (i < k)
R = k - 1;
else
L = k + 1;
return rselect(A, i, L, R);
}
let run = (filename, i) => {
let A = [];
let LineByLine = require("n-readlines");
let input = new LineByLine(filename);
for (let line; line = input.next(); A.push(Number(line)));
let N = A.length;
return rselect(A, i - 1, 0, N - 1); // -1 for 0-based indexing
};
console.log(`problem6.5test1.txt: ${run('problem6.5test1.txt', 5)}`); // problem6.5test1.txt: 5469
console.log(`problem6.5test2.txt: ${run('problem6.5test2.txt', 50)}`); // problem6.5test2.txt: 4715
```
*Python3*
```python
from random import uniform
from math import floor
def partition(A, L, R):
i = L + 1
j = L + 1
k = floor(uniform(L, R))
A[L], A[k] = A[k], A[L] # swap pivot A[k] with first element of subarray A[L]
while j <= R:
if A[j] < A[L]: # maintain loop invariant A[i] < pivot < A[j]
A[i], A[j] = A[j], A[i]
i += 1
j += 1
A[L], A[i - 1] = A[i - 1], A[L] # swap pivot A[L] with last value less-than pivot A[i - 1]
return i - 1
def rselect(A, i, L, R):
k = partition(A, L, R)
if i == k:
return A[k] # 🎯 lucky guess
if i < k:
R = k - 1
else:
L = k + 1
return rselect(A, i, L, R)
def run(filename, i):
A = []
with open(filename) as fin:
while True:
line = fin.readline()
if not line:
break
A.append(int(line))
N = len(A)
return rselect(A, i - 1, 0, N - 1) # -1 for 0-based indexing
print('problem6.5test1.txt:', run('problem6.5test1.txt', 5)) # problem6.5test1.txt: 5469
print('problem6.5test2.txt:', run('problem6.5test2.txt', 50)) # problem6.5test2.txt: 4715
```
*C++*
```cpp
#include <iostream>
#include <fstream>
#include <vector>
#include <random>
using namespace std;
using VI = vector<int>;
int random(int L, int R) {
random_device rd;
mt19937 gen{ rd() };
uniform_int_distribution dist(L, R);
return dist(gen);
}
int partition(VI& A, int L, int R) {
auto i = L + 1,
j = L + 1,
k = random(L, R);
swap(A[L], A[k]); // swap pivot A[k] with first element of the subarray A[L]
while (j <= R) {
if (A[j] < A[L]) // maintain loop invariant A[i] < pivot < A[j]
swap(A[i++], A[j]);
++j;
}
swap(A[L], A[i - 1]); // swap pivot A[L] with last value less-than pivot A[i - 1]
return i - 1;
}
int rselect(VI& A, int i, int L, int R) {
auto k = partition(A, L, R);
if (i == k)
return A[k]; // 🎯 lucky guess
if (i < k)
R = k - 1;
else
L = k + 1;
return rselect(A, i, L, R);
}
int run(string filename, int i, VI A = {}) {
fstream fin{ filename };
for (string line; fin >> line; A.push_back(stoi(line)));
int N = A.size();
return rselect(A, i - 1, 0, N - 1); // -1 for 0-based indexing
}
int main() {
cout << "problem6.5test1.txt: " << run("problem6.5test1.txt", 5) << endl; // problem6.5test1.txt: 5469
cout << "problem6.5test2.txt: " << run("problem6.5test2.txt", 50) << endl; // problem6.5test2.txt: 4715
return 0;
}
```
</details>
---
# Part 2: Graph Algorithms and Data Structures
<br/>
<a href="https://www.amazon.com/dp/0999282921" target="_blank">
<img src="images/ai2large.jpg" />
</a>
---
### Topological Sort
<details><summary>📚 Lectures</summary>
<br/>
* [Graphs: The Basics (from 2:06 to 6:39)](https://www.youtube.com/watch?v=4Ih3UhVuEtw&list=PLEGCF-WLh2RJ5W-pt-KE9GUArTDzVwL1P&index=1) (Sections 7.1 and 7.2)
* [Graph Representations](https://www.youtube.com/watch?v=b-Mfu8dPv9U&list=PLEGCF-WLh2RJ5W-pt-KE9GUArTDzVwL1P&index=2) (Sections 7.3 and 7.4)
* [Graph Search Overview](https://www.youtube.com/watch?v=SW6jwg7WS48&list=PLEGCF-WLh2RJ5W-pt-KE9GUArTDzVwL1P&index=3) (Section 8.1)
* [Breadth-First Search](https://www.youtube.com/watch?v=73qCvXsYkfk&list=PLEGCF-WLh2RJ5W-pt-KE9GUArTDzVwL1P&index=4) (Section 8.2, Part 1)
* [Depth-First Search](https://www.youtube.com/watch?v=73qCvXsYkfk&list=PLEGCF-WLh2RJ5W-pt-KE9GUArTDzVwL1P&index=4) (Section 8.4)
* [Topological Sort](https://www.youtube.com/watch?v=ozso3xxkVGU&list=PLEGCF-WLh2RJ5W-pt-KE9GUArTDzVwL1P&index=8) (Section 8.5)
</details>
<details><summary>🎯 Solutions</summary>
<br/>
*Kotlin*
```java
import java.util.Queue
import java.util.LinkedList
class Solution(val adj: MutableMap<Char, List<Char>>) {
var N: Int
var color: Int
var m = mutableMapOf<Char, Int>()
var seen = mutableSetOf<Char>()
init {
N = adj.size
color = 0
}
fun init(start: Int) {
color = start
m.clear()
seen.clear()
}
fun topoSortBFS(): String {
init(1) // 👉 color forward from 1..N
bfs()
return toString()
}
fun topoSortDFS(): String {
init(N) // 👈 color reverse from N..1 (as the recursive stack unwinds)
adj.forEach{ (u, _) -> dfs(u) }
return toString()
}
fun bfs() {
var degree = mutableMapOf<Char, Int>()
adj.forEach{ (_, neighbors) ->
neighbors.forEach{ v ->
degree[v] = 1 + degree.getOrDefault(v, 0)
}
}
var q: Queue<Char> = LinkedList(adj.map{ (u, _) -> u }.filter{ !degree.contains(it) })
while (0 < q.size) {
var u = q.poll()
m[u] = color++
adj[u]!!.forEach{ v ->
degree[v] = degree[v]!!.minus(1)
if (degree[v] == 0 && !seen.contains(v)) {
q.add(v); seen.add(v)
}
}
}
}
fun dfs(u: Char) {
if (seen.contains(u))
return
seen.add(u)
adj[u]!!.forEach{ v ->
dfs(v)
}
m[u] = color--
}
override fun toString(): String {
var s = mutableListOf<String>()
adj.forEach{ (u, _) ->
s.add("$u: ${m[u]}")
}
return s.joinToString("\n")
}
}
fun main() {
var adj = mutableMapOf<Char, List<Char>>(
's' to listOf<Char>('v', 'w'),
'v' to listOf<Char>('t'),
'w' to listOf<Char>('t'),
't' to listOf<Char>()
)
var solution = Solution(adj)
println("BFS:\n${solution.topoSortBFS()}\n\nDFS:\n${solution.topoSortDFS()}")
// BFS:
// s: 1
// v: 2
// w: 3
// t: 4
// DFS:
// s: 1
// v: 3
// w: 2
// t: 4
}
```
*Javascript*
```javascript
class Solution {
constructor(adj) {
this.adj = adj;
this.N = this.adj.size;
}
init(start) {
this.color = start;
this.seen = new Set();
this.m = new Map();
}
topo_sort_bfs() {
this.init(1); // 👉 color forward from 1..N
this.bfs();
return this.to_string();
}
topo_sort_dfs() {
this.init(this.N); // 👈 color reverse from N..1 (as the recursive stack unwinds)
for (let [u, _] of [...this.adj])
this.dfs(u);
return this.to_string();
}
bfs() {
let degree = new Map();
for (let [u, _] of [...this.adj]) {
degree.set(u, (degree.get(u) || 0));
for (let v of this.adj.get(u))
degree.set(v, 1 + (degree.get(v) || 0));
}
let q = [...this.adj].map(([u, _]) => u).filter(u => !degree.get(u));
let seen = new Set(q);
while (q.length) {
let u = q.shift();
this.m.set(u, this.color++);
for (let v of this.adj.get(u)) {
degree.set(v, -1 + degree.get(v));
if (!degree.get(v) && !seen.has(v))
q.push(v), seen.add(v);
}
}
}
dfs(u) {
if (this.seen.has(u))
return;
this.seen.add(u);
for (let v of this.adj.get(u))
if (!this.seen.has(v))
this.dfs(v);
this.m.set(u, this.color--);
}
to_string() {
let s = [];
for (let [u, color] of [...this.m])
s.push(`${u}: ${color}`);
return s.join('\n');
}
}
let adj = new Map();
adj.set('s', ['v', 'w']);
adj.set('v', ['t']);
adj.set('w', ['t']);
adj.set('t', []);
let solution = new Solution(adj);
console.log(`BFS:\n${solution.topo_sort_bfs()}\n\nDFS:\n${solution.topo_sort_dfs()}`);
// BFS:
// s: 1
// v: 2
// w: 3
// t: 4
// DFS:
// t: 4
// v: 3
// w: 2
// s: 1
```
*Python3*
```python
from collections import deque
class Solution:
def __init__(self, adj):
self.adj = adj
self.N = len(adj)
self.seen = set()
self.m = {}
def init(self, start):
self.color = start
self.seen.clear()
self.m.clear()
def topo_sort_bfs(self):
self.init(1) # 👉 color forward from 1..N
self.bfs()
return self.to_string()
def topo_sort_dfs(self):
self.init(self.N) # 👈 color reverse from N..1 (as the recursive stack unwinds)
for u, _ in self.adj.items():
self.dfs(u)
return self.to_string()
def bfs(self):
degree = {}
for _, neighbors in self.adj.items():
for v in neighbors:
degree[v] = 1 + (degree[v] if v in degree else 0)
q = deque(u for u, _ in self.adj.items() if u not in degree)
self.seen.update(*q)
while q:
u = q.popleft()
self.m[u] = self.color; self.color += 1
for v in adj[u]:
degree[v] -= 1
if not degree[v] and v not in self.seen:
q.append(v); self.seen.add(v)
def dfs(self, u):
if u in self.seen:
return
self.seen.add(u)
for v in adj[u]:
self.dfs(v)
self.m[u] = self.color; self.color -= 1
def to_string(self):
s = []
for u, color in self.m.items():
s.append(f'{u}: {color}')
return '\n'.join(s)
#
# graph from Quiz 8.3 on page 45 of Algorithms Illuminated: Part 2
#
adj = {
's': ['v', 'w'],
'v': ['t'],
'w': ['t'],
't': []
}
solution = Solution(adj)
print(f'BFS:\n{solution.topo_sort_bfs()}\n\nDFS:\n{solution.topo_sort_dfs()}')
# BFS:
# s: 1
# v: 2
# w: 3
# t: 4
# DFS:
# t: 4
# v: 3
# w: 2
# s: 1
```
*C++*
```cpp
#include <iostream>
#include <sstream>
#include <vector>
#include <unordered_map>
#include <unordered_set>
#include <queue>
using namespace std;
using VI = vector<int>;
using AdjList = unordered_map<char, VI>;
using Set = unordered_set<char>;
using Map = unordered_map<char, int>;
using Queue = queue<char>;
using fun = function<void(char)>;
class Solution {
private:
AdjList adj;
const int N;
Map m;
Set seen;
int color;
public:
Solution(AdjList&
gitextract_gtzmg6or/
├── .gitignore
├── Algorithms-Illuminated/
│ ├── .gitignore
│ ├── Algorithms-Illuminated.iml
│ ├── Cargo.toml
│ ├── README.md
│ ├── src/
│ │ └── main.rs
│ └── target/
│ ├── .rustc_info.json
│ └── CACHEDIR.TAG
├── README.md
├── _challenge_problems/
│ ├── index_equal_element.py
│ └── unimodal_array.py
├── bellman_ford/
│ ├── CMakeLists.txt
│ ├── main.cpp
│ ├── main.js
│ ├── main.kt
│ ├── main.py
│ ├── package.json
│ └── test.txt
├── closest_pair/
│ ├── naive.py
│ └── recursive.py
├── dijkstra/
│ ├── CMakeLists.txt
│ ├── main.cpp
│ ├── main.js
│ ├── main.kt
│ ├── main.py
│ ├── package.json
│ ├── problem9.8.txt
│ └── problem9.8test.txt
├── floyd_warshall/
│ ├── CMakeLists.txt
│ ├── floyd_warshall
│ ├── main.cpp
│ ├── main.js
│ ├── main.kt
│ ├── main.py
│ ├── package.json
│ ├── problem18.8file1.txt
│ ├── problem18.8file2.txt
│ ├── problem18.8file3.txt
│ ├── problem18.8file4.txt
│ ├── problem18.8test1.txt
│ └── problem18.8test2.txt
├── greedy_scheduling/
│ ├── CMakeLists.txt
│ ├── main.cpp
│ ├── main.js
│ ├── main.kt
│ ├── main.py
│ ├── package.json
│ ├── problem13.4.txt
│ ├── problem13.4test1.txt
│ └── problem13.4test2.txt
├── huffman/
│ ├── CMakeLists.txt
│ ├── main.cpp
│ ├── main.js
│ ├── main.kt
│ ├── main.py
│ ├── package.json
│ ├── problem14.6.txt
│ ├── problem14.6test1.txt
│ └── problem14.6test2.txt
├── karatsuba/
│ ├── main.jl
│ └── main.py
├── knapsack/
│ ├── CMakeLists.txt
│ ├── main.cpp
│ ├── main.js
│ ├── main.kt
│ ├── main.py
│ ├── package.json
│ ├── problem16.7.txt
│ └── problem16.7test.txt
├── kosaraju/
│ ├── CMakeLists.txt
│ ├── main.cpp
│ ├── main.js
│ ├── main.kt
│ ├── main.py
│ ├── package.json
│ ├── problem8.10.txt
│ ├── problem8.10test1.txt
│ ├── problem8.10test2.txt
│ ├── problem8.10test3.txt
│ ├── problem8.10test4.txt
│ ├── problem8.10test5.txt
│ └── section8.6.5page64.txt
├── kotlin/
│ ├── dijkstra.kt
│ ├── greedy_scheduling.kt
│ ├── huffman.kt
│ ├── knapsack.kt
│ ├── kosaraju.kt
│ ├── kruskal.kt
│ ├── merge_sort.kt
│ ├── merge_sort_inversions.kt
│ ├── prim.kt
│ ├── quick_sort.kt
│ ├── rselect.kt
│ ├── topo_sort.kt
│ └── weighted_independent_set.kt
├── kruskal/
│ ├── CMakeLists.txt
│ ├── main.cpp
│ ├── main.js
│ ├── main.kt
│ ├── main.py
│ ├── package.json
│ ├── problem15.9.txt
│ └── problem15.9test.txt
├── matrix_multiplication/
│ ├── main.jl
│ └── main.py
├── merge_sort/
│ ├── CMakeLists.txt
│ ├── merge_sort.cpp
│ ├── merge_sort.jl
│ ├── merge_sort.js
│ ├── merge_sort.kt
│ └── merge_sort.py
├── merge_sort_inversions/
│ ├── CMakeLists.txt
│ ├── merge_sort_inversions.cpp
│ ├── merge_sort_inversions.jl
│ ├── merge_sort_inversions.js
│ ├── merge_sort_inversions.kt
│ ├── merge_sort_inversions.py
│ ├── problem3.5.txt
│ └── problem3.5test.txt
├── prim/
│ ├── CMakeLists.txt
│ ├── main.cpp
│ ├── main.js
│ ├── main.kt
│ ├── main.py
│ ├── package.json
│ ├── problem15.9.txt
│ └── problem15.9test.txt
├── quick_sort/
│ ├── CMakeLists.txt
│ ├── main.cpp
│ ├── main.js
│ ├── main.kt
│ ├── main.py
│ ├── package.json
│ ├── problem5.6.txt
│ ├── problem5.6test1.txt
│ └── problem5.6test2.txt
├── rec_int_mult/
│ └── main.jl
├── rec_mat_mult/
│ ├── main.py
│ └── pyproject.toml
├── rselect/
│ ├── CMakeLists.txt
│ ├── main.cpp
│ ├── main.js
│ ├── main.kt
│ ├── main.py
│ ├── package.json
│ ├── problem6.5test1.txt
│ └── problem6.5test2.txt
├── strassen/
│ ├── main.py
│ ├── pyproject.toml
│ └── variants.py
├── topo_sort/
│ ├── CMakeLists.txt
│ ├── main.cpp
│ ├── main.js
│ ├── main.kt
│ └── main.py
├── traveling_salesman/
│ ├── README.md
│ ├── main.cpp
│ ├── main.js
│ ├── main.kt
│ ├── main.py
│ ├── package.json
│ ├── quiz19.2.txt
│ └── quiz20.7.txt
├── traveling_salesman_nearest_neighbor/
│ ├── README.md
│ ├── input.txt
│ └── main.py
└── weighted_independent_set/
├── CMakeLists.txt
├── main.cpp
├── main.js
├── main.kt
├── main.py
├── package.json
├── problem16.6.txt
└── problem16.6test.txt
SYMBOL INDEX (231 symbols across 47 files)
FILE: Algorithms-Illuminated/src/main.rs
function main (line 1) | fn main() {
FILE: _challenge_problems/index_equal_element.py
function same (line 15) | def same(A):
function run (line 25) | def run(A, ok=False):
FILE: _challenge_problems/unimodal_array.py
function best (line 4) | def best(A):
function run (line 14) | def run(A):
function random_unimodal (line 40) | def random_unimodal(seed=0):
FILE: bellman_ford/main.cpp
function VI (line 19) | VI bell(Edges& E, int N, int start = 1, int INF = 1e6) {
function VI (line 30) | VI spfa(Edges& E, int N, int start = 1, int INF = 1e6, AdjList adj = {}) {
function VI (line 45) | VI run(const string& filename) {
function main (line 66) | int main() {
FILE: bellman_ford/main.py
function bell (line 4) | def bell(E, N, start = 1, INF = int(1e6)):
function spfa (line 15) | def spfa(E, N, start = 1, INF = int(1e6)):
function run (line 29) | def run(filename):
FILE: closest_pair/naive.py
function dist (line 5) | def dist(i, j):
FILE: closest_pair/recursive.py
function distance (line 3) | def distance(a, b):
function split (line 9) | def split(Px, Py, d):
function best (line 28) | def best(P):
function go (line 40) | def go(Px, Py):
function run (line 64) | def run(points, expected_best_pair):
FILE: dijkstra/main.cpp
class BaseSolution (line 15) | class BaseSolution {
class NaiveSolution (line 22) | class NaiveSolution : public BaseSolution {
method Distance (line 26) | Distance dijkstra(Edges& E) {
method string (line 50) | string run(string filename, Queries&& queries) {
class HeapSolution (line 67) | class HeapSolution : public BaseSolution {
method Distance (line 73) | Distance dijkstra(AdjList& adj) {
method string (line 91) | string run(string filename, Queries&& queries) {
function run (line 108) | void run(BaseSolution&& solution) {
function main (line 113) | int main() {
FILE: dijkstra/main.js
constant INF (line 3) | let INF = Number(1e9 + 7);
class NaiveSolution (line 5) | class NaiveSolution {
method dijkstra (line 6) | dijkstra(E) {
method run (line 30) | run(filename, queries) {
class HeapSolution (line 81) | class HeapSolution {
method dijkstra (line 82) | dijkstra(adj) {
method run (line 97) | run(filename, queries) {
FILE: dijkstra/main.py
class BaseSolution (line 6) | class BaseSolution(ABC):
method run (line 8) | def run(self, filename, queries):
class NaiveSolution (line 11) | class NaiveSolution(BaseSolution):
method dijkstra (line 12) | def dijkstra(self, E):
method run (line 33) | def run(self, filename, queries):
class HeapSolution (line 48) | class HeapSolution(BaseSolution):
method dijkstra (line 49) | def dijkstra(self, adj, start = 1):
method run (line 62) | def run(self, filename, queries):
function run (line 79) | def run(solution):
FILE: floyd_warshall/main.cpp
function string (line 34) | string key(int i, int j) {
function VVL (line 39) | VVL floyd_warshall(Edges& E, int N) {
function VVL (line 55) | VVL floyd_warshall_memopt(Edges& E, int N) {
function run (line 74) | void run(const string& filename) {
function main (line 99) | int main() {
FILE: floyd_warshall/main.py
function floyd_warshall (line 18) | def floyd_warshall(E, N):
function floyd_warshall_memopt (line 32) | def floyd_warshall_memopt(E, N):
function run (line 48) | def run(filename):
FILE: greedy_scheduling/main.cpp
type Job (line 9) | struct Job {
method Job (line 11) | Job(LL weight, LL length) : weight{ weight }, length{ length } {}
class Solution (line 15) | class Solution {
method Pair (line 18) | Pair minSum(Jobs& jobs) {
method LL (line 33) | LL calcSum(Jobs& jobs, Comp comp, LL time = 0LL) {
function run (line 41) | void run(const string& filename) {
function main (line 50) | int main() {
FILE: greedy_scheduling/main.js
class Job (line 3) | class Job {
method constructor (line 4) | constructor(weight, length) {
class Solution (line 10) | class Solution {
method minSum (line 11) | minSum(jobs) {
method _calcSum (line 24) | _calcSum(jobs, comp, time = 0) {
FILE: greedy_scheduling/main.py
class Job (line 3) | class Job:
method __init__ (line 4) | def __init__(self, weight, length):
class Solution (line 8) | class Solution:
method minSum (line 9) | def minSum(self, jobs):
method _calcSum (line 19) | def _calcSum(self, jobs, comp, time = 0, total = 0):
function run (line 26) | def run(filename):
FILE: huffman/main.cpp
type Tree (line 27) | struct Tree
method Tree (line 32) | Tree(Weight weight, TreePtr left = nullptr, TreePtr right = nullptr) :
type Tree (line 29) | struct Tree {
method Tree (line 32) | Tree(Weight weight, TreePtr left = nullptr, TreePtr right = nullptr) :
type Comp (line 38) | struct Comp {
function TreePtr (line 44) | TreePtr encode(const Weights& A, Queue q = {}) {
function TreePtr (line 61) | TreePtr encode(Weights& A, Queue first = {}, Queue second = {}) {
function MinMax (line 88) | MinMax run(const string& filename) {
function main (line 112) | int main() {
FILE: huffman/main.js
class Tree (line 13) | class Tree {
method constructor (line 14) | constructor(weight, left = null, right = null) {
FILE: huffman/main.py
class Tree (line 11) | class Tree:
method __init__ (line 12) | def __init__(self, weight, left = None, right = None):
method __lt__ (line 16) | def __lt__(self, other):
function encode (line 40) | def encode(A):
function run (line 55) | def run(filename):
FILE: karatsuba/main.py
function go (line 1) | def go(x, y):
FILE: knapsack/main.cpp
function top_down (line 15) | int top_down(Pairs& A, int K, Map m = {}) {
function bottom_up (line 31) | int bottom_up(Pairs& A, int K) {
function bottom_up_memopt (line 48) | int bottom_up_memopt(Pairs& A, int K) {
function run (line 65) | void run(const string& filename) {
function main (line 78) | int main() {
FILE: knapsack/main.py
function top_down (line 3) | def top_down(A, K):
function bottom_up (line 16) | def bottom_up(A, K):
function bottom_up_memopt (line 29) | def bottom_up_memopt(A, K):
function run (line 42) | def run(filename):
FILE: kosaraju/main.cpp
type Base (line 16) | namespace Base {
class Solution (line 17) | class Solution {
method Solution (line 21) | Solution(AdjList& adj, AdjList& rev) : adj{ adj }, rev{ rev } {}
type Recursive (line 24) | namespace Recursive {
type Solution (line 25) | struct Solution : public Base::Solution {
method Solution (line 26) | Solution(AdjList& adj, AdjList& rev) : Base::Solution{ adj, rev } {}
method Lists (line 27) | Lists kosaraju() {
method List (line 46) | List topo_sort() {
type Iterative (line 63) | namespace Iterative {
type Solution (line 64) | struct Solution : public Base::Solution {
method Solution (line 65) | Solution(AdjList& adj, AdjList& rev) : Base::Solution{ adj, rev } {}
method Lists (line 66) | Lists kosaraju() {
method List (line 87) | List topo_sort() {
function run (line 108) | void run(string filename) {
function main (line 123) | int main() {
FILE: kosaraju/main.js
class BaseSolution (line 1) | class BaseSolution {
method constructor (line 2) | constructor(adj, rev) {
class RecursiveSolution (line 8) | class RecursiveSolution extends BaseSolution {
method constructor (line 9) | constructor(adj, rev) {
method topo_sort (line 12) | topo_sort() {
method kosaraju (line 27) | kosaraju() {
class IterativeSolution (line 48) | class IterativeSolution extends BaseSolution {
method constructor (line 49) | constructor(adj, rev) {
method topo_sort (line 52) | topo_sort() {
method kosaraju (line 71) | kosaraju() {
FILE: kosaraju/main.py
class BaseSolution (line 4) | class BaseSolution:
method __init__ (line 5) | def __init__(self, adj, rev):
class RecursiveSolution (line 9) | class RecursiveSolution(BaseSolution):
method topo_sort (line 10) | def topo_sort(self):
method kosaraju (line 24) | def kosaraju(self):
class IterativeSolution (line 41) | class IterativeSolution(BaseSolution):
method topo_sort (line 42) | def topo_sort(self):
method kosaraju (line 58) | def kosaraju(self):
function run (line 77) | def run(filename):
FILE: kruskal/main.cpp
function kruskal (line 13) | int kruskal(Edges& E, int total = 0) {
function run (line 38) | void run(const string& filename, Edges E = {}) {
function main (line 48) | int main() {
FILE: kruskal/main.py
function kruskal (line 3) | def kruskal(E, total = 0):
function run (line 22) | def run(filename):
FILE: merge_sort/merge_sort.cpp
class Solution (line 6) | class Solution {
method VI (line 9) | VI mergesort(VI& A) {
method VI (line 13) | VI go(VI&& A) {
method VI (line 22) | VI merge(VI& A, VI& B, VI C = {}) {
function main (line 33) | int main() {
FILE: merge_sort/merge_sort.py
function sort (line 3) | def sort(A):
FILE: merge_sort_inversions/merge_sort_inversions.cpp
class Solution (line 7) | class Solution {
method Pair (line 12) | Pair merge(VL& A, VL& B, VL C = {}, long inv = 0) {
method Pair (line 27) | Pair inversions(VL& A) {
function run (line 42) | long run(string filename) {
function main (line 51) | int main() {
FILE: merge_sort_inversions/merge_sort_inversions.py
function sort (line 3) | def sort(A):
function run (line 28) | def run(filename):
FILE: prim/main.cpp
function getRandom (line 17) | int getRandom(int N) {
function prim (line 23) | int prim(int N, AdjList& adj, Queue q = {}, Set seen = {}, int total = 0) {
function run (line 40) | void run(const string& filename) {
function main (line 53) | int main() {
FILE: prim/main.py
function prim (line 4) | def prim(N, adj, total = 0):
function run (line 20) | def run(filename):
FILE: quick_sort/main.cpp
function partition (line 21) | int partition(VI& A, int L, int R, fun choosePivot) {
function quicksort (line 37) | int quicksort(VI& A, int L, int R, fun choosePivot) {
function run (line 45) | int run(string& filename, fun choosePivot) {
function main (line 53) | int main() {
FILE: quick_sort/main.py
function pivotLeft (line 1) | def pivotLeft(A, L, R): return L
function pivotRight (line 2) | def pivotRight(A, L, R): return R
function pivotMedian (line 3) | def pivotMedian(A, L, R):
function partition (line 11) | def partition(A, L, R, choosePivot):
function quicksort (line 24) | def quicksort(A, L, R, choosePivot):
function run (line 30) | def run(filename, choosePivot):
FILE: rec_mat_mult/main.py
function go (line 11) | def go(X, Y):
function pretty_print (line 33) | def pretty_print(A, label=''):
function main (line 46) | def main():
FILE: rselect/main.cpp
function random (line 9) | int random(int L, int R) {
function partition (line 16) | int partition(VI& A, int L, int R) {
function rselect (line 30) | int rselect(VI& A, int i, int L, int R) {
function run (line 41) | int run(string filename, int i, VI A = {}) {
function main (line 48) | int main() {
FILE: rselect/main.py
function partition (line 4) | def partition(A, L, R):
function rselect (line 17) | def rselect(A, i, L, R):
function run (line 27) | def run(filename, i):
FILE: strassen/main.py
function go (line 11) | def go(X, Y):
function pretty_print (line 40) | def pretty_print(A, label=''):
function main (line 53) | def main():
FILE: strassen/variants.py
function go (line 19) | def go(X: np.ndarray, Y: np.ndarray, method: str = "strassen") -> np.nda...
function pretty_print (line 142) | def pretty_print(A, label=''):
function main (line 153) | def main():
FILE: topo_sort/main.cpp
class Solution (line 17) | class Solution {
method Solution (line 25) | Solution(AdjList& adj) : adj{ adj }, N{ int(adj.size()) } {
method init (line 27) | void init(int start) {
method string (line 32) | string topo_sort_bfs() {
method string (line 37) | string topo_sort_dfs() {
method bfs (line 43) | void bfs() {
method dfs (line 60) | void dfs(char start) {
method string (line 70) | string to_string() {
function main (line 78) | int main() {
FILE: topo_sort/main.js
class Solution (line 1) | class Solution {
method constructor (line 2) | constructor(adj) {
method init (line 6) | init(start) {
method topo_sort_bfs (line 11) | topo_sort_bfs() {
method topo_sort_dfs (line 16) | topo_sort_dfs() {
method bfs (line 22) | bfs() {
method dfs (line 41) | dfs(u) {
method to_string (line 50) | to_string() {
FILE: topo_sort/main.py
class Solution (line 3) | class Solution:
method __init__ (line 4) | def __init__(self, adj):
method init (line 10) | def init(self, start):
method topo_sort_bfs (line 15) | def topo_sort_bfs(self):
method topo_sort_dfs (line 20) | def topo_sort_dfs(self):
method bfs (line 26) | def bfs(self):
method dfs (line 41) | def dfs(self, u):
method to_string (line 49) | def to_string(self):
FILE: traveling_salesman/main.cpp
class Solution (line 16) | class Solution {
method string (line 21) | string key(int u, int v) {
method init (line 26) | void init(const string& input_file, string line = {}) {
method go (line 47) | void go(int u, VI&& path, Set&& seen, int t = 0) {
method Pair (line 63) | Pair run(const string& input_file) {
function main (line 71) | int main() {
FILE: traveling_salesman/main.js
class Solution (line 3) | class Solution {
method init (line 5) | init(input_file) {
method run (line 27) | run(input_file) {
method go (line 33) | go(u, path, seen, t = 0) {
FILE: traveling_salesman/main.py
class Solution (line 4) | class Solution():
method init (line 5) | def init(self, input_file):
method run (line 21) | def run(self, input_file):
method go (line 27) | def go(self, u, path, seen, t = 0):
FILE: weighted_independent_set/main.cpp
type TopDown (line 22) | namespace TopDown {
function LL (line 23) | LL best(List& A, Map m = {}) {
type BottomUp (line 37) | namespace BottomUp {
function LL (line 38) | LL best(List& A, Map m = {}) {
type BottomUpMemOpt (line 52) | namespace BottomUpMemOpt {
function LL (line 53) | LL best(List& A) {
function run (line 68) | void run(const string& filename) {
function main (line 80) | int main() {
FILE: weighted_independent_set/main.py
function top_down (line 13) | def top_down(A):
function bottom_up (line 24) | def bottom_up(A):
function bottom_up_memopt (line 35) | def bottom_up_memopt(A):
function run (line 47) | def run(filename):
Condensed preview — 174 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (3,450K chars).
[
{
"path": ".gitignore",
"chars": 417,
"preview": "# IDE\n.idea\n.vscode\n.python-version\n\n# Prerequisites\n*.d\n\n# Compiled Object files\n*.slo\n*.lo\n*.o\n*.obj\n\n# Precompiled He"
},
{
"path": "Algorithms-Illuminated/.gitignore",
"chars": 395,
"preview": "# IDE\n.idea\n.vscode\n.python-version\n\n# Prerequisites\n*.d\n\n# Compiled Object files\n*.slo\n*.lo\n*.o\n*.obj\n\n# Precompiled He"
},
{
"path": "Algorithms-Illuminated/Algorithms-Illuminated.iml",
"chars": 480,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<module type=\"RUST_MODULE\" version=\"4\">\n <component name=\"NewModuleRootManager\" "
},
{
"path": "Algorithms-Illuminated/Cargo.toml",
"chars": 278,
"preview": "[package]\nname = \"Algorithms-Illuminated\"\nversion = \"0.1.1\"\nedition = \"2021\"\ndescription = \"https://github.com/claytonjw"
},
{
"path": "Algorithms-Illuminated/README.md",
"chars": 146457,
"preview": "# Algorithms Illuminated\n\n* [algorithmsilluminated.org](https://www.algorithmsilluminated.org/)\n\nCoincidentally, my [alg"
},
{
"path": "Algorithms-Illuminated/src/main.rs",
"chars": 45,
"preview": "fn main() {\n println!(\"Hello, world!\");\n}\n"
},
{
"path": "Algorithms-Illuminated/target/.rustc_info.json",
"chars": 1209,
"preview": "{\"rustc_fingerprint\":10491184667718451318,\"outputs\":{\"17598535894874457435\":{\"success\":true,\"status\":\"\",\"code\":0,\"stdout"
},
{
"path": "Algorithms-Illuminated/target/CACHEDIR.TAG",
"chars": 177,
"preview": "Signature: 8a477f597d28d172789f06886806bc55\n# This file is a cache directory tag created by cargo.\n# For information abo"
},
{
"path": "README.md",
"chars": 174207,
"preview": "# Algorithms Illuminated\n\n* [algorithmsilluminated.org](https://www.algorithmsilluminated.org/)\n\nCoincidentally, my [alg"
},
{
"path": "_challenge_problems/index_equal_element.py",
"chars": 756,
"preview": "# You are given a sorted (from smallest to largest) array A of N distinct integers which can be positive, negative, or z"
},
{
"path": "_challenge_problems/unimodal_array.py",
"chars": 1397,
"preview": "\n# Problem: You are given a unimodal array of n distinct elemnts, meaning that its entries are in increasing order up un"
},
{
"path": "bellman_ford/CMakeLists.txt",
"chars": 125,
"preview": "cmake_minimum_required(VERSION 3.16)\nproject(bellman_ford)\n\nset(CMAKE_CXX_STANDARD 17)\n\nadd_executable(bellman_ford main"
},
{
"path": "bellman_ford/main.cpp",
"chars": 2155,
"preview": "#include <iostream>\n#include <fstream>\n#include <sstream>\n#include <vector>\n#include <unordered_map>\n#include <queue>\n\nu"
},
{
"path": "bellman_ford/main.js",
"chars": 1754,
"preview": "const assert = require('assert');\nconst zip = require('lodash/zip');\nconst LineByLine = require('n-readlines');\n\n// bell"
},
{
"path": "bellman_ford/main.kt",
"chars": 1926,
"preview": "import java.io.File\nimport java.util.LinkedList\nimport java.util.Queue\n\n// bellman-ford: N - 1 edge relaxations (u -> v "
},
{
"path": "bellman_ford/main.py",
"chars": 1551,
"preview": "from collections import deque\n\n# bellman-ford: N - 1 edge relaxations (u -> v of cost w) [ie. attempting to relax M edge"
},
{
"path": "bellman_ford/package.json",
"chars": 322,
"preview": "{\n \"name\": \"bellman_ford\",\n \"version\": \"1.0.0\",\n \"description\": \"\",\n \"main\": \"main.js\",\n \"scripts\": {\n \"test\": \""
},
{
"path": "bellman_ford/test.txt",
"chars": 31836,
"preview": "1\t80,982\t163,8164\t170,2620\t145,648\t200,8021\t173,2069\t92,647\t26,4122\t140,546\t11,1913\t160,6461\t27,7905\t40,9047\t150,2183\t61"
},
{
"path": "closest_pair/naive.py",
"chars": 657,
"preview": "P = [(1, 8), (2, 5), (4, 7), (6, 3)]\nN = len(P)\nINF = 1234567890 # arbitary choice for infinity\n\ndef dist(i, j):\n x1"
},
{
"path": "closest_pair/recursive.py",
"chars": 5755,
"preview": "INF = 1234567890 # arbitary choice for infinity\n\ndef distance(a, b):\n x1, y1 = a\n x2, y2 = b\n return (x1 - x2)"
},
{
"path": "dijkstra/CMakeLists.txt",
"chars": 117,
"preview": "cmake_minimum_required(VERSION 3.16)\nproject(dijkstra)\n\nset(CMAKE_CXX_STANDARD 17)\n\nadd_executable(dijkstra main.cpp)"
},
{
"path": "dijkstra/main.cpp",
"chars": 3699,
"preview": "#include <iostream>\n#include <fstream>\n#include <sstream>\n#include <vector>\n#include <unordered_map>\n#include <unordered"
},
{
"path": "dijkstra/main.js",
"chars": 3880,
"preview": "let LineByLine = require('n-readlines');\n\nlet INF = Number(1e9 + 7);\n\nclass NaiveSolution {\n dijkstra(E) {\n le"
},
{
"path": "dijkstra/main.kt",
"chars": 3354,
"preview": "import java.io.File\nimport java.util.PriorityQueue\n\nvar INF = (1e9 + 7).toInt()\n\ninterface BaseSolution {\n fun run(fi"
},
{
"path": "dijkstra/main.py",
"chars": 2745,
"preview": "from abc import ABC, abstractmethod\nfrom heapq import heappush, heappop\n\nINF = int(1e9 + 7)\n\nclass BaseSolution(ABC):\n "
},
{
"path": "dijkstra/package.json",
"chars": 256,
"preview": "{\n \"name\": \"dijkstra\",\n \"version\": \"1.0.0\",\n \"description\": \"\",\n \"main\": \"main.js\",\n \"scripts\": {\n \"test\": \"echo"
},
{
"path": "dijkstra/problem9.8.txt",
"chars": 32237,
"preview": "1\t80,982\t163,8164\t170,2620\t145,648\t200,8021\t173,2069\t92,647\t26,4122\t140,546\t11,1913\t160,6461\t27,7905\t40,9047\t150,2183\t61"
},
{
"path": "dijkstra/problem9.8test.txt",
"chars": 80,
"preview": "1\t2,1\t8,2\n2\t1,1\t3,1\n3\t2,1\t4,1\n4\t3,1\t5,1\n5\t4,1\t6,1\n6\t5,1\t7,1\n7\t6,1\t8,1\n8\t7,1\t1,2\n"
},
{
"path": "floyd_warshall/CMakeLists.txt",
"chars": 129,
"preview": "cmake_minimum_required(VERSION 3.16)\nproject(floyd_warshall)\n\nset(CMAKE_CXX_STANDARD 17)\n\nadd_executable(floyd_warshall "
},
{
"path": "floyd_warshall/main.cpp",
"chars": 3663,
"preview": "/*\n * Programming Problem 18.8: All-Pairs Shortest Paths\n *\n * In this problem, each file describes a directed graph. Th"
},
{
"path": "floyd_warshall/main.js",
"chars": 3558,
"preview": "/*\n * Programming Problem 18.8: All-Pairs Shortest Paths\n *\n * In this problem, each file describes a directed graph. Th"
},
{
"path": "floyd_warshall/main.kt",
"chars": 3503,
"preview": "/*\n * Programming Problem 18.8: All-Pairs Shortest Paths\n *\n * In this problem, each file describes a directed graph. Th"
},
{
"path": "floyd_warshall/main.py",
"chars": 3347,
"preview": "#\n# Programming Problem 18.8: All-Pairs Shortest Paths\n#\n# In this problem, each file describes a directed graph. The fi"
},
{
"path": "floyd_warshall/package.json",
"chars": 298,
"preview": "{\n \"name\": \"floyd_warshall\",\n \"version\": \"1.0.0\",\n \"description\": \"\",\n \"main\": \"main.js\",\n \"scripts\": {\n \"test\":"
},
{
"path": "floyd_warshall/problem18.8file1.txt",
"chars": 509515,
"preview": "1000 47978\n1 14 6\n1 25 47\n1 70 22\n1 82 31\n1 98 17\n1 134 39\n1 146 7\n1 189 44\n1 192 20\n1 261 38\n1 283 30\n1 340 21\n1 380 37"
},
{
"path": "floyd_warshall/problem18.8file2.txt",
"chars": 509674,
"preview": "1000 47978\n1 2 2\n1 26 28\n1 68 -2\n1 123 48\n1 139 28\n1 147 2\n1 182 41\n1 187 7\n1 215 15\n1 227 27\n1 229 17\n1 246 45\n1 254 3\n"
},
{
"path": "floyd_warshall/problem18.8file3.txt",
"chars": 509496,
"preview": "1000 47978\n1 8 36\n1 33 29\n1 38 18\n1 63 25\n1 76 39\n1 100 26\n1 105 41\n1 120 20\n1 131 34\n1 181 32\n1 221 24\n1 255 16\n1 269 3"
},
{
"path": "floyd_warshall/problem18.8test1.txt",
"chars": 53,
"preview": "5 8\n1 2 2\n1 5 3\n2 4 -2\n3 1 1\n4 1 4\n4 3 1\n4 5 2\n5 3 -1"
},
{
"path": "floyd_warshall/problem18.8test2.txt",
"chars": 54,
"preview": "5 8\n1 2 2\n1 5 3\n2 4 -2\n3 1 1\n4 1 4\n4 3 1\n4 5 -1\n5 3 -1"
},
{
"path": "greedy_scheduling/CMakeLists.txt",
"chars": 135,
"preview": "cmake_minimum_required(VERSION 3.16)\nproject(greedy_scheduling)\n\nset(CMAKE_CXX_STANDARD 17)\n\nadd_executable(greedy_sched"
},
{
"path": "greedy_scheduling/main.cpp",
"chars": 1832,
"preview": "#include <iostream>\n#include <fstream>\n#include <vector>\n#include <numeric>\n\nusing namespace std;\n\nusing LL = long long;"
},
{
"path": "greedy_scheduling/main.js",
"chars": 1607,
"preview": "let LineByLine = require('n-readlines');\n\nclass Job {\n constructor(weight, length) {\n this.weight = weight;\n "
},
{
"path": "greedy_scheduling/main.kt",
"chars": 2113,
"preview": "import java.io.File\n\ndata class Job(val weight: Long, val length: Long)\n\nclass Solution {\n fun minSum(jobs: Array<Job"
},
{
"path": "greedy_scheduling/main.py",
"chars": 1595,
"preview": "from functools import cmp_to_key\n\nclass Job:\n def __init__(self, weight, length):\n self.weight = weight\n "
},
{
"path": "greedy_scheduling/package.json",
"chars": 277,
"preview": "{\n \"name\": \"greedy_scheduling\",\n \"version\": \"1.0.0\",\n \"description\": \"\",\n \"main\": \"main.js\",\n \"scripts\": {\n \"tes"
},
{
"path": "greedy_scheduling/problem13.4.txt",
"chars": 58399,
"preview": "10000\n8 50\n74 59\n31 73\n45 79\n24 10\n41 66\n93 43\n88 4\n28 30\n41 13\n4 70\n10 58\n61 34\n100 79\n17 36\n98 27\n13 68\n11 34\n80 50\n80"
},
{
"path": "greedy_scheduling/problem13.4test1.txt",
"chars": 10,
"preview": "2\n3 5\n1 2\n"
},
{
"path": "greedy_scheduling/problem13.4test2.txt",
"chars": 72,
"preview": "12\n8 50\n74 59\n31 73\n45 79\n24 10\n41 66\n93 43\n88 4\n28 30\n41 13\n4 70\n10 58\n"
},
{
"path": "huffman/CMakeLists.txt",
"chars": 115,
"preview": "cmake_minimum_required(VERSION 3.16)\nproject(huffman)\n\nset(CMAKE_CXX_STANDARD 17)\n\nadd_executable(huffman main.cpp)"
},
{
"path": "huffman/main.cpp",
"chars": 3626,
"preview": "/*\n * Programming Problem 14.6: Huffman Codes\n *\n * In this problem the file format is:\n * [number_of_symbols]\n * [weigh"
},
{
"path": "huffman/main.js",
"chars": 3629,
"preview": "/*\n * Programming Problem 14.6: Huffman Codes\n *\n * In this problem the file format is:\n * [number_of_symbols]\n * [weigh"
},
{
"path": "huffman/main.kt",
"chars": 2840,
"preview": "/*\n * Programming Problem 14.6: Huffman Codes\n *\n * In this problem the file format is:\n * [number_of_symbols]\n * [weigh"
},
{
"path": "huffman/main.py",
"chars": 2504,
"preview": "#\n# Programming Problem 14.6: Huffman Codes\n#\n# In this problem the file format is:\n# [number_of_symbols]\n# [weight of s"
},
{
"path": "huffman/package.json",
"chars": 267,
"preview": "{\n \"name\": \"huffman\",\n \"version\": \"1.0.0\",\n \"description\": \"\",\n \"main\": \"main.js\",\n \"scripts\": {\n \"test\": \"echo "
},
{
"path": "huffman/problem14.6.txt",
"chars": 7888,
"preview": "1000\n7540662\n6852892\n3235725\n8045172\n2667794\n2595511\n7030103\n5882478\n2731795\n8630567\n7224817\n9147454\n9180184\n4194220\n801"
},
{
"path": "huffman/problem14.6test1.txt",
"chars": 31,
"preview": "10\n37\n59\n43\n27\n30\n96\n96\n71\n8\n76"
},
{
"path": "huffman/problem14.6test2.txt",
"chars": 62,
"preview": "15\n895\n121\n188\n953\n378\n849\n153\n579\n144\n727\n589\n301\n442\n327\n930"
},
{
"path": "karatsuba/main.jl",
"chars": 1235,
"preview": "#\n# Karatsuba Multiplication\n#\n# Input: two n-digit positive integers x and y\n# Output: the product x * y\n# Assumption: "
},
{
"path": "karatsuba/main.py",
"chars": 823,
"preview": "def go(x, y):\n if x < 10 or y < 10:\n return x * y\n n = max(len(str(x)), len(str(y)))\n if n & 1:\n "
},
{
"path": "knapsack/CMakeLists.txt",
"chars": 117,
"preview": "cmake_minimum_required(VERSION 3.16)\nproject(knapsack)\n\nset(CMAKE_CXX_STANDARD 17)\n\nadd_executable(knapsack main.cpp)"
},
{
"path": "knapsack/main.cpp",
"chars": 3001,
"preview": "#include <iostream>\n#include <fstream>\n#include <sstream>\n#include <vector>\n#include <unordered_map>\n\nusing namespace st"
},
{
"path": "knapsack/main.js",
"chars": 2826,
"preview": "const assert = require('assert');\nconst LineByLine = require('n-readlines');\n\nlet top_down = (A, K, m = new Map()) => {\n"
},
{
"path": "knapsack/main.kt",
"chars": 2991,
"preview": "import java.io.File\n\nvar INF = (1e9 + 7).toInt()\n\nfun top_down(A: List<Pair<Int, Int>>, K: Int): Int {\n var N = A.siz"
},
{
"path": "knapsack/main.py",
"chars": 2609,
"preview": "from functools import lru_cache\n\ndef top_down(A, K):\n N = len(A)\n total = [0] * N\n @lru_cache(maxsize = None) "
},
{
"path": "knapsack/package.json",
"chars": 292,
"preview": "{\n \"name\": \"knapsack\",\n \"version\": \"1.0.0\",\n \"description\": \"\",\n \"main\": \"main.js\",\n \"scripts\": {\n \"test\": \"echo"
},
{
"path": "knapsack/problem16.7.txt",
"chars": 25570,
"preview": "2000000 2000\n16808 241486\n50074 834558\n8931 738037\n27545 212860\n77924 494349\n64441 815107\n84493 723724\n7988 421316\n82328"
},
{
"path": "knapsack/problem16.7test.txt",
"chars": 987,
"preview": "10000 100\n16808 250\n50074 659\n8931 273\n27545 879\n77924 710\n64441 166\n84493 43\n7988 504\n82328 730\n78841 613\n44304 170\n177"
},
{
"path": "kosaraju/CMakeLists.txt",
"chars": 117,
"preview": "cmake_minimum_required(VERSION 3.16)\nproject(kosaraju)\n\nset(CMAKE_CXX_STANDARD 17)\n\nadd_executable(kosaraju main.cpp)"
},
{
"path": "kosaraju/main.cpp",
"chars": 4782,
"preview": "#include <iostream>\n#include <fstream>\n#include <vector>\n#include <unordered_map>\n#include <unordered_set>\n#include <que"
},
{
"path": "kosaraju/main.js",
"chars": 4260,
"preview": "class BaseSolution {\n constructor(adj, rev) {\n this.adj = adj;\n this.rev = rev;\n }\n}\n\nclass Recursiv"
},
{
"path": "kosaraju/main.kt",
"chars": 4497,
"preview": "import java.util.Stack\nimport java.io.File\n\nclass RecursiveSolution(var adj: MutableMap<Int, MutableList<Int>>, var rev:"
},
{
"path": "kosaraju/main.py",
"chars": 3735,
"preview": "from collections import deque\nfrom functools import cmp_to_key\n\nclass BaseSolution:\n def __init__(self, adj, rev):\n "
},
{
"path": "kosaraju/package.json",
"chars": 268,
"preview": "{\n \"name\": \"kosaraju\",\n \"version\": \"1.0.0\",\n \"description\": \"\",\n \"main\": \"main.js\",\n \"scripts\": {\n \"test\": \"echo"
},
{
"path": "kosaraju/problem8.10test1.txt",
"chars": 44,
"preview": "1 4\n2 8\n3 6\n4 7\n5 2\n6 9\n7 1\n8 5\n8 6\n9 7\n9 3\n"
},
{
"path": "kosaraju/problem8.10test2.txt",
"chars": 56,
"preview": "1 2\n2 6\n2 3\n2 4\n3 1\n3 4\n4 5\n5 4\n6 5\n6 7\n7 6\n7 8\n8 5\n8 7\n"
},
{
"path": "kosaraju/problem8.10test3.txt",
"chars": 36,
"preview": "1 2\n2 3\n3 1\n3 4\n5 4\n6 4\n8 6\n6 7\n7 8\n"
},
{
"path": "kosaraju/problem8.10test4.txt",
"chars": 44,
"preview": "1 2\n2 3\n3 1\n3 4\n5 4\n6 4\n8 6\n6 7\n7 8\n4 3\n4 6\n"
},
{
"path": "kosaraju/problem8.10test5.txt",
"chars": 88,
"preview": "1 2\n2 3\n2 4\n2 5\n3 6\n4 5\n4 7\n5 2\n5 6\n5 7\n6 3\n6 8\n7 8\n7 10\n8 7\n9 7\n10 9\n10 11\n11 12\n12 10\n"
},
{
"path": "kosaraju/section8.6.5page64.txt",
"chars": 78,
"preview": "1 5\n2 9\n3 1\n4 2\n4 9\n5 3\n6 8\n6 11\n7 4\n7 5\n8 9\n8 10\n8 11\n9 5\n9 7\n10 2\n10 6\n11 3\n"
},
{
"path": "kotlin/dijkstra.kt",
"chars": 3354,
"preview": "import java.io.File\nimport java.util.PriorityQueue\n\nvar INF = (1e9 + 7).toInt()\n\ninterface BaseSolution {\n fun run(fi"
},
{
"path": "kotlin/greedy_scheduling.kt",
"chars": 2113,
"preview": "import java.io.File\n\ndata class Job(val weight: Long, val length: Long)\n\nclass Solution {\n fun minSum(jobs: Array<Job"
},
{
"path": "kotlin/huffman.kt",
"chars": 2840,
"preview": "/*\n * Programming Problem 14.6: Huffman Codes\n *\n * In this problem the file format is:\n * [number_of_symbols]\n * [weigh"
},
{
"path": "kotlin/knapsack.kt",
"chars": 2263,
"preview": "import java.io.File\n\nvar INF = (1e9 + 7).toInt()\n\nfun top_down(A: List<Pair<Int, Int>>, K: Int): Int {\n var N = A.siz"
},
{
"path": "kotlin/kosaraju.kt",
"chars": 4497,
"preview": "import java.util.Stack\nimport java.io.File\n\nclass RecursiveSolution(var adj: MutableMap<Int, MutableList<Int>>, var rev:"
},
{
"path": "kotlin/kruskal.kt",
"chars": 1268,
"preview": "import java.io.File\n\nfun kruskal(E: MutableList<Triple<Int, Int, Int>>): Int {\n var total: Int = 0\n var M = E.size"
},
{
"path": "kotlin/merge_sort.kt",
"chars": 918,
"preview": "fun sort(A: IntArray): IntArray {\n fun merge(A: IntArray, B: IntArray): IntArray {\n var C = mutableListOf<Int>"
},
{
"path": "kotlin/merge_sort_inversions.kt",
"chars": 1480,
"preview": "import java.io.File\n\nfun sort(A: IntArray): Pair<IntArray, Long> {\n fun merge(A: IntArray, B: IntArray): Pair<IntArra"
},
{
"path": "kotlin/prim.kt",
"chars": 1537,
"preview": "import java.io.File\nimport java.util.PriorityQueue\nimport java.util.Random\n\nfun prim(N: Int, adj: MutableMap<Int, Mutabl"
},
{
"path": "kotlin/quick_sort.kt",
"chars": 2033,
"preview": "import java.io.File\n\ntypealias PivotFunc = (A: MutableList<Int>, L: Int, R: Int) -> (Int)\nvar pivotLeft: PivotFunc = { _"
},
{
"path": "kotlin/rselect.kt",
"chars": 1351,
"preview": "import java.io.File\nimport kotlin.random.Random\n\nfun partition(A: MutableList<Int>, L: Int, R: Int): Int {\n var i = L"
},
{
"path": "kotlin/topo_sort.kt",
"chars": 2124,
"preview": "import java.util.Queue\nimport java.util.LinkedList\n\nclass Solution(val adj: MutableMap<Char, List<Char>>) {\n\n var N: "
},
{
"path": "kotlin/weighted_independent_set.kt",
"chars": 2328,
"preview": "/*\n * In this problem, each file describes the weights of vertices in a path graph and has the format:\n * [number_of_ver"
},
{
"path": "kruskal/CMakeLists.txt",
"chars": 109,
"preview": "cmake_minimum_required(VERSION 3.16)\nproject(prim)\n\nset(CMAKE_CXX_STANDARD 17)\n\nadd_executable(prim main.cpp)"
},
{
"path": "kruskal/main.cpp",
"chars": 1490,
"preview": "#include <iostream>\n#include <fstream>\n#include <vector>\n#include <unordered_map>\n#include <numeric>\n\nusing namespace st"
},
{
"path": "kruskal/main.js",
"chars": 1195,
"preview": "let LineByLine = require('n-readlines');\n\nlet kruskal = E => {\n let total = 0;\n let M = E.length;\n let P = [..."
},
{
"path": "kruskal/main.kt",
"chars": 1268,
"preview": "import java.io.File\n\nfun kruskal(E: MutableList<Triple<Int, Int, Int>>): Int {\n var total: Int = 0\n var M = E.size"
},
{
"path": "kruskal/main.py",
"chars": 1223,
"preview": "from functools import cmp_to_key\n\ndef kruskal(E, total = 0):\n M = len(E)\n P = [i for i in range(M)] # 🙂 parent rep"
},
{
"path": "kruskal/package.json",
"chars": 267,
"preview": "{\n \"name\": \"kruskal\",\n \"version\": \"1.0.0\",\n \"description\": \"\",\n \"main\": \"main.js\",\n \"scripts\": {\n \"test\": \"echo "
},
{
"path": "kruskal/problem15.9.txt",
"chars": 28290,
"preview": "500 2184\n1 2 6807\n2 3 -8874\n3 4 -1055\n4 5 4414\n5 6 1728\n6 7 -2237\n7 8 -7507\n8 9 7990\n9 10 -5012\n10 11 7353\n11 12 -6736\n1"
},
{
"path": "kruskal/problem15.9test.txt",
"chars": 64,
"preview": "6 10\n1 2 6\n1 4 5\n1 5 4\n2 4 1\n2 5 2\n2 3 5\n2 6 3\n3 6 4\n4 5 2\n5 6 4"
},
{
"path": "matrix_multiplication/main.jl",
"chars": 561,
"preview": "#\n# Straightforward Matrix Multiplication\n#\n# Input: n x n integer matrices X and Y\n#\n# Output: Z = X * Y\n#\n\nN = 2\n\nX = "
},
{
"path": "matrix_multiplication/main.py",
"chars": 545,
"preview": "#\n# Straightforward Matrix Multiplication\n#\n# Input: n x n integer matrices X and Y\n#\n# Output: Z = X * Y\n#\n\nN = 2\n\nX = "
},
{
"path": "merge_sort/CMakeLists.txt",
"chars": 125,
"preview": "cmake_minimum_required(VERSION 3.14)\nproject(mergesort)\n\nset(CMAKE_CXX_STANDARD 17)\n\nadd_executable(mergesort merge_sort"
},
{
"path": "merge_sort/merge_sort.cpp",
"chars": 974,
"preview": "#include <iostream>\n#include <vector>\n\nusing namespace std;\n\nclass Solution {\npublic:\n using VI = vector<int>;\n VI"
},
{
"path": "merge_sort/merge_sort.jl",
"chars": 758,
"preview": "#\n# Merge Sort\n#\n# Input: array A of n distinct integers\n# Output: array with the same integers, sorted from smallest to"
},
{
"path": "merge_sort/merge_sort.js",
"chars": 688,
"preview": "let sort = A => {\n let go = A => {\n let N = A.length;\n if (N < 2)\n return A;\n let hal"
},
{
"path": "merge_sort/merge_sort.kt",
"chars": 918,
"preview": "fun sort(A: IntArray): IntArray {\n fun merge(A: IntArray, B: IntArray): IntArray {\n var C = mutableListOf<Int>"
},
{
"path": "merge_sort/merge_sort.py",
"chars": 615,
"preview": "from math import floor\n\ndef sort(A):\n def go(A):\n N = len(A)\n if N < 2:\n return A\n ha"
},
{
"path": "merge_sort_inversions/CMakeLists.txt",
"chars": 160,
"preview": "cmake_minimum_required(VERSION 3.16)\nproject(merge_sort_inversions)\n\nset(CMAKE_CXX_STANDARD 17)\n\nadd_executable(merge_so"
},
{
"path": "merge_sort_inversions/merge_sort_inversions.cpp",
"chars": 1695,
"preview": "#include <iostream>\n#include <vector>\n#include <fstream>\n\nusing namespace std;\n\nclass Solution {\npublic:\n using VL = "
},
{
"path": "merge_sort_inversions/merge_sort_inversions.jl",
"chars": 1040,
"preview": "#\n# Merge Sort Inversions\n#\n# Input: array A of n distinct integers\n# Output: the number of inversions of A\n#\nfunction g"
},
{
"path": "merge_sort_inversions/merge_sort_inversions.js",
"chars": 1263,
"preview": "let sort = A => {\n let go = A => {\n let N = A.length;\n if (N < 2)\n return [A, 0];\n le"
},
{
"path": "merge_sort_inversions/merge_sort_inversions.kt",
"chars": 1480,
"preview": "import java.io.File\n\nfun sort(A: IntArray): Pair<IntArray, Long> {\n fun merge(A: IntArray, B: IntArray): Pair<IntArra"
},
{
"path": "merge_sort_inversions/merge_sort_inversions.py",
"chars": 1163,
"preview": "from math import floor\n\ndef sort(A):\n def go(A):\n N = len(A)\n if N < 2:\n return [A, 0]\n "
},
{
"path": "merge_sort_inversions/problem3.5.txt",
"chars": 688893,
"preview": "54044\r\n14108\r\n79294\r\n29649\r\n25260\r\n60660\r\n2995\r\n53777\r\n49689\r\n9083\r\n16122\r\n90436\r\n4615\r\n40660\r\n25675\r\n58943\r\n92904\r\n9900"
},
{
"path": "merge_sort_inversions/problem3.5test.txt",
"chars": 66,
"preview": "54044\r\n14108\r\n79294\r\n29649\r\n25260\r\n60660\r\n2995\r\n53777\r\n49689\r\n9083"
},
{
"path": "prim/CMakeLists.txt",
"chars": 109,
"preview": "cmake_minimum_required(VERSION 3.16)\nproject(prim)\n\nset(CMAKE_CXX_STANDARD 17)\n\nadd_executable(prim main.cpp)"
},
{
"path": "prim/main.cpp",
"chars": 1537,
"preview": "#include <iostream>\n#include <fstream>\n#include <unordered_set>\n#include <unordered_map>\n#include <queue>\n#include <rand"
},
{
"path": "prim/main.js",
"chars": 2219,
"preview": "let LineByLine = require('n-readlines');\n\nlet key = x => Array.isArray(x) ? x[0] : x;\nlet heappush = (A, x, f = Math.min"
},
{
"path": "prim/main.kt",
"chars": 1537,
"preview": "import java.io.File\nimport java.util.PriorityQueue\nimport java.util.Random\n\nfun prim(N: Int, adj: MutableMap<Int, Mutabl"
},
{
"path": "prim/main.py",
"chars": 1170,
"preview": "from random import randint\nfrom heapq import heappush, heappop\n\ndef prim(N, adj, total = 0):\n q = []\n seen = set()"
},
{
"path": "prim/package.json",
"chars": 264,
"preview": "{\n \"name\": \"prim\",\n \"version\": \"1.0.0\",\n \"description\": \"\",\n \"main\": \"main.js\",\n \"scripts\": {\n \"test\": \"echo \\\"E"
},
{
"path": "prim/problem15.9.txt",
"chars": 28290,
"preview": "500 2184\n1 2 6807\n2 3 -8874\n3 4 -1055\n4 5 4414\n5 6 1728\n6 7 -2237\n7 8 -7507\n8 9 7990\n9 10 -5012\n10 11 7353\n11 12 -6736\n1"
},
{
"path": "prim/problem15.9test.txt",
"chars": 64,
"preview": "6 10\n1 2 6\n1 4 5\n1 5 4\n2 4 1\n2 5 2\n2 3 5\n2 6 3\n3 6 4\n4 5 2\n5 6 4"
},
{
"path": "quick_sort/CMakeLists.txt",
"chars": 121,
"preview": "cmake_minimum_required(VERSION 3.16)\nproject(quick_sort)\n\nset(CMAKE_CXX_STANDARD 17)\n\nadd_executable(quick_sort main.cpp"
},
{
"path": "quick_sort/main.cpp",
"chars": 1826,
"preview": "#include <iostream>\n#include <fstream>\n#include <vector>\n\nusing namespace std;\nusing VI = vector<int>;\nusing fun = funct"
},
{
"path": "quick_sort/main.js",
"chars": 1619,
"preview": "let pivotLeft = (A, L, R) => L;\nlet pivotRight = (A, L, R) => R;\nlet pivotMedian = (A, L, R) => {\n let M = L + Math.f"
},
{
"path": "quick_sort/main.kt",
"chars": 2033,
"preview": "import java.io.File\n\ntypealias PivotFunc = (A: MutableList<Int>, L: Int, R: Int) -> (Int)\nvar pivotLeft: PivotFunc = { _"
},
{
"path": "quick_sort/main.py",
"chars": 1434,
"preview": "def pivotLeft(A, L, R): return L\ndef pivotRight(A, L, R): return R\ndef pivotMedian(A, L, R):\n M = L + (R - L) // 2\n "
},
{
"path": "quick_sort/package.json",
"chars": 270,
"preview": "{\n \"name\": \"quick_sort\",\n \"version\": \"1.0.0\",\n \"description\": \"\",\n \"main\": \"main.js\",\n \"scripts\": {\n \"test\": \"ec"
},
{
"path": "quick_sort/problem5.6.txt",
"chars": 58894,
"preview": "2148\r\n9058\r\n7742\r\n3153\r\n6324\r\n609\r\n7628\r\n5469\r\n7017\r\n504\r\n4092\r\n1582\r\n9572\r\n1542\r\n5697\r\n2081\r\n4218\r\n3130\r\n7923\r\n9595\r\n65"
},
{
"path": "quick_sort/problem5.6test1.txt",
"chars": 58,
"preview": "2148\r\n9058\r\n7742\r\n3153\r\n6324\r\n609\r\n7628\r\n5469\r\n7017\r\n504\r\n"
},
{
"path": "quick_sort/problem5.6test2.txt",
"chars": 590,
"preview": "2148\r\n9058\r\n7742\r\n3153\r\n6324\r\n609\r\n7628\r\n5469\r\n7017\r\n504\r\n4092\r\n1582\r\n9572\r\n1542\r\n5697\r\n2081\r\n4218\r\n3130\r\n7923\r\n9595\r\n65"
},
{
"path": "rec_int_mult/main.jl",
"chars": 916,
"preview": "#\n# Recursive Integer Multiplication\n#\n# Input: two n-digit positive integers x and y\n# Output: the product x * y\n# Assu"
},
{
"path": "rec_mat_mult/main.py",
"chars": 1895,
"preview": "#\n# Standard Recursive Matrix Multiplication (RecMatMult)\n#\n# Input: n x n integer matrices X and Y\n# Output: Z = X * Y\n"
},
{
"path": "rec_mat_mult/pyproject.toml",
"chars": 185,
"preview": "[project]\nname = \"rec-mat-mult\"\nversion = \"0.1.0\"\ndescription = \"Recursive matrix multiplication\"\nreadme = \"README.md\"\nr"
},
{
"path": "rselect/CMakeLists.txt",
"chars": 115,
"preview": "cmake_minimum_required(VERSION 3.16)\nproject(rselect)\n\nset(CMAKE_CXX_STANDARD 17)\n\nadd_executable(rselect main.cpp)"
},
{
"path": "rselect/main.cpp",
"chars": 1426,
"preview": "#include <iostream>\n#include <fstream>\n#include <vector>\n#include <random>\n\nusing namespace std;\nusing VI = vector<int>;"
},
{
"path": "rselect/main.js",
"chars": 1315,
"preview": "let random = (L, R) => Math.floor(Math.random() * (R + 1 - L) + L); // +1 for L..R inclusive\n\nlet partition = (A, L, R)"
},
{
"path": "rselect/main.kt",
"chars": 1351,
"preview": "import java.io.File\nimport kotlin.random.Random\n\nfun partition(A: MutableList<Int>, L: Int, R: Int): Int {\n var i = L"
},
{
"path": "rselect/main.py",
"chars": 1166,
"preview": "from random import uniform\nfrom math import floor\n\ndef partition(A, L, R):\n i = L + 1\n j = L + 1\n k = floor(uni"
},
{
"path": "rselect/package.json",
"chars": 267,
"preview": "{\n \"name\": \"rselect\",\n \"version\": \"1.0.0\",\n \"description\": \"\",\n \"main\": \"main.js\",\n \"scripts\": {\n \"test\": \"echo "
},
{
"path": "rselect/problem6.5test1.txt",
"chars": 58,
"preview": "2148\r\n9058\r\n7742\r\n3153\r\n6324\r\n609\r\n7628\r\n5469\r\n7017\r\n504\r\n"
},
{
"path": "rselect/problem6.5test2.txt",
"chars": 590,
"preview": "2148\r\n9058\r\n7742\r\n3153\r\n6324\r\n609\r\n7628\r\n5469\r\n7017\r\n504\r\n4092\r\n1582\r\n9572\r\n1542\r\n5697\r\n2081\r\n4218\r\n3130\r\n7923\r\n9595\r\n65"
},
{
"path": "strassen/main.py",
"chars": 2006,
"preview": "#\n# Strassen's Recursive Matrix Multiplication\n#\n# Input: n x n integer matrices X and Y\n# Output: Z = X * Y\n# Assumptio"
},
{
"path": "strassen/pyproject.toml",
"chars": 175,
"preview": "[project]\nname = \"strassen\"\nversion = \"0.1.0\"\ndescription = \"Add your description here\"\nreadme = \"README.md\"\nrequires-py"
},
{
"path": "strassen/variants.py",
"chars": 5707,
"preview": "#\n# Strassen-family Recursive Matrix Multiplication (2x2 block form)\n#\n# Methods:\n# - \"strassen\" : classical Strassen"
},
{
"path": "topo_sort/CMakeLists.txt",
"chars": 119,
"preview": "cmake_minimum_required(VERSION 3.16)\nproject(topo_sort)\n\nset(CMAKE_CXX_STANDARD 17)\n\nadd_executable(topo_sort main.cpp)"
},
{
"path": "topo_sort/main.cpp",
"chars": 2365,
"preview": "#include <iostream>\n#include <sstream>\n#include <vector>\n#include <unordered_map>\n#include <unordered_set>\n#include <que"
},
{
"path": "topo_sort/main.js",
"chars": 1979,
"preview": "class Solution {\n constructor(adj) {\n this.adj = adj;\n this.N = this.adj.size;\n }\n init(start) {\n"
},
{
"path": "topo_sort/main.kt",
"chars": 2124,
"preview": "import java.util.Queue\nimport java.util.LinkedList\n\nclass Solution(val adj: MutableMap<Char, List<Char>>) {\n\n var N: "
},
{
"path": "topo_sort/main.py",
"chars": 1863,
"preview": "from collections import deque\n\nclass Solution:\n def __init__(self, adj):\n self.adj = adj\n self.N = len("
},
{
"path": "traveling_salesman/README.md",
"chars": 217,
"preview": "Quiz 19.2 and 20.7 input file format:\n\n```\n[number_of_vertices] [number_of_edges]\n[one_endpoint_of_edge_1] [other_endpoi"
},
{
"path": "traveling_salesman/main.cpp",
"chars": 2407,
"preview": "#include <fstream>\n#include <iostream>\n#include <iterator>\n#include <sstream>\n#include <unordered_map>\n#include <unorder"
},
{
"path": "traveling_salesman/main.js",
"chars": 1934,
"preview": "let LineByLine = require('n-readlines');\n\nclass Solution {\n key = (u, v) => `${u},${v}`;\n init(input_file) {\n "
},
{
"path": "traveling_salesman/main.kt",
"chars": 2200,
"preview": "import java.io.File\n\nclass Solution() {\n private var best = (1e9 + 7).toInt()\n private var best_path = listOf<Int>"
},
{
"path": "traveling_salesman/main.py",
"chars": 1620,
"preview": "from random import randint\nfrom collections import defaultdict\n\nclass Solution():\n def init(self, input_file):\n "
},
{
"path": "traveling_salesman/package.json",
"chars": 303,
"preview": "{\n \"name\": \"traveling_salesman\",\n \"version\": \"1.0.0\",\n \"description\": \"Quiz 19.2 and 20.7 input file format:\",\n \"mai"
},
{
"path": "traveling_salesman/quiz19.2.txt",
"chars": 39,
"preview": "4 6\n1 2 1\n1 3 4\n1 4 3\n2 3 5\n2 4 2\n3 4 6"
},
{
"path": "traveling_salesman/quiz20.7.txt",
"chars": 65,
"preview": "5 10\n1 2 1\n1 3 4\n1 4 5\n1 5 10\n2 3 2\n2 4 6\n2 5 3\n3 4 7\n3 5 8\n4 5 9"
},
{
"path": "traveling_salesman_nearest_neighbor/README.md",
"chars": 514,
"preview": "Quiz 20.7 input file format:\n\n```\n[number_of_vertices] [number_of_edges]\n[one_endpoint_of_edge_1] [other_endpoint_of_edg"
},
{
"path": "traveling_salesman_nearest_neighbor/input.txt",
"chars": 65,
"preview": "5 10\n1 2 1\n1 3 4\n1 4 5\n1 5 10\n2 3 2\n2 4 6\n2 5 3\n3 4 7\n3 5 8\n4 5 9"
},
{
"path": "traveling_salesman_nearest_neighbor/main.py",
"chars": 935,
"preview": "from collections import defaultdict\n\nN, M = -1, -1\nadj = defaultdict(list)\ncost, key = {}, lambda u, v: f'{u},{v}'\nwith "
},
{
"path": "weighted_independent_set/CMakeLists.txt",
"chars": 149,
"preview": "cmake_minimum_required(VERSION 3.16)\nproject(weighted_independent_set)\n\nset(CMAKE_CXX_STANDARD 17)\n\nadd_executable(weigh"
},
{
"path": "weighted_independent_set/main.cpp",
"chars": 3153,
"preview": "/*\n * In this problem, each file describes the weights of vertices in a path graph and has the format:\n * [number_of_ver"
},
{
"path": "weighted_independent_set/main.js",
"chars": 2928,
"preview": "/*\n * In this problem, each file describes the weights of vertices in a path graph and has the format:\n * [number_of_ver"
},
{
"path": "weighted_independent_set/main.kt",
"chars": 2960,
"preview": "/*\n * In this problem, each file describes the weights of vertices in a path graph and has the format:\n * [number_of_ver"
},
{
"path": "weighted_independent_set/main.py",
"chars": 2586,
"preview": "#\n# In this problem, each file describes the weights of vertices in a path graph and has the format:\n# [number_of_vertic"
},
{
"path": "weighted_independent_set/package.json",
"chars": 284,
"preview": "{\n \"name\": \"weighted_independent_set\",\n \"version\": \"1.0.0\",\n \"description\": \"\",\n \"main\": \"main.js\",\n \"scripts\": {\n "
},
{
"path": "weighted_independent_set/problem16.6.txt",
"chars": 7890,
"preview": "1000\n4962786\n6395702\n5601590\n3803402\n6784626\n4944482\n2882725\n9310662\n5247184\n9819854\n8398364\n1470063\n4199696\n4623136\n816"
},
{
"path": "weighted_independent_set/problem16.6test.txt",
"chars": 41,
"preview": "10\n280\n618\n762\n908\n409\n34\n312\n277\n246\n779"
}
]
// ... and 3 more files (download for full content)
About this extraction
This page contains the full source code of the claytonjwong/Algorithms-Illuminated GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 174 files (85.3 MB), approximately 764.6k tokens, and a symbol index with 231 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.