mirror of
https://github.com/mfocko/blog.git
synced 2025-05-09 21:02:59 +02:00
chore: transfer all KBs to single one
Signed-off-by: Matej Focko <mfocko@redhat.com>
This commit is contained in:
parent
d207e870d4
commit
7427475022
159 changed files with 28847 additions and 0 deletions
ib002/10-graphs
6
ib002/10-graphs/_category_.yaml
Normal file
6
ib002/10-graphs/_category_.yaml
Normal file
|
@ -0,0 +1,6 @@
|
|||
label: Graphs
|
||||
position: 10
|
||||
link:
|
||||
type: generated-index
|
||||
description: |
|
||||
Materials related to basic graph algorithms and graph problems.
|
149
ib002/10-graphs/bfs-tree.md
Normal file
149
ib002/10-graphs/bfs-tree.md
Normal file
|
@ -0,0 +1,149 @@
|
|||
---
|
||||
title: Distance boundaries from BFS tree on undirected graphs
|
||||
description: |
|
||||
Short explanation of distance boundaries deduced from a BFS tree.
|
||||
---
|
||||
|
||||
## Introduction
|
||||
|
||||
As we have talked on the seminar, if we construct from some vertex $u$ BFS tree on an undirected graph, we can obtain:
|
||||
|
||||
- lower bound of length of the shortest path between 2 vertices from the _height difference_
|
||||
- upper bound of length of the shortest path between 2 vertices from the _path through the root_
|
||||
|
||||
## Lower bound
|
||||
|
||||
Consider the following graph:
|
||||
|
||||

|
||||
|
||||
<details>
|
||||
<summary>Source for the rendered graph</summary>
|
||||
|
||||
```dot showLineNumbers
|
||||
graph {
|
||||
a -- c
|
||||
a -- e
|
||||
|
||||
c -- i
|
||||
c -- b
|
||||
|
||||
e -- j
|
||||
|
||||
i -- d
|
||||
|
||||
b -- h
|
||||
|
||||
d -- h
|
||||
|
||||
h -- j
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
We run BFS from the vertex $a$ and obtain the following BFS tree:
|
||||
|
||||

|
||||
|
||||
<details>
|
||||
<summary>Source for the BFS tree</summary>
|
||||
|
||||
```dot showLineNumbers
|
||||
digraph {
|
||||
a -> c
|
||||
a -> e
|
||||
|
||||
c -> b
|
||||
c -> i
|
||||
|
||||
e -> j
|
||||
|
||||
b -> h
|
||||
|
||||
i -> d
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
Let's consider pair of vertices $e$ and $h$. For them we can safely lay, from the BFS tree, following properties:
|
||||
|
||||
- lower bound: $2$
|
||||
- upper bound: $4$
|
||||
|
||||
By having a look at the graph we started from, we can see that we have a path ‹$e, j, h$› that has a length 2. Apart from that we can also notice there is another path from $e$ to $h$ and that is ‹$e, a, c, i, d, h$›. And that path has a length of $5$. Doesn't this break our statements at the beginning? (_I'm leaving that as an exercise ;)_)
|
||||
|
||||
## Proof by contradiction
|
||||
|
||||
Let's keep the same graph, but break the lower bound, i.e. I have gotten a lower bound $2$, but „there must be a shorter path“! ;)
|
||||
|
||||
Now the more important question, is there a shorter path in that graph? The answer is no, there's no shorter path than the one with length $2$. So what can we do about it? We'll add an edge to have a shorter path. Now we have gotten a lower bound of $2$, which means the only shorter path we can construct has $1$ edge and that is ‹$e, h$› (no intermediary vertices). Let's do this!
|
||||
|
||||

|
||||
|
||||
<details>
|
||||
<summary>Source for the BFS graph with an additional edge</summary>
|
||||
|
||||
```dot showLineNumbers
|
||||
graph {
|
||||
a -- c
|
||||
a -- e
|
||||
|
||||
c -- i
|
||||
c -- b
|
||||
|
||||
e -- j
|
||||
e -- h
|
||||
|
||||
i -- d
|
||||
|
||||
b -- h
|
||||
|
||||
d -- h
|
||||
|
||||
h -- j
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
Okay, so we have a graph that breaks the rule we have laid. However, we need to run BFS to obtain the new BFS tree, since we have changed the graph.
|
||||
|
||||
:::tip
|
||||
|
||||
Do we need to run BFS after **every** change?
|
||||
|
||||
I am leaving that as an exercise ;)
|
||||
|
||||
:::
|
||||
|
||||

|
||||
|
||||
<details>
|
||||
<summary>Source for the BFS tree</summary>
|
||||
|
||||
```dot showLineNumbers
|
||||
digraph {
|
||||
a -> c
|
||||
a -> e
|
||||
|
||||
c -> b
|
||||
c -> i
|
||||
|
||||
e -> h
|
||||
e -> j
|
||||
|
||||
i -> d
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
Oops, we have gotten a new BFS tree, that has a height difference of 1.
|
||||
|
||||
:::tip
|
||||
|
||||
Try to think about a way this can be generalized for shortening of minimal length 3 to minimal length 2 ;)
|
||||
|
||||
:::
|
133
ib002/10-graphs/iterative-and-iterators.md
Normal file
133
ib002/10-graphs/iterative-and-iterators.md
Normal file
|
@ -0,0 +1,133 @@
|
|||
---
|
||||
title: Iterative algorithms via iterators
|
||||
description: |
|
||||
Examples of iterative implementations of DFS using iterators.
|
||||
---
|
||||
|
||||
## Introduction
|
||||
|
||||
- [Source code used later on.](pathname:///files/ib002/iterative-and-iterators/)
|
||||
|
||||
As we have talked on the seminar, iterative approach to implementing DFS is not very intuitive and is a very easy way how to create an incorrect implementation.
|
||||
|
||||
On the other hand, we have seen iterative implementation in the exercises and I have also prepared two from which one was similar to recursive implementation without colors from exercises and the other one used features of high-level languages.
|
||||
|
||||
## Different implementations
|
||||
|
||||
### Recursive DFS implementation from exercises without colors
|
||||
|
||||
```
|
||||
FUNCTION VisitedDFS(u, Visited) IS
|
||||
Visited <- Union(Visited, { u })
|
||||
FOR v IN u.successors DO
|
||||
IF v NOT IN Visited THEN
|
||||
Visited <- VisitedDFS(v, Visited)
|
||||
FI
|
||||
OD
|
||||
|
||||
RETURN Visited
|
||||
END
|
||||
```
|
||||
|
||||
This implementation is correct, does the DFS traversal as it should, however it has one „smallish“ downside and that is the time complexity. The usage of set raises the time complexity, of course it is implementation dependant. However in case of either RB-tree or hash-table implementation, we get look-up in time $\mathcal{O}(n)$ for hash-table in worst-case or $\mathcal{O}(\log n)$ for the other in the worst-case. Both are not ideal compared to checking color on vertex.
|
||||
|
||||
### Iterative DFS from the exercises
|
||||
|
||||
```
|
||||
PROCEDURE IterDFS(u)
|
||||
stack <- empty stack
|
||||
Push(stack, (u, 0))
|
||||
u.color <- gray
|
||||
time <- 1
|
||||
u.d <- time
|
||||
|
||||
WHILE NOT Empty(stack) DO
|
||||
(u, k) <- Pop(stack)
|
||||
|
||||
IF k < Length(u.successors) THEN
|
||||
// search is not finished, is pushed back to stack
|
||||
Push(stack, (u, k + 1))
|
||||
|
||||
v <- u.successors[k + 1]
|
||||
IF v.color = white THEN
|
||||
Push(stack, (v, 0))
|
||||
v.color <- gray
|
||||
time <- time + 1
|
||||
v.d <- time
|
||||
FI
|
||||
ELSE
|
||||
// u has no other successors, we can finish the search
|
||||
time <- time + 1
|
||||
u.f <- time
|
||||
u.color <- black
|
||||
FI
|
||||
OD
|
||||
END
|
||||
```
|
||||
|
||||
As we can see, there is some ordering in which we search through the successors. Time complexity is OK, stack holds at most all vertices (they must be on the current path).
|
||||
|
||||
### My iterative with path in stack
|
||||
|
||||
```
|
||||
PROCEDURE DFS(G, start) IS
|
||||
path <- [ start ]
|
||||
time <- 1
|
||||
start.d, start.color <- time, gray
|
||||
|
||||
WHILE NOT Empty(path) DO
|
||||
hasSuccessor <- false
|
||||
FOR successor IN path[-1].successors DO
|
||||
IF successor.color = white THEN
|
||||
hasSuccessor <- true
|
||||
successor.d, successor.color <- ++time, gray
|
||||
path <- Append(path, successor)
|
||||
BREAK
|
||||
FI
|
||||
OD
|
||||
IF NOT hasSuccessor THEN
|
||||
lastVertex <- Pop(path)
|
||||
lastVertex.f, lastVertex.color <- ++time, black
|
||||
FI
|
||||
OD
|
||||
END
|
||||
```
|
||||
|
||||
This approach is similar to the iterative solution from the exercises, but it does not keep the index of the next successor, therefore it always iterates through all of them, which raises the time complexity.
|
||||
|
||||
### My iterative solution with iterators
|
||||
|
||||
On the other hand, we do not actually have to depend on the representation of the graph. In this case, we just _somehow_ obtain the iterator (which yields all of the succesors) and keep it in the stack.
|
||||
|
||||
```
|
||||
PROCEDURE DFS(G, start) IS
|
||||
path <- [ (start, Iterator(start.successors)) ]
|
||||
time <- 1
|
||||
start.d, start.color <- time, gray
|
||||
|
||||
WHILE NOT Empty(path) DO
|
||||
lastVertex, successors <- path[-1]
|
||||
|
||||
IF NOT MoveNext(successors) THEN
|
||||
Pop(path)
|
||||
lastVertex.f, lastVertex.color <- ++time, black
|
||||
ELSE IF successors.Current.color = white THEN
|
||||
nextVertex <- successors.Current
|
||||
nextVertex.d, nextVertex.color <- ++time, gray
|
||||
path <- Append(path, (nextVertex, Iterator(nextVertex.successors)))
|
||||
FI
|
||||
OD
|
||||
END
|
||||
```
|
||||
|
||||
( The way we manipulate with the iterators is closest to the C# implementation. Apart from the `Iterator` thing :) In case you tried to implement it in C++, you would more than likely need to change the check, since you would get first successor right at the beginning )
|
||||
|
||||
So here we don't keep indices, but the iterators. We can also check existence of other successors easily: by the iterator moving after the last successor.
|
||||
|
||||
Closer explanation of the _iterator shenanigans_ follows. In the beginning, either `start` or when pushing new vertex, we are pushing an iterator that points _just before_ the first successor. When populating `lastVertex` and `successors` in the `while`-loop, we take the element from the top of the stack. `MoveNext` returns `true` if there is an element, i.e. successor in this case. If it returns `false` we have nothing to do and we pop the vertex from the stack (also set finishing time and color). If we have successor we check if it has been already visited or not. If has not, we set discovery time and color accordingly, also we add it to stack.
|
||||
|
||||
## Implementation
|
||||
|
||||
In case you want to play around with the code. At the beginning there is a link to the C# implementation that can be used. It has a basic representation of graph and includes BFS/DFS implementation in classes.
|
||||
|
||||
In `Program.cs` you can also find a method that returns graph we used on the seminar.
|
Loading…
Add table
Add a link
Reference in a new issue