Skip to content

Commit c400da3

Browse files
Merge pull request #88 from yasarcereen/yasarcereen/graph-lecture
Add graph lecture docs
2 parents 658bca1 + 4860d2b commit c400da3

11 files changed

+358
-0
lines changed

docs/graph/breadth-first-search.md

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
---
2+
title: Breadth First Search
3+
tags:
4+
- Graph
5+
- Breadth First Search
6+
- BFS
7+
---
8+
9+
Breadth First Search (BFS) is an algorithm for traversing or searching tree. (For example, you can find the shortest path from one node to another in an unweighted graph.)
10+
11+
<figure>
12+
![Breadth First Search Traversal](img/bfs.jpg)
13+
<figcaption>An example breadth first search traversal</figcaption>
14+
</figure>
15+
16+
## Method
17+
18+
BFS is a traversing algorithm where you should start traversing from a selected node (source or starting node) and traverse the graph layerwise thus exploring the neighbour nodes (nodes which are directly connected to source node). You must then move towards the next-level neighbour nodes. [1]
19+
20+
• As the name BFS suggests, you are required to traverse the graph breadthwise as follows:
21+
• First move horizontally and visit all the nodes of the current layer
22+
• Add to the queue neighbour nodes of current layer.
23+
• Move to the next layer, which are in the queue
24+
25+
Example question: Given a unweighted graph, a source and a destination, we need to find shortest path from source to destination in the graph in most optimal way?
26+
27+
```cpp
28+
#include <bits/stdc++.h>
29+
using namespace std;
30+
31+
cont int MaxN=100005; // Max number of nodes 5
32+
33+
vector <int> adj[MaxN];
34+
bool mark[MaxN];
35+
36+
void bfs(int starting_point,int ending_point) {
37+
memset(mark,0,sizeof(mark)); //clear the cache
38+
queue <pair <int,int> > q; // the value of node
39+
// , and length between this node and the starting node
40+
41+
q.push_back(make_pair(starting_point,0));
42+
mark[starting_point]=1;
43+
44+
while(q.empty()==false) {
45+
pair <int,int> tmp = q.front(); // get the next node
46+
q.pop(); // delete from q
47+
48+
if(ending_point==tmp.first) {
49+
printf("The length of path between %d - %d : %d\n",
50+
starting_point,ending_point,tmp.second);
51+
return;
52+
}
53+
54+
for (auto j : adj[tmp.first]) {
55+
if(mark[j]) continue ; // if it reached before
56+
mark[j]=1;
57+
q.push_back(make_pair(j,tmp.second+1)); // add next node to queue
58+
}
59+
}
60+
}
61+
62+
int main() {
63+
cin » n
64+
65+
for (int i=0 ; i < m; i++) {
66+
cin » a » b;
67+
adj[a].push_back(b);
68+
}
69+
70+
cin » start_point » end_point;
71+
bfs(start_point);
72+
return 0;
73+
}
74+
```
75+
76+
## Complexity
77+
78+
The time complexity of BFS is \(O(V + E)\), where \(V\) is the number of nodes and \(E\) is the number of edges.

docs/graph/depth-first-search.md

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
---
2+
title: Depth First Search
3+
tags:
4+
- Graph
5+
- Depth First Search
6+
- DFS
7+
---
8+
9+
Depth First Search (DFS) is an algorithm for traversing or searching tree. (For example, you can check if graph is connected or not via DFS) [2]
10+
11+
<figure markdown="span">
12+
![Depth First Search](img/dfs.jpg)
13+
<figcaption>Example of DFS traversal</figcaption>
14+
</figure>
15+
16+
## Method
17+
18+
The DFS algorithm is a recursive algorithm that uses the idea of backtracking. It involves exhaustive searches of all the nodes by going ahead, if possible, else by backtracking.
19+
20+
Here, the word backtrack means that when you are moving forward and there are no more nodes along the current path, you move backwards on the same path to find nodes to traverse. All the nodes will be visited on the current path till all the unvisited nodes have been traversed after which the next path will be selected. [3]
21+
22+
```cpp
23+
vector<vector<int» adj; // graph represented as an adjacency list
24+
int n; // number of vertices
25+
vector<bool> visited;
26+
void dfs(int v) {
27+
visited[v] = true;
28+
for (int u : adj[v]) {
29+
if (!visited[u]) dfs(u);
30+
}
31+
}
32+
```
33+
34+
This recursive nature of DFS can be implemented using stacks. The basic idea is as follows: Pick a starting node and push all its adjacent nodes into a stack. Pop a node from stack to select the next node to visit and push all its adjacent nodes into a stack. Repeat this process until the stack is empty. However, ensure that the nodes that are visited are marked. This will prevent you from visiting the same node more than once. If you do not mark the nodes that are visited and you visit the same node more than once, you may end up in an infinite loop. [3]
35+
36+
```cpp
37+
DFS-iterative(G, s): //Where G is graph and s is source vertex let S be stack
38+
S.push(s) //Inserting s in stack
39+
mark s as visited.
40+
while ( S is not empty):
41+
//Pop a vertex from stack to visit next v = S.top( )
42+
S.pop( )
43+
//Push all the neighbours of v in stack that are not visited
44+
for all neighbours w of v in Graph G:
45+
if w is not visited :
46+
S.push(w)
47+
mark w as visited
48+
```
49+
50+
Example Question: Given an undirected graph, find out whether the graph is strongly connected or not? An undirected graph is strongly connected if there is a path between any two pair of vertices.
51+
52+
53+
```cpp
54+
#include <bits/stdc++.h>
55+
using namespace std;
56+
57+
cont int MaxN=100005; // Max number of nodes
58+
59+
vector <int> adj[MaxN];
60+
bool mark[MaxN];
61+
62+
void dfs(int k) {
63+
mark[k]=1; // visited
64+
for(auto j : adj[k]) // iterate over adjacent nodes
65+
if(mark[j]==false) // check if it is visited or not
66+
dfs(j); // do these operation for that node
67+
}
68+
69+
int main() {
70+
cin » n » m; // number of nodes , number of edges
71+
for (int i=0 ; i < m; i++){
72+
cin » a » b;
73+
adj[a].push_back(b);
74+
adj[b].push_back(a);
75+
}
76+
77+
dfs(1);
78+
79+
bool connected=1;
80+
for(int i=1 ; i <= n ;i++)
81+
if(mark[i]==0) {
82+
connected=0;
83+
break;
84+
}
85+
86+
if(connected)
87+
cout « "Graph is connected" « endl;
88+
else
89+
cout « "Graph is not connected" « endl;
90+
91+
return 0;
92+
}
93+
```
94+
95+
## Complexity
96+
97+
The time complexity of DFS is \(O(V+E)\) when implemented using an adjacency list ( with Adjacency Matrices it is \(O(V^2)\)), where \(V\) is the number of nodes and \(E\) is the number of edges. [4]

docs/graph/img/bfs.jpg

23.9 KB
Loading

docs/graph/img/dfs.jpg

42.6 KB
Loading
27.6 KB
Loading

docs/graph/img/shortest.png

37.9 KB
Loading

docs/graph/img/toporder.png

13 KB
Loading

docs/graph/index.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
---
2+
title: Graph
3+
---
4+
5+
**Editor:** Kayacan Vesek
6+
7+
**Reviewers:** Yasin Kaya
8+
9+
## Introduction
10+
11+
A graph is a structure amounting to a set of objects in which some pairs of the objects are in some sense "related". The objects correspond to the mathematical abstractions called vertices (also called nodes or points) and each of the related pairs of vertices is called an edge. Typically, a graph is depicted in diagrammatic form as a set of dots for the vertices, joined by lines for the edges. [8]
12+
13+
Why graphs? Graphs are usually used to represent different elements that are somehow related to each other.
14+
15+
A Graph consists of a finite set of vertices(or nodes) and set of edges which connect a pair of nodes. G = (V,E)
16+
17+
V = set of nodes
18+
19+
E = set of edges(e) represented as e = a,b
20+
21+
Graph are used to show a relation between objects. So, some graphs may have directional edges (e.g. people and their love relationships that are not mutual: Alice may love Alex, while Alex is not in love with her and so on), and some graphs may have weighted edges (e.g. people and their relationship in the instance of a debt)
22+
23+
<figure markdown="span">
24+
![Directed Acyclic Graph](img/directed_acyclic_graph.png)
25+
<figcaption>Figure 1: a simple unweigted graph</figcaption>
26+
</figure>
27+
28+
### [Depth First Search](depth-first-search.md)
29+
### [Breadth First Search](breadth-first-search.md)
30+
### [Shortest Path](shortest-path.md)
31+
### [Topological Sort](topological-sort.md)
32+
33+
## References
34+
35+
1. [https://www.hackerearth.com/practice/algorithms/graphs/breadth-first-search/tutorial/](https://www.hackerearth.com/practice/algorithms/graphs/breadth-first-search/tutorial/)
36+
2. [https://www.geeksforgeeks.org/depth-first-search-or-dfs-for-a-graph/](https://www.geeksforgeeks.org/depth-first-search-or-dfs-for-a-graph/)
37+
3. [https://cp-algorithms.com/graph/depth-first-search.html](https://cp-algorithms.com/graph/depth-first-search.html)
38+
4. [https://www.hackerearth.com/practice/algorithms/graphs/depth-first-search/tutorial/](https://www.hackerearth.com/practice/algorithms/graphs/depth-first-search/tutorial/)
39+
5. [Shortest Path. Wikipedia, the free online encyclopedia. Retrieved January 5, 2019](https://www.wikiwand.com/en/articles/Shortest_path_problem)
40+
6. [Topological sort. Geeksforgeeks website. Retrieved January 5, 2019](https://www.geeksforgeeks.org/topological-sorting/)
41+
7. [Topological Sort. Wikipedia, the free online encyclopedia. Retrieved January 5, 2019](https://en.wikipedia.org/wiki/Topological_sorting)
42+
8. [https://en.wikipedia.org/wiki/Graph_theory](https://en.wikipedia.org/wiki/Graph_theory)

docs/graph/shortest-path.md

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
---
2+
title: Shortest Path Problem
3+
tags:
4+
- Graph
5+
- Shortest Path Problem
6+
- Dijkstra
7+
---
8+
9+
## Definition
10+
11+
Let \(G(V,E)\) be a graph, \(v_i\) and \(v_j\) be two nodes of \(G\). We say a path between \(v_i\) and \(v_j\) is the shortest path if sum of the edge weights (cost) in the path is minimum. In other words, the shortest path problem is the problem of finding a path between two vertices (or nodes) in a graph such that the sum of the weights of its constituent edges is minimized. [5]
12+
13+
<figure>
14+
![Shortest Path](img/shortest.png)
15+
<figcaption>Example shortest path in graph. Source is A and target is F. Image taken from [5].</figcaption>
16+
</figure>
17+
18+
We will cover several shortest path algorithms in this bundle. One of them is Dijkstra’s Shortest Path Algorithm but it has some drawbacks: Edge weights should be non-negative for the optimally of the algorithm. We will discover other algorithms in which these condition isn’t necessary, like Floyd-Warshall and Bellman-Ford algorithms.
19+
20+
## Dijkstra's Shortest Path Algorithm
21+
22+
Dijkstra’s Shortest Path algorithm is straight forward. In brief we have a set \(S\) that contains explored nodes and \(d\) which contains the shortest path cost from source to another node. In other words, \(d(u)\) represents the shortest path cost from source to node \(u\). The procedure follows as that. First, add source node to set \(S\) which represents the explored nodes and assigns the minimum cost of the source to zero. Then each iteration we add node to \(S\) that has lowest cost \((d(u))\) from unexplored nodes. Let’s say \(S′ = V − S\) which means unexplored nodes. For all nodes in \(S′\) we calculate \(d(x)\) for each node \(x\) is \(S′\) then we pick minimum cost node and add it to \(S\). So how we calculate \(d(x)\)? For any \(x\) node from \(S′\), \(d(x)\) calculated as that, let’s say \(e\) cost of any edge from \(S\) to \(x\) then \(d(x) = min(d(u) + e)\). It is a greedy algorithm.
23+
24+
Here is the explanation of the algorithm step by step.
25+
26+
1. Initialize an empty set, distance array, insert source to set.
27+
28+
2. Initialize a min-heap, put source to heap with key is zero.
29+
30+
3. While heap is not empty, take the top element from heap and add its neighbours to min-heap.
31+
32+
4. Once we pick an element from the heap, it is guaranteed that the same node will never be added to heap with lower key value.
33+
34+
In implementation we can use priority queue data structure in order to increase efficiency. If we put unexplored nodes to min - priority queue where the distance is key, we can take the lowest cost unexplored node in \(O(log(n))\) time which is efficient.
35+
36+
```cpp
37+
typedef pair<int,int> edge;
38+
typedef vector<edge> adjList;
39+
typedef vector<adjList> graph;
40+
41+
void dijkstra(graph &g, int s) {
42+
vector<int> dist(g.size(),INT_MAX/2);
43+
vector<bool> visited(g.size(),false);
44+
45+
dist[s] = 0;
46+
47+
priority_queue<edge, vector<edge>, greater<edge>> q;
48+
q.push({0, s});
49+
50+
while(!q.empty()) {
51+
int v = q.top().second;
52+
int d = q.top().first;
53+
q.pop();
54+
55+
if(visited[v]) continue;
56+
visited[v] = true;
57+
58+
for(auto it: g[v]) {
59+
int u = it.first;
60+
int w = it.second;
61+
if(dist[v] + w < dist[u]) {
62+
dist[u] = dist[v] + w;
63+
q.push({dist[u], u});
64+
}
65+
}
66+
}
67+
}
68+
```

docs/graph/topological-sort.md

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
---
2+
title: Topological Sort
3+
tags:
4+
- Graph
5+
- Topological Sort
6+
---
7+
8+
## Definition
9+
10+
11+
Topological sorting for Directed Acyclic Graph (DAG) is a linear ordering of vertices such that for every directed edge u->v, vertex u comes before v in the ordering. Topological Sorting for a graph is not possible if the graph is not a DAG [6].
12+
13+
There are many important usages of topological sorting in computer science; applications of this type arise in instruction scheduling, ordering of formula cell evaluation when recomputing formula values in spreadsheets, logic synthesis, determining the order of compilation tasks to perform in makefiles, data serialization, and resolving symbol dependencies in linkers. It is also used to decide in which order to load tables with foreign keys in databases [7].
14+
15+
There are known algorithms (e.g Kahn’s algorithm) to find topological order in linear time. Below, you can find one of the implementations:
16+
17+
<figure>
18+
![Topological Order](img/toporder.png)
19+
<figcaption>For example, a topological sorting of this graph is “5 4 2 3 1 0”. There can be more than one topological sorting for a graph. For example, another topological sorting of the following graph is “4 5 2 3 1 0”. The first vertex in topological sorting is always a vertex with in-degree as 0 (a vertex with no incoming edges)[6].</figcaption>
20+
</figure>
21+
22+
## Algorithm
23+
24+
```cpp
25+
typedef vector<int> adjList;
26+
typedef vector<adjList> graph;
27+
typedef pair<int,int> ii;
28+
29+
void kahn(graph &g) {
30+
vector<int> result;
31+
queue<int> q;
32+
vector<int> degree(g.size(),0); // number of incoming egdes.
33+
for(auto &list: g){
34+
for(auto &node:list) {
35+
degree[node]++;
36+
}
37+
}
38+
39+
for(int i=0; i < g.size(); ++i) {
40+
if (degree[i] == 0)
41+
q.push(i);
42+
}
43+
44+
while( !q.empty()) {
45+
int node = q.front();
46+
result.push_back(node);
47+
q.pop();
48+
49+
for (auto &ng: g[node]) {
50+
degree[ng]--;
51+
if (degree[ng] == 0)
52+
q.push(ng);
53+
}
54+
}
55+
56+
for(auto &i:result)
57+
cout << i << " ";
58+
cout << endl;
59+
}
60+
int main(){
61+
graph g(6);
62+
g[1].push_back(0);
63+
g[1].push_back(2);
64+
g[2].push_back(3);
65+
g[3].push_back(4);
66+
g[4].push_back(5);
67+
kahn(g);
68+
return 0;
69+
}
70+
```
71+
72+
As for time complexity: we traverse all edges in the beginning (calculating degrees) and in the while segment we remove edges (once for an edge) and traverse all nodes. Hence, the time complexity of this algorithm is \(O(V +E)\). Note that this implementation assumes the graph is DAG. Try improving this code to support checking if the graph is DAG!

mkdocs.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ nav:
55
- Introduction: introduction/index.md
66
- Data Structures: data-structures/index.md
77
- Algorithms: algorithms/index.md
8+
- Graph: graph/index.md
89
- Dynamic Programming: dynamic-programming/index.md
910
theme:
1011
name: material

0 commit comments

Comments
 (0)