Compare commits

..

10 Commits

6 changed files with 588 additions and 232 deletions

View File

@ -1,71 +0,0 @@
#+TITLE: Random Data Structures
#+AUTHOR: Joseph Ferano
#+OPTIONS: ^:{}
** Stack
*** C
#+begin_src C :includes stdlib.h stdio.h stdbool.h
typedef struct Node {
struct Node* next;
/* void *data; */
int data;
} Node;
typedef struct Stack {
Node* top;
int length;
} Stack;
/* void stack_create(void* data) { */
Stack *stack_create(int data) {
Stack *stack = malloc(sizeof(Stack));
Node *node = malloc(sizeof(Node));
node->data = data;
node->next = NULL;
stack->top = node;
stack->length = 1;
return stack;
}
int stack_pop(Stack *stack) {
Node *top = stack->top;
Node *next = top->next;
if (stack->length > 0 && next != NULL) {
stack->top = next;
stack->length--;
int value = top->data;
free(top);
return value;
} else {
// A better API design would be to return a bool and the int as a pointer
// Although once we switch away from int and use void pointers, might not be needed
return -1;
}
}
/* void stack_push(Stack *stack, void *data) { */
void stack_push(Stack *stack, int data) {
Node *node = malloc(sizeof(Node));
Node *top = stack->top;
node->data = data;
node->next = top;
stack->top = node;
stack->length++;
}
/* void* stack_peak(Stack *stack) { */
int stack_peak(Stack *stack) {
return stack->top->data;
}
void stack_print(Stack *stack) {
Node *current = stack->top;
int i = 0;
while (current != NULL) {
printf("Stack at %d: %d\n", ++i, current->data);
current = current->next;
}
printf("------------------\n");
}
#+end_src

View File

@ -1,138 +0,0 @@
#+TITLE: Notes & Exercises: Grokking Algorithms
#+AUTHOR: Joseph Ferano
#+OPTIONS: ^:{}
* Algorithms from the book
** Recursive sum
*** OCaml
#+begin_src ocaml
let rec sum_rec = function
| [] -> 0
| n::ns -> n + sum_rec ns;;
sum_rec [2;3;4;2;1];;
#+end_src
#+RESULTS:
: 12
#+begin_src ocaml
let sum_rec_tail list =
let rec f acc = function
| [] -> 0
| n::ns -> sum_rec (acc + n) ns
in f 0 list;;
sum_rec [2;3;4;2;1];;
#+end_src
#+RESULTS:
: 12
*** Python
#+begin_src python :results output
def sum_rec(arr):
if not arr:
return 0
else:
return arr[0] + sum_rec(arr[1:])
print(sum_rec([1,2,3]))
#+end_src
#+RESULTS:
: 6
** Binary Search
*** OCaml
#+begin_src ocaml
let binary_search items target =
let rec f low high =
match (high - low) / 2 + low with
| mid when target = items.(mid) -> Some items.(mid)
| mid when target < items.(mid) -> f low mid
| mid when target > items.(mid) -> f mid high
| _ -> None
in f 0 (Array.length items);;
binary_search [|1;2;3;4;5|] 3;;
#+end_src
** Selection Sort
Runtime O(n^{2})
*** Python
#+begin_src python
def selection_sort(arr):
sorted_list = []
for i in range(len(arr)):
max = arr[0]
for count, value in enumerate(arr):
if value > max:
max = value
sorted_list.append(max)
arr.remove(max)
return sorted_list
selection_sort([2,1,5,3,4])
#+end_src
*** OCaml
Really reinventing the wheel on this one...
#+begin_src ocaml
let max_element = function
| [] -> invalid_arg "empty list"
| x::xs ->
let rec f acc = function
| [] -> acc
| x::xs -> f (if x > acc then x else acc) xs
in f x xs
let remove item list =
let rec f acc item = function
| [] -> List.rev acc
| x::xs -> if item = x then (List.rev acc) @ xs else f (x::acc) item xs
in f [] item list
let selection_sort list =
let rec f acc = function
| [] -> acc
| xs ->
let m = max xs
in f (m::acc) (remove m xs)
in f [] list
#+end_src
** Quicksort
*** Python
#+begin_src python
import random
def quicksort(arr):
if len(arr) < 2:
return arr
elif len(arr) == 2:
if arr[0] > arr[1]:
temp = arr[1]
arr[1] = arr[0]
arr[0] = temp
return arr
else:
# Pick a random pivot
index = random.randrange(0, len(arr))
pivot = arr.pop(index)
left = [x for x in arr if x <= pivot]
right = [x for x in arr if x > pivot]
return quicksort(left) + [pivot] + quicksort(right)
#+end_src

20
LICENSE Normal file
View File

@ -0,0 +1,20 @@
Copyright (c) 2023 Joseph Ferano - joseph@ferano.io
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -1,9 +1,8 @@
#+TITLE: Notes & Exercises: The Algorith Design Manual
#+AUTHOR: Joseph Ferano #+AUTHOR: Joseph Ferano
#+STARTUP: overview #+STARTUP: overview
#+OPTIONS: ^:{} #+OPTIONS: ^:{}
* Chapter 1 * Chapter 1 - Introduction
** 1.1 Robots ** 1.1 Robots
@ -70,7 +69,7 @@ structures. These fundamental structures include;
** 1.5-1.6 War Story about Psychics ** 1.5-1.6 War Story about Psychics
* Chapter 2 * Chapter 2 - Algorithm Analyses
** 2.1 RAM Model of Computation ** 2.1 RAM Model of Computation
@ -126,6 +125,52 @@ Apparently you can do arithmetic on the Big Oh functions
** 2.5 Efficiency ** 2.5 Efficiency
*** Selection Sort *** Selection Sort
**** OCaml
Really reinventing the wheel on this one...
#+begin_src ocaml
let max_element = function
| [] -> invalid_arg "empty list"
| x::xs ->
let rec f acc = function
| [] -> acc
| x::xs -> f (if x > acc then x else acc) xs
in f x xs
let remove item list =
let rec f acc item = function
| [] -> List.rev acc
| x::xs -> if item = x then (List.rev acc) @ xs else f (x::acc) item xs
in f [] item list
let selection_sort list =
let rec f acc = function
| [] -> acc
| xs ->
let m = max xs
in f (m::acc) (remove m xs)
in f [] list
#+end_src
**** Python
#+begin_src python
def selection_sort(arr):
sorted_list = []
for i in range(len(arr)):
max = arr[0]
for count, value in enumerate(arr):
if value > max:
max = value
sorted_list.append(max)
arr.remove(max)
return sorted_list
selection_sort([2,1,5,3,4])
#+end_src
**** C **** C
#+begin_src C :includes stdio.h #+begin_src C :includes stdio.h
@ -157,17 +202,6 @@ int nums[9] = { 2, 4, 9, 1, 3, 8, 5, 7, 6 };
selection_sort(nums, 9); selection_sort(nums, 9);
#+end_src #+end_src
#+RESULTS:
| 2 | 4 | 9 | 1 | 3 | 8 | 5 | 7 | 6 | |
| 1 | 4 | 9 | 2 | 3 | 8 | 5 | 7 | 6 | |
| 1 | 2 | 9 | 4 | 3 | 8 | 5 | 7 | 6 | |
| 1 | 2 | 3 | 4 | 9 | 8 | 5 | 7 | 6 | |
| 1 | 2 | 3 | 4 | 9 | 8 | 5 | 7 | 6 | |
| 1 | 2 | 3 | 4 | 5 | 8 | 9 | 7 | 6 | |
| 1 | 2 | 3 | 4 | 5 | 6 | 9 | 7 | 8 | |
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | 8 | |
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | |
*** Insertion Sort *** Insertion Sort
**** C **** C
@ -215,6 +249,7 @@ no real impact on the growth rate; log_{2} and log_{3} are roughly equivalent.
Cool story bro Cool story bro
** 2.9 Advanced Analysis ** 2.9 Advanced Analysis
Some advanced stuff Some advanced stuff
@ -224,11 +259,11 @@ Some advanced stuff
Binary search on a sorted array of only log n items Binary search on a sorted array of only log n items
- *log n / log log n* - *log n / log log n*
- log^{2} n - log^{2} n
- \sqrt{,}n - sqrt(n)
There are also limits and dominance relations There are also limits and dominance relations
* Chapter 3 * Chapter 3 - Data Structures
** 3.1 Contiguous vs Linked Data Structures ** 3.1 Contiguous vs Linked Data Structures
@ -252,9 +287,145 @@ However, pointers require extra space for storing pointer fields
*** Stacks *** Stacks
/(PUSH, /POP/) LIFO, useful in executing recursive algorithms. /(PUSH, /POP/) LIFO, useful in executing recursive algorithms.
#+begin_src C :includes stdlib.h stdio.h stdbool.h
typedef struct Node {
struct Node* next;
/* void *data; */
int data;
} Node;
typedef struct Stack {
Node* top;
int length;
} Stack;
/* void stack_create(void* data) { */
Stack *stack_create(int data) {
Stack *stack = malloc(sizeof(Stack));
Node *node = malloc(sizeof(Node));
node->data = data;
node->next = NULL;
stack->top = node;
stack->length = 1;
return stack;
}
int stack_pop(Stack *stack) {
Node *top = stack->top;
Node *next = top->next;
if (stack->length > 0 && next != NULL) {
stack->top = next;
stack->length--;
int value = top->data;
free(top);
return value;
} else {
// A better API design would be to return a bool and the int as a pointer
// Although once we switch away from int and use void pointers, might not be needed
return -1;
}
}
/* void stack_push(Stack *stack, void *data) { */
void stack_push(Stack *stack, int data) {
Node *node = malloc(sizeof(Node));
Node *top = stack->top;
node->data = data;
node->next = top;
stack->top = node;
stack->length++;
}
/* void* stack_peak(Stack *stack) { */
int stack_peak(Stack *stack) {
return stack->top->data;
}
void stack_print(Stack *stack) {
Node *current = stack->top;
int i = 0;
while (current != NULL) {
printf("Stack at %d: %d\n", ++i, current->data);
current = current->next;
}
printf("------------------\n");
}
#+end_src
*** Queues *** Queues
(/ENQUEUE/, /DEQUEUE/) FIFO, useful for breadth-first searches in graphs. (/ENQUEUE/, /DEQUEUE/) FIFO, useful for breadth-first searches in graphs.
#+name: queue
#+begin_src C :includes stdio.h stdlib.h
#define QBUFSIZE 64
#define T int
struct queue {
T buf[QBUFSIZE];
int start;
int end;
int size;
};
struct queue *q_create() {
struct queue *q = calloc(1, sizeof(struct queue));
q->start = 0;
q->end = 0;
}
void q_enqueue(struct queue *q, T item) {
if (q->size == QBUFSIZE) {
printf("Queue Overflow");
exit(1);
}
q->buf[q->end] = item;
q->end = ++q->end % QBUFSIZE;
q->size++;
}
T q_dequeue(struct queue *q) {
if (q->size == 0) {
printf("Queue empty");
exit(1);
}
T item = q->buf[q->start++];
q->size--;
return item;
}
T q_peek(struct queue *q) {
if (q->size == 0) {
printf("Queue empty");
exit(1);
}
return q->buf[q->start];
}
bool q_empty(struct queue *q) {
return q->size <= 0;
}
void q_print(struct queue *q) {
printf("Qeueu_Elements: ");
for (int i = 0; i < q->size; i++) {
printf("%i-", q->buf[(i + q->start) % QBUFSIZE]);
}
printf("\n");
}
// struct queue *q = q_create();
// q_enqueue(q, 1);
// q_enqueue(q, 2);
// q_enqueue(q, 3);
// q_enqueue(q, 4);
// q_dequeue(q);
// q_dequeue(q);
// q_enqueue(q, 5);
// q_enqueue(q, 6);
// q_print(q);
#+end_src
** 3.3 Dictionaries ** 3.3 Dictionaries
Not just hashtables but anything that can provide access to data by Not just hashtables but anything that can provide access to data by
@ -359,7 +530,7 @@ printf("Final: %s\n", str);
| After: | sirhC | si | eman | yM | | After: | sirhC | si | eman | yM |
| Final: | Chris | is | name | My | | Final: | Chris | is | name | My |
* Chapter 4 * Chapter 4 - Sorting and Searching
** 4.1 Applications of Sorting ** 4.1 Applications of Sorting
@ -419,19 +590,21 @@ because the nodes aren't guaranteed to be ordered, only the relationship between
Here's the full heap implementation; Here's the full heap implementation;
#+begin_src C :includes stdio.h stdlib.h #+begin_src C :includes stdio.h stdlib.h
#define PQ_SIZE 256
#define item_type int #define item_type int
struct priority_queue { struct priority_queue {
// TODO: Why did I make this a pointer?
item_type *q; item_type *q;
int len; int len;
int capacity;
}; };
struct priority_queue *pq_create() { struct priority_queue *pq_create(int capacity) {
item_type *q = calloc(PQ_SIZE, sizeof(item_type)); item_type *q = calloc(capacity, sizeof(item_type));
struct priority_queue *pq = malloc(sizeof(struct priority_queue)); struct priority_queue *pq = malloc(sizeof(struct priority_queue));
pq->q = q; pq->q = q;
pq->len = 0; pq->len = 0;
pq->capacity = capacity;
} }
int pq_parent(int n) { int pq_parent(int n) {
@ -488,7 +661,7 @@ void pq_bubble_down(struct priority_queue *pq, int n) {
} }
void pq_insert(struct priority_queue *pq, item_type x) { void pq_insert(struct priority_queue *pq, item_type x) {
if (pq->len >= PQ_SIZE) { if (pq->len >= pq->capacity) {
printf("Error: Priority Queue Overflow"); printf("Error: Priority Queue Overflow");
return; return;
} }
@ -567,7 +740,7 @@ print_pq(pq);
** 4.4 War Story ** 4.4 War Story
Apparently calculating airline tickets is hard Apparently calculating the price of airline tickets is hard.
** 4.5 Mergesort ** 4.5 Mergesort
@ -712,6 +885,29 @@ new arrays to hold the new sorted elements. However, it becomes more challenging
when doing it in place. This algorithm requires 3 pointers to keep track of the when doing it in place. This algorithm requires 3 pointers to keep track of the
mid point, the iterator, and the high, then finish once mid passes h. mid point, the iterator, and the high, then finish once mid passes h.
*** Python
#+begin_src python
import random
def quicksort(arr):
if len(arr) < 2:
return arr
elif len(arr) == 2:
if arr[0] > arr[1]:
temp = arr[1]
arr[1] = arr[0]
arr[0] = temp
return arr
else:
# Pick a random pivot
index = random.randrange(0, len(arr))
pivot = arr.pop(index)
left = [x for x in arr if x <= pivot]
right = [x for x in arr if x > pivot]
return quicksort(left) + [pivot] + quicksort(right)
#+end_src
** 4.7 Distribution Sort: Bucketing ** 4.7 Distribution Sort: Bucketing
Two other sorting algorithms function similarly by subdividing the sorting Two other sorting algorithms function similarly by subdividing the sorting
@ -735,6 +931,21 @@ dealing with lazy sequences, where you search first by A[1], then A[2], A[4],
A[8], A[16], and so forth. You can also use these sorts of bisections on square A[8], A[16], and so forth. You can also use these sorts of bisections on square
root problems, whatever those are. root problems, whatever those are.
Here's a simple binary search guessing game;
#+begin_src ocaml
let binary_search items target =
let rec f low high =
match (high - low) / 2 + low with
| mid when target = items.(mid) -> Some items.(mid)
| mid when target < items.(mid) -> f low mid
| mid when target > items.(mid) -> f mid high
| _ -> None
in f 0 (Array.length items);;
binary_search [|1;2;3;4;5|] 3;;
#+end_src
** 4.10 Divide-and-Conquer ** 4.10 Divide-and-Conquer
Algorithms that use this technique, Mergesort being the classic example, have Algorithms that use this technique, Mergesort being the classic example, have
@ -747,6 +958,317 @@ relations.
It is an equation that is defined in terms of itself, so I guess recursive. It is an equation that is defined in terms of itself, so I guess recursive.
Fibonacci is a good example F_{n} = F_{n-1} + F_{n-2}... Fibonacci is a good example F_{n} = F_{n-1} + F_{n-2}...
* Chapter 5 * Chapter 5 - Graph Traversal
** 5.1 Graphs
Graphs are G = (E,V), so a set of edges and a set of vertices. Many real world
systems can be modeled as graphs, such as road/city networks. Many algorithmic
problems become much simpler when modeled with graphs. There are several flavors
of graphs;
- Directed vs Undirected
- Weighted vs Unweighted
- Simple vs Non-simple
- Spares vs Dense
- Cyclic vs Acyclic
- Embedded vs Topological
- Implicted vs Explicit
- Labeled vs Unlabeled
Social networks provide an interesting way to analyze each one of these
considerations.
** 5.2 Data Structures for Graphs
Which data structure we use will impact the time and space complexity of certain
operations.
- Adjecency Matrix
You can use an /n/ x /m/ matrix, where each /(i,j)/ index answers whether an edge
exists. However, with sparse graphs, there might be a lot of wasted space.
- Adjencency Lists
Use linked lists instead, however it is harder to verify if a certain edge
exists. This can be mitigated by collecting them in a BFS of DFS.
#+name: graph
#+begin_src C :includes stdio.h stdlib.h stdbool.h
#define MAXV 1000
struct edgenode {
int y;
int weight;
struct edgenode *next;
};
struct graph {
struct edgenode *edges[MAXV];
int degree[MAXV];
int nvertices;
int nedges;
bool directed;
};
void initialize_graph(struct graph *g, bool directed) {
g->nvertices = 0;
g->nedges = 0;
g->directed = directed;
for (int i = 0; i < MAXV; i++) g->degree[i] = 0;
for (int i = 0; i < MAXV; i++) g->edges[i] = NULL;
}
// TODO: We have to read whether the graph is directed or not and
// insert edges twice
void insert_edge(struct graph *g, int x, int y) {
struct edgenode *p;
p = malloc(sizeof(struct edgenode));
p->weight = 0;
p->y = y;
p->next = g->edges[x];
g->edges[x] = p;
g->degree[x]++;
g->nedges++;
}
void print_graph(struct graph *g) {
int i;
struct edgenode *p;
for (i = 0; i < g->nvertices; i++) {
printf("V%d", i+1);
p = g->edges[i];
while (p != NULL) {
printf("->%d", p->y);
p = p->next;
}
printf("\n");
}
}
struct graph *g = malloc(sizeof(struct graph));
initialize_graph(g, true);
g->nvertices = 3;
insert_edge(g, 0, 1);
insert_edge(g, 0, 2);
insert_edge(g, 0, 3);
insert_edge(g, 1, 1);
insert_edge(g, 1, 3);
insert_edge(g, 2, 1);
print_graph(g);
#+end_src
** 5.3 War Story
Apparently, computers were really slow before. That and it's better to keep
asymptotics in mind even if it's about the same.
** 5.4 War Story
Apparently loading data itself can be really slow.
** 5.5 Traversing a Graph
When traversing a graph, it's useful to keep track of the state of each vertex,
whether the vertex has been /undiscovered/, /discovered/, or /processed/.
** 5.6 Breadth-First Search (BFS)
This is a C implementation of BFS, however at the moment it doesn't really do
much. The important thing here is that a BFS uses a queue to visit every node in
the graph.
#+begin_src C :includes stdio.h stdlib.h stdbool.h :noweb yes
<<queue>>
<<graph>>
bool processed[QBUFSIZE];
bool discovered[QBUFSIZE];
int parent[QBUFSIZE];
void initialize_search(struct graph *g) {
for (int i = 0; i < g->nvertices; i++) {
processed[i] = discovered[i] = false;
parent[i] = -1;
}
}
void process_edge(T vertex, T edge) {}
void bfs(struct graph *g, int start) {
struct queue *q = q_create();
initialize_search(g);
q_enqueue(q, start);
discovered[start] = true;
while (!q_empty(q)) {
T v = q_dequeue(q);
processed[v] = true;
struct edgenode *p = g->edges[v];
while (p != NULL) {
T y = p->y;
if ((processed[y] == false) || g->directed) {
process_edge(v, y);
}
if (discovered[y] == false) {
q_enqueue(q, y);
discovered[y] = true;
parent[y] = v;
}
p = p->next;
}
}
free(q);
}
#+end_src
** 5.7 Applications of BFS
The one project we worked on, the "Six Degrees of Bacon", which is just a six
degrees of separation problem. However, one interesting point to the "six
degrees of separation" between all humans is that it assumes that all human
social networks are "connected", meaning that there may be vertices on the graph
that are not connected to the others and form their own little cluster. BFS
works well for this, since it's basically a shortest path problem.
Other applications include solving a Rubiks cube so in theory you should now
know enough to be able to create a solver, because each legal move up until the
solved move should be connected on the graph.
The vertex-coloring problem assigns each vertex a label or color such that no
edge links any two vertices of the same label/color, ideally using as few colors
as possible. A graph is /bipartite/ if it can be colored without conflicts with
just two colors.
** 5.8 Depth-first Search (DFS)
While BFS uses a queue, DFS uses a stack, but since recursion models a stack
already, we don't need an extra data structure. Here is the C implementation
mostly just copied from the book.
#+begin_src C :includes stdio.h stdlib.h stdbool.h :noweb yes
<<graph>>
void dfs(struct graph *g, int v) {
edgenode *p;
int y;
// No idea where this symbol comes from
// They seem to be global
if (finished) return;
discovered[v] = true;
// And these two as well
entry_time[v] = ++time;
p = g->edges[v];
while (p != NULL) {
y = p->y;
if (discovered[y] == false) {
parent[y] == v;
process_edge(v, y);
dfs(g, y);
} else if (!processed[y] || g->directed) {
process_edge(v, y);
if (finished) return;
p = p->next;
}
time = time + 1;
exit_time[v] = time;
processed[v] = true;
}
}
#+end_src
This uses a technique called Backtracking which still hasn't been explored, but
the idea is that it goes further down until it cannot process anything further,
then goes all the way back up to where it can start branching out again.
** 5.9 Applications of DFS
DFS is useful for finding articulation vertices, which are basically weak points
where removal will cause other vertices to become disconnected. It's also useful
for finding cycles.
** 5.10 DFS on Directed Graphs
The important operation here is topological sorting which is useful with
Directed Acyclic Graphs (DAGs). We can also check if a DAG is strongly
connected, meaning that we won't run into any dead ends since we cannot
backtrack. However, these graphs can be partitioned.
* Chapter 6 - Weighted Graph Algorithms
** 6.1 Minimum Spanning Trees
Weighted graphs open up a whole new universe of algorithms which tend to be a
bit more helpful in modeling real world stuff like roads. A minimum spanning
tree would have all vertices connected but uses the smallest weights for all its
edges.
Prim's Algorithm can be used to construct a spanning tree but because it is a
greedy algorithm. Kruskal's algorithm is a more efficient way to find MSTs with
the use of a /union-find/. This data structure is a /set partition/, which finds
disjointed subsets. These can also be used to solve other interesting problems;
- Maximum Spanning Trees
- Minimum Product Spanning Trees
- Minimum Bottleneck Spanning Tree
However, Steiner Tree and Low-degree Spanning Tree apparently cannot be solved
with the previous two algorithms.
** 6.2 War Story
Minimum Spanning Trees and its corresponding algorithms can help solve Traveling
Salesman Problems
** 6.3 Shortest Paths
In an unweighted graph, BFS will find the shortest path between two nodes. For
weighted graphs the shortest path might have many more edges. Dijstra's
algorithm can help us find the shortest path in a weighted path. It runs in
O(n^{2}) time. One caveat is that it does not work with negative weighted edges.
Another problem in this space is the all-pairs shortest path, and for this we
would use the O(n^{3}) Floyd-Warshall algorithm which uses an adjacency matrix
instead since we needed to construct one anyway to track all the possible pairs.
It's a useful algorithm for figuring out /transitive closure/ which is a fancy way
of asking if some vertices are reachable from a node.
*** TODO Dijstra's
#+begin_src C :includes stdio.h stdlib.h
#+end_src
** 6.4 War Story
Kids are probably too young to know what the author is even talking about with these
phone codes.
** 6.5 Network Flows and Bipartite Matching
Bipartite matching is where no two edges share a vertex. There are also residual
flow graphs which are useful for network bandwidth optimization. Flow algorithms
generally solve problems related to edge and vertex connectivity. Edmonds and
Karp runs at O(n^{3}).
*** TODO Network Flow
** 6.6 Design Graphs, Not Algorithms
Modeling problems as graphs is useful for a variety of applications; pathfinding
in games, DNA sequencing, smallest set of non-overlapping rectangles in a plain,
filename shortening, line segmentation for optical character-recognition, and
the list goes on.
* Chapter 7 - Combinatorial Search and Heuristic Methods
** 7.1 Backtracking

23
README.org Normal file
View File

@ -0,0 +1,23 @@
* About
[[./Notes.org]] contains notes and solutions to exercises, as well as
implementations of [[https://www.algorist.com/][The Algorithm Design Manual]]. All notes and source code can be
found in this single file and the reason for that is that it uses a style of
programming known as [[https://en.wikipedia.org/wiki/Literate_programming][Literate Programming]] with the help of Emacs, org-mode, and
[[https://orgmode.org/worg/org-contrib/babel/intro.html][org-babel]]. Most code implementations are done in C, however, some have been also
implemented in Python or OCaml in order to compare and contrast styles. All code
is executable from within Emacs by just running ~C-c C-c~ while the cursor is in
the source code block.
* Progress
So far, the first 6 chapters have a decent amount of coverage. The focus is more
on implementing the actual data structures and algorithms discussed in the book,
rather than working through the exercises, although some have solutions. I plan
on revisiting the book from time to time, to keep developing my knowledge of
Data Structures and Algorithms.
* Screenshot of org-mode
[[./screenshot.png]]

BIN
screenshot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB