Finishing notes for Chapter 5
This commit is contained in:
parent
1e126d78fa
commit
aae61d0441
@ -302,6 +302,10 @@ T q_peek(struct queue *q) {
|
||||
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++) {
|
||||
@ -487,19 +491,21 @@ because the nodes aren't guaranteed to be ordered, only the relationship between
|
||||
Here's the full heap implementation;
|
||||
|
||||
#+begin_src C :includes stdio.h stdlib.h
|
||||
#define PQ_SIZE 256
|
||||
#define item_type int
|
||||
|
||||
struct priority_queue {
|
||||
// TODO: Why did I make this a pointer?
|
||||
item_type *q;
|
||||
int len;
|
||||
int capacity;
|
||||
};
|
||||
|
||||
struct priority_queue *pq_create() {
|
||||
item_type *q = calloc(PQ_SIZE, sizeof(item_type));
|
||||
struct priority_queue *pq_create(int capacity) {
|
||||
item_type *q = calloc(capacity, sizeof(item_type));
|
||||
struct priority_queue *pq = malloc(sizeof(struct priority_queue));
|
||||
pq->q = q;
|
||||
pq->len = 0;
|
||||
pq->capacity = capacity;
|
||||
}
|
||||
|
||||
int pq_parent(int n) {
|
||||
@ -556,7 +562,7 @@ void pq_bubble_down(struct priority_queue *pq, int n) {
|
||||
}
|
||||
|
||||
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");
|
||||
return;
|
||||
}
|
||||
@ -849,6 +855,7 @@ operations.
|
||||
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
|
||||
|
||||
@ -934,11 +941,131 @@ 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 :noweb yes
|
||||
#+begin_src C :includes stdio.h stdlib.h stdbool.h :noweb yes
|
||||
<<queue>>
|
||||
struct queue *q2 = q_create();
|
||||
<<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
|
||||
|
||||
#+RESULTS:
|
||||
** 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
|
||||
|
||||
** 6.1 Minimum Spanning Trees
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user