Skip to content

Commit c10bb8a

Browse files
authored
Merge pull request animator#427 from Eshparsi/main
Added graph.md file
2 parents 3319bc1 + 5643f2c commit c10bb8a

File tree

2 files changed

+220
-0
lines changed

2 files changed

+220
-0
lines changed

contrib/ds-algorithms/graph.md

+219
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
# Graph Data Stucture
2+
3+
Graph is a non-linear data structure consisting of vertices and edges. It is a powerful tool for representing and analyzing complex relationships between objects or entities.
4+
5+
## Components of a Graph
6+
7+
1. **Vertices:** Vertices are the fundamental units of the graph. Sometimes, vertices are also known as vertex or nodes. Every node/vertex can be labeled or unlabeled.
8+
9+
2. **Edges:** Edges are drawn or used to connect two nodes of the graph. It can be ordered pair of nodes in a directed graph. Edges can connect any two nodes in any possible way. There are no rules. very edge can be labelled/unlabelled.
10+
11+
## Basic Operations on Graphs
12+
- Insertion of Nodes/Edges in the graph
13+
- Deletion of Nodes/Edges in the graph
14+
- Searching on Graphs
15+
- Traversal of Graphs
16+
17+
## Types of Graph
18+
19+
20+
**1. Undirected Graph:** In an undirected graph, edges have no direction, and they represent symmetric relationships between nodes. If there is an edge between node A and node B, you can travel from A to B and from B to A.
21+
22+
**2. Directed Graph (Digraph):** In a directed graph, edges have a direction, indicating a one-way relationship between nodes. If there is an edge from node A to node B, you can travel from A to B but not necessarily from B to A.
23+
24+
**3. Weighted Graph:** In a weighted graph, edges have associated weights or costs. These weights can represent various attributes such as distance, cost, or capacity. Weighted graphs are commonly used in applications like route planning or network optimization.
25+
26+
**4. Cyclic Graph:** A cyclic graph contains at least one cycle, which is a path that starts and ends at the same node. In other words, you can traverse the graph and return to a previously visited node by following the edges.
27+
28+
**5. Acyclic Graph:** An acyclic graph, as the name suggests, does not contain any cycles. This type of graph is often used in scenarios where a cycle would be nonsensical or undesirable, such as representing dependencies between tasks or events.
29+
30+
**6. Tree:** A tree is a special type of acyclic graph where each node has a unique parent except for the root node, which has no parent. Trees have a hierarchical structure and are frequently used in data structures like binary trees or decision trees.
31+
32+
## Representation of Graphs
33+
There are two ways to store a graph:
34+
35+
1. **Adjacency Matrix:**
36+
In this method, the graph is stored in the form of the 2D matrix where rows and columns denote vertices. Each entry in the matrix represents the weight of the edge between those vertices.
37+
38+
```python
39+
def create_adjacency_matrix(graph):
40+
num_vertices = len(graph)
41+
42+
adj_matrix = [[0] * num_vertices for _ in range(num_vertices)]
43+
44+
for i in range(num_vertices):
45+
for j in range(num_vertices):
46+
if graph[i][j] == 1:
47+
adj_matrix[i][j] = 1
48+
adj_matrix[j][i] = 1
49+
50+
return adj_matrix
51+
52+
53+
graph = [
54+
[0, 1, 0, 0],
55+
[1, 0, 1, 0],
56+
[0, 1, 0, 1],
57+
[0, 0, 1, 0]
58+
]
59+
60+
adj_matrix = create_adjacency_matrix(graph)
61+
62+
for row in adj_matrix:
63+
print(' '.join(map(str, row)))
64+
65+
```
66+
67+
2. **Adjacency List:**
68+
In this method, the graph is represented as a collection of linked lists. There is an array of pointer which points to the edges connected to that vertex.
69+
70+
```python
71+
def create_adjacency_list(edges, num_vertices):
72+
adj_list = [[] for _ in range(num_vertices)]
73+
74+
for u, v in edges:
75+
adj_list[u].append(v)
76+
adj_list[v].append(u)
77+
78+
return adj_list
79+
80+
if __name__ == "__main__":
81+
num_vertices = 4
82+
edges = [(0, 1), (0, 2), (1, 2), (2, 3), (3, 1)]
83+
84+
adj_list = create_adjacency_list(edges, num_vertices)
85+
86+
for i in range(num_vertices):
87+
print(f"{i} -> {' '.join(map(str, adj_list[i]))}")
88+
```
89+
`Output`
90+
`0 -> 1 2`
91+
`1 -> 0 2 3`
92+
`2 -> 0 1 3`
93+
`3 -> 2 1 `
94+
95+
96+
97+
# Traversal Techniques
98+
99+
## Breadth First Search (BFS)
100+
- It is a graph traversal algorithm that explores all the vertices in a graph at the current depth before moving on to the vertices at the next depth level.
101+
- It starts at a specified vertex and visits all its neighbors before moving on to the next level of neighbors.
102+
BFS is commonly used in algorithms for pathfinding, connected components, and shortest path problems in graphs.
103+
104+
**Steps of BFS algorithms**
105+
106+
107+
- **Step 1:** Initially queue and visited arrays are empty.
108+
- **Step 2:** Push node 0 into queue and mark it visited.
109+
- **Step 3:** Remove node 0 from the front of queue and visit the unvisited neighbours and push them into queue.
110+
- **Step 4:** Remove node 1 from the front of queue and visit the unvisited neighbours and push them into queue.
111+
- **Step 5:** Remove node 2 from the front of queue and visit the unvisited neighbours and push them into queue.
112+
- **Step 6:** Remove node 3 from the front of queue and visit the unvisited neighbours and push them into queue.
113+
- **Step 7:** Remove node 4 from the front of queue and visit the unvisited neighbours and push them into queue.
114+
115+
```python
116+
117+
from collections import deque
118+
119+
def bfs(adjList, startNode, visited):
120+
q = deque()
121+
122+
visited[startNode] = True
123+
q.append(startNode)
124+
125+
while q:
126+
currentNode = q.popleft()
127+
print(currentNode, end=" ")
128+
129+
for neighbor in adjList[currentNode]:
130+
if not visited[neighbor]:
131+
visited[neighbor] = True
132+
q.append(neighbor)
133+
134+
def addEdge(adjList, u, v):
135+
adjList[u].append(v)
136+
137+
def main():
138+
vertices = 5
139+
140+
adjList = [[] for _ in range(vertices)]
141+
142+
addEdge(adjList, 0, 1)
143+
addEdge(adjList, 0, 2)
144+
addEdge(adjList, 1, 3)
145+
addEdge(adjList, 1, 4)
146+
addEdge(adjList, 2, 4)
147+
148+
visited = [False] * vertices
149+
150+
print("Breadth First Traversal", end=" ")
151+
bfs(adjList, 0, visited)
152+
153+
if __name__ == "__main__": #Output : Breadth First Traversal 0 1 2 3 4
154+
main()
155+
156+
```
157+
158+
- **Time Complexity:** `O(V+E)`, where V is the number of nodes and E is the number of edges.
159+
- **Auxiliary Space:** `O(V)`
160+
161+
162+
## Depth-first search
163+
164+
Depth-first search is an algorithm for traversing or searching tree or graph data structures. The algorithm starts at the root node (selecting some arbitrary node as the root node in the case of a graph) and explores as far as possible along each branch before backtracking.
165+
166+
**Steps of DFS algorithms**
167+
168+
- **Step 1:** Initially stack and visited arrays are empty.
169+
- **Step 2:** Visit 0 and put its adjacent nodes which are not visited yet into the stack.
170+
- **Step 3:** Now, Node 1 at the top of the stack, so visit node 1 and pop it from the stack and put all of its adjacent nodes which are not visited in the stack.
171+
- **Step 4:** Now, Node 2 at the top of the stack, so visit node 2 and pop it from the stack and put all of its adjacent nodes which are not visited (i.e, 3, 4) in the stack.
172+
- **Step 5:** Now, Node 4 at the top of the stack, so visit node 4 and pop it from the stack and put all of its adjacent nodes which are not visited in the stack.
173+
- **Step 6:** Now, Node 3 at the top of the stack, so visit node 3 and pop it from the stack and put all of its adjacent nodes which are not visited in the stack.
174+
175+
176+
177+
```python
178+
from collections import defaultdict
179+
180+
class Graph:
181+
182+
def __init__(self):
183+
self.graph = defaultdict(list)
184+
185+
def addEdge(self, u, v):
186+
self.graph[u].append(v)
187+
188+
def DFSUtil(self, v, visited):
189+
visited.add(v)
190+
print(v, end=' ')
191+
192+
for neighbour in self.graph[v]:
193+
if neighbour not in visited:
194+
self.DFSUtil(neighbour, visited)
195+
196+
def DFS(self, v):
197+
visited = set()
198+
self.DFSUtil(v, visited)
199+
200+
if __name__ == "__main__":
201+
g = Graph()
202+
g.addEdge(0, 1)
203+
g.addEdge(0, 2)
204+
g.addEdge(1, 2)
205+
g.addEdge(2, 0)
206+
g.addEdge(2, 3)
207+
g.addEdge(3, 3)
208+
209+
print("Depth First Traversal (starting from vertex 2): ",g.DFS(2))
210+
211+
```
212+
`Output: Depth First Traversal (starting from vertex 2): 2 0 1 3 `
213+
214+
- **Time complexity:** `O(V + E)`, where V is the number of vertices and E is the number of edges in the graph.
215+
- **Auxiliary Space:** `O(V + E)`, since an extra visited array of size V is required, And stack size for iterative call to DFS function.
216+
217+
</br>
218+
219+

contrib/ds-algorithms/index.md

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# List of sections
22

33
- [Queues in Python](Queues.md)
4+
- [Graphs](graph.md)
45
- [Sorting Algorithms](sorting-algorithms.md)
56
- [Recursion and Backtracking](recursion.md)
67
- [Divide and Conquer Algorithm](divide-and-conquer-algorithm.md)

0 commit comments

Comments
 (0)