└── README.md /README.md: -------------------------------------------------------------------------------- 1 | # 50 Essential Graph Data Structure Interview Questions in 2025 2 | 3 |
4 |

5 | 6 | data-structures-and-algorithms 7 | 8 |

9 | 10 | #### You can also find all 50 answers here 👉 [Devinterview.io - Graph Data Structure](https://devinterview.io/questions/data-structures-and-algorithms/graph-data-structure-interview-questions) 11 | 12 |
13 | 14 | ## 1. What is a _Graph_? 15 | 16 | A **graph** is a data structure that represents a collection of interconnected **nodes** through a set of **edges**. 17 | 18 | This abstract structure is highly versatile and finds applications in various domains, from social network analysis to computer networking. 19 | 20 | ### Core Components 21 | 22 | A graph consists of two main components: 23 | 24 | 1. **Nodes**: Also called **vertices**, these are the fundamental units that hold data. 25 | 2. **Edges**: These are the connections between nodes, and they can be either **directed** or **undirected**. 26 | 27 | ### Visual Representation 28 | 29 | ![Graph: Unidirected, Directed, Cyclic, Acyclic, Weighted, Unweighted, Sparse, Dense](https://firebasestorage.googleapis.com/v0/b/dev-stack-app.appspot.com/o/graph-theory%2Fwhat-is-graph.png?alt=media&token=0d7b76b3-23d6-4a72-99d8-1b84565274b4) 30 | 31 | ### Graph Representations 32 | 33 | There are several ways to represent graphs in computer memory, with the most common ones being **adjacency matrix**, **adjacency list**, and **edge list**. 34 | 35 | #### Adjacency Matrix 36 | 37 | In an adjacency matrix, a 2D Boolean array indicates the edges between nodes. A value of `True` at index `[i][j]` means that an edge exists between nodes `i` and `j`. 38 | 39 | Here is the Python code: 40 | 41 | ```python 42 | graph = [ 43 | [False, True, True], 44 | [True, False, True], 45 | [True, True, False] 46 | ] 47 | ``` 48 | 49 | #### Adjacency List 50 | 51 | An adjacency list represents each node as a list, and the indices of the list are the nodes. Each node's list contains the nodes that it is directly connected to. 52 | 53 | Here is the Python code: 54 | 55 | ```python 56 | graph = { 57 | 0: [1, 2], 58 | 1: [0, 2], 59 | 2: [0, 1] 60 | } 61 | ``` 62 | 63 | #### Edge List 64 | 65 | An edge list is a simple list of tuples, where each tuple represents an edge between two nodes. 66 | 67 | Here is the Python code: 68 | 69 | ```python 70 | graph = [(0, 1), (0, 2), (1, 2)] 71 | ``` 72 |
73 | 74 | ## 2. What are some common _Types_ and _Categories_ of _Graphs_? 75 | 76 | Graphs serve as **adaptable data structures** for various computational tasks and real-world applications. Let's look at their diverse types. 77 | 78 | ### Types of Graphs 79 | 80 | 1. **Undirected**: Edges lack direction, allowing free traversal between connected nodes. Mathematically, $(u,v)$ as an edge implies $(v,u)$ as well. 81 | 2. **Directed (Digraph)**: Edges have a set direction, restricting traversal accordingly. An edge $(u,v)$ doesn't guarantee $(v,u)$. 82 | 83 | ![Graph Types: Unidirected, Directed](https://firebasestorage.googleapis.com/v0/b/dev-stack-app.appspot.com/o/graph-theory%2Fdirection.png?alt=media&token=187d88ba-04f3-4993-bbcf-f3bc35c41c0d) 84 | 85 | #### Weight Considerations 86 | 87 | 1. **Weighted**: Each edge has a numerical "weight" or "cost." 88 | 2. **Unweighted**: All edges are equal in weight, typically considered as 1. 89 | 90 | ![Graph Types: Weighted, Unweighted](https://firebasestorage.googleapis.com/v0/b/dev-stack-app.appspot.com/o/graph-theory%2Fweight.png?alt=media&token=c975e0b9-4c21-4518-8c7e-9489279b642b) 91 | 92 | #### Presence of Cycles 93 | 94 | 1. **Cyclic**: Contains at least one cycle or closed path. 95 | 2. **Acyclic**: Lacks cycles entirely. 96 | 97 | ![Graph Types: Cyclic, Acyclic](https://firebasestorage.googleapis.com/v0/b/dev-stack-app.appspot.com/o/graph-theory%2Fcyclic.png?alt=media&token=f072557c-d298-4c73-b5b8-11ca014eeb08) 98 | 99 | #### Edge Density 100 | 101 | 1. **Dense**: High edge-to-vertex ratio, nearing the maximum possible connections. 102 | 2. **Sparse**: Low edge-to-vertex ratio, closer to the minimum. 103 | 104 | ![Graph Types: Sparse, Dense](https://firebasestorage.googleapis.com/v0/b/dev-stack-app.appspot.com/o/graph-theory%2Fdensity.png?alt=media&token=084eec75-b348-4429-ae30-788ac7f49778) 105 | 106 | #### Connectivity 107 | 108 | 1. **Connected**: Every vertex is reachable from any other vertex. 109 | 2. **Disconnected**: Some vertices are unreachable from others. 110 | 111 | ![Graph Types: Connected, Disconnected](https://firebasestorage.googleapis.com/v0/b/dev-stack-app.appspot.com/o/graph-theory%2Fconnected-disconnected-graph.png?alt=media&token=3aa6db3e-852e-4c62-a42d-a6ebf501d9f7) 112 | 113 | #### Edge Uniqueness 114 | 115 | 1. **Multigraph**: Allows duplicate edges between vertices. 116 | 2. **Simple**: Limits vertices to a single connecting edge. 117 | 118 | ![Graph Types: Multigraph, Simple Graph](https://firebasestorage.googleapis.com/v0/b/dev-stack-app.appspot.com/o/graph-theory%2Fsimple-multigraph.png?alt=media&token=a7c888e1-a317-470c-94b8-bf998880bdd7) 119 |
120 | 121 | ## 3. What is the difference between a _Tree_ and a _Graph_? 122 | 123 | **Graphs** and **trees** are both nonlinear data structures, but there are fundamental distinctions between them. 124 | 125 | ### Key Distinctions 126 | 127 | - **Uniqueness**: Trees have a single root, while graphs may not have such a concept. 128 | - **Topology**: Trees are **hierarchical**, while graphs can exhibit various structures. 129 | - **Focus**: Graphs center on relationships between individual nodes, whereas trees emphasize the relationship between nodes and a common root. 130 | 131 | ### Graphs: Versatile and Unstructured 132 | 133 | - **Elements**: Composed of vertices/nodes (denoted as V) and edges (E) representing relationships. Multiple edges and **loops** are possible. 134 | - **Directionality**: Edges can be directed or undirected. 135 | - **Connectivity**: May be **disconnected**, with sets of vertices that aren't reachable from others. 136 | - **Loops**: Can contain cycles. 137 | 138 | ### Trees: Hierarchical and Organized 139 | 140 | - **Elements**: Consist of nodes with parent-child relationships. 141 | - **Directionality**: Edges are strictly parent-to-child. 142 | - **Connectivity**: Every node is accessible from the unique root node. 143 | - **Loops**: Cycles are not allowed. 144 | 145 | ### Visual Representation 146 | 147 | ![Graph vs Tree](https://firebasestorage.googleapis.com/v0/b/dev-stack-app.appspot.com/o/graph-theory%2Ftree-graph.jpg?alt=media&token=0362c5d3-e851-4cd2-bbb4-c632e77ccede&_gl=1*euedhq*_ga*OTYzMjY5NTkwLjE2ODg4NDM4Njg.*_ga_CW55HF8NVT*MTY5NzI4NzY1Ny4xNTUuMS4xNjk3Mjg5NjU2LjYwLjAuMA..) 148 |
149 | 150 | ## 4. How can you determine the _Minimum number of edges_ for a graph to remain connected? 151 | 152 | To ensure a graph remains **connected**, it must have a minimum number of edges determined by the number of vertices. This is known as the **edge connectivity** of the graph. 153 | 154 | ### Edge Connectivity Formula 155 | 156 | The minimum number of edges required for a graph to remain connected is given by: 157 | 158 | $$ 159 | \text{{Edge Connectivity}} = \max(\delta(G),1) 160 | $$ 161 | 162 | Where: 163 | - $\delta(G)$ is the minimum degree of a vertex in $G$. 164 | - The maximum function ensures that the graph remains connected even if all vertices have a degree of 1 or 0. 165 | 166 | For example, a graph with a minimum vertex degree of 3 or more requires at least 3 edges to stay connected. 167 |
168 | 169 | ## 5. Define _Euler Path_ and _Euler Circuit_ in the context of graph theory. 170 | 171 | In **graph theory**, an **Euler Path** and an **Euler Circuit** serve as methods to visit all edges (links) exactly once, with the distinction that an Euler Circuit also visits all vertices once. 172 | 173 | ### Euler Path and Euler Circuit Definitions 174 | 175 | A graph has an **Euler Path** if it contains exactly two vertices of odd degree. 176 | 177 | A graph has an **Euler Circuit** if every vertex has even degree. 178 | 179 | **Degree** specifies the number of edges adjacent to a vertex. 180 | 181 | ### Key Concepts 182 | 183 | - **Starting Vertex**: In an Euler Path, the unique starting and ending vertices are the two with odd degrees. 184 | - **Reachability**: In both Euler Path and Circuit, every edge must be reachable from the starting vertex. 185 | - **Direction-Consistency**: While an Euler Path is directionally open-ended, an Euler Circuit is directionally closed. 186 | 187 | ### Visual Representation: Euler Path and Circuit 188 | 189 | ![Euler Path and Euler Circuit](https://firebasestorage.googleapis.com/v0/b/dev-stack-app.appspot.com/o/data%20structures%2Feulerian-path-and-circuit-.webp?alt=media&token=6b2ced12-db4a-435a-9943-ece237d9ef8c) 190 |
191 | 192 | ## 6. Compare _Adjacency Lists_ and _Adjacency Matrices_ for graph representation. 193 | 194 | Graphs can be represented in various ways, but **Adjacency Matrix** and **Adjacency List** are the most commonly used data structures. Each method offers distinct advantages and trade-offs, which we'll explore below. 195 | 196 | ![Example Graph](https://static.javatpoint.com/tutorial/graph-theory/images/graph-representations1.png) 197 | 198 | ### Space Complexity 199 | 200 | - **Adjacency Matrix**: Requires a $N \times N$ matrix, resulting in $O(N^2)$ space complexity. 201 | - **Adjacency List**: Utilizes a list or array for each node's neighbors, leading to $O(N + E)$ space complexity, where $E$ is the number of edges. 202 | 203 | ### Time Complexity for Edge Look-Up 204 | 205 | - **Adjacency Matrix**: Constant time, $O(1)$, as the presence of an edge is directly accessible. 206 | - **Adjacency List**: Up to $O(k)$, where $k$ is the degree of the vertex, as the list of neighbors may need to be traversed. 207 | 208 | ### Time Complexity for Traversal 209 | 210 | - **Adjacency Matrix**: Requires $O(N^2)$ time to iterate through all potential edges. 211 | - **Adjacency List**: Takes $O(N + E)$ time, often faster for sparse graphs. 212 | 213 | ### Time Complexity for Edge Manipulation 214 | 215 | - **Adjacency Matrix**: $O(1)$ for both addition and removal, as it involves updating a single cell. 216 | - **Adjacency List**: $O(k)$ for addition or removal, where $k$ is the degree of the vertex involved. 217 | 218 | ### Time Complexity for Vertex Manipulation 219 | 220 | - **Adjacency Matrix**: $O(N^2)$ as resizing the matrix is needed. 221 | - **Adjacency List**: $O(1)$ as it involves updating a list or array. 222 | 223 | ### Code Example: Adjacency Matrix & Adjacency List 224 | 225 | Here is the Python code: 226 | 227 | ```python 228 | adj_matrix = [ 229 | [0, 1, 1, 0, 0, 0], 230 | [1, 0, 0, 1, 0, 0], 231 | [1, 0, 0, 0, 0, 1], 232 | [0, 1, 0, 0, 1, 1], 233 | [0, 0, 0, 1, 0, 0], 234 | [0, 0, 1, 1, 0, 0] 235 | ] 236 | 237 | adj_list = [ 238 | [1, 2], 239 | [0, 3], 240 | [0, 5], 241 | [1, 4, 5], 242 | [3], 243 | [2, 3] 244 | ] 245 | ``` 246 |
247 | 248 | ## 7. What is an _Incidence Matrix_, and when would you use it? 249 | 250 | An **incidence matrix** is a binary graph representation that maps vertices to edges. It's especially useful for **directed** and **multigraphs**. The matrix contains $0$s and $1$s, with positions corresponding to "vertex connected to edge" relationships. 251 | 252 | ### Matrix Structure 253 | 254 | - **Columns**: Represent edges 255 | - **Rows**: Represent vertices 256 | - **Cells**: Indicate whether a vertex is connected to an edge 257 | 258 | Each unique **row-edge pair** depicts an incidence of a vertex in an edge, relating to the graph's structure differently based on the graph type. 259 | 260 | ### Example: Incidence Matrix for a Directed Graph 261 | 262 | ![Directed Graph](https://firebasestorage.googleapis.com/v0/b/dev-stack-app.appspot.com/o/graph-theory%2Fdirected-graph1.png?alt=media&token=aa5733dc-b79a-4a5f-8f4d-127e1c7b5130) 263 | 264 | ### Example: Incidence Matrix for an Undirected Multigraph 265 | 266 | ![Uniderected Graph](https://firebasestorage.googleapis.com/v0/b/dev-stack-app.appspot.com/o/graph-theory%2Fundirected-graph1.png?alt=media&token=224b6d3a-a2ab-432d-8691-a83483b88cc8) 267 | 268 | ### Applications of Incidence Matrices 269 | 270 | - **Algorithm Efficiency**: Certain matrix operations can be faster than graph traversals. 271 | - **Graph Comparisons**: It enables direct graph-to-matrix or matrix-to-matrix comparisons. 272 | - **Database Storage**: A way to represent graphs in databases amongst others. 273 | - **Graph Transformations**: Useful in transformations like line graphs and dual graphs. 274 |
275 | 276 | ## 8. Discuss _Edge List_ as a graph representation and its use cases. 277 | 278 | **Edge list** is a straightforward way to represent graphs. It's apt for dense graphs and offers a quick way to query edge information. 279 | 280 | ### Key Concepts 281 | 282 | - **Edge Storage**: The list contains tuples (a, b) to denote an edge between nodes $a$ and $b$. 283 | - **Edge Direction**: The edges can be directed or undirected. 284 | - **Edge Duplicates**: Multiple occurrences signal multigraph. Absence ensures simple graph. 285 | 286 | ### Visual Example 287 | 288 | ![Edge List Example](https://firebasestorage.googleapis.com/v0/b/dev-stack-app.appspot.com/o/graph-theory%2Fedge-list.png?alt=media&token=d1bbe33f-c145-4c40-aff3-a6c4f1e80554) 289 | 290 | ### Code Example: Edge List 291 | 292 | Here is the Python 3 code: 293 | 294 | ```python 295 | # Example graph 296 | edges = {('A', 'B'), ('A', 'C'), ('B', 'C'), ('C', 'D'), ('B', 'D'), ('D', 'E')} 297 | 298 | # Check existence 299 | print(('A', 'B') in edges) # True 300 | print(('B', 'A') in edges) # False 301 | print(('A', 'E') in edges) # False 302 | 303 | # Adding an edge 304 | edges.add(('E', 'C')) 305 | 306 | # Removing an edge 307 | edges.remove(('D', 'E')) 308 | 309 | print(edges) # Updated set: {('A', 'C'), ('B', 'D'), ('C', 'D'), ('A', 'B'), ('E', 'C'), ('B', 'C')} 310 | ``` 311 |
312 | 313 | ## 9. Explain how to save space while storing a graph using _Compressed Sparse Row_ (CSR). 314 | 315 | In **Compressed Sparse Row** format, the graph is represented by three linked arrays. This streamlined approach can significantly reduce memory use and is especially beneficial for **sparse graphs**. 316 | 317 | Let's go through the data structures and the detailed process. 318 | 319 | ### Data Structures 320 | 321 | 1. **Indptr Array (IA)**: A list of indices where each row starts in the `adj_indices` array. It's of length `n_vertices + 1`. 322 | 2. **Adjacency Index Array (AA)**: The column indices for each edge based on their position in the `indptr` array. 323 | 3. **Edge Data**: The actual edge data. This array's length matches the number of non-zero elements. 324 | 325 | 326 | ### Visual Representation 327 | 328 | ![CSR Graph Representation](https://firebasestorage.googleapis.com/v0/b/dev-stack-app.appspot.com/o/graph-theory%2Fcompressed-sparse-row1.png?alt=media&token=aa1b2449-f800-4962-a7f3-f94431272191) 329 | 330 | ### Code Example: CSR Graph Representation 331 | 332 | Here is the Python code: 333 | 334 | ```python 335 | indptr = [0, 2, 3, 5, 6, 7, 8] 336 | indices = [2, 4, 0, 1, 3, 4, 2, 3] 337 | data = [1, 2, 3, 4, 5, 6, 7, 8] 338 | 339 | # Reading from the CSR Format 340 | for i in range(len(indptr) - 1): 341 | start = indptr[i] 342 | end = indptr[i + 1] 343 | print(f"Vertex {i} is connected to vertices {indices[start:end]} with data {data[start:end]}") 344 | 345 | # Writing a CSR Represented Graph 346 | # Vertices 0 to 5, Inclusive. 347 | # 0 -> [2, 3, 4] - Data [3, 5, 7] 348 | # 1 -> [0] - Data [1] 349 | # 2 -> [] - No outgoing edges. 350 | # 3 -> [1] - Data [2] 351 | # 4 -> [3] - Data [4] 352 | # 5 -> [2] - Data [6] 353 | 354 | # Code to populate: 355 | # indptr = [0, 3, 4, 4, 5, 6, 7] 356 | # indices = [2, 3, 4, 0, 1, 3, 2] 357 | # data = [3, 5, 7, 1, 2, 4, 6] 358 | ``` 359 |
360 | 361 | ## 10. Explain the _Breadth-First Search_ (BFS) traversing method. 362 | 363 | **Breadth-First Search** (BFS) is a graph traversal technique that systematically explores a graph level by level. It uses a **queue** to keep track of nodes to visit next and a list to record visited nodes, avoiding redundancy. 364 | 365 | ### Key Components 366 | 367 | - **Queue**: Maintains nodes in line for exploration. 368 | - **Visited List**: Records nodes that have already been explored. 369 | 370 | ### Algorithm Steps 371 | 372 | 1. **Initialize**: Choose a starting node, mark it as visited, and enqueue it. 373 | 2. **Explore**: Keep iterating as long as the queue is not empty. In each iteration, dequeue a node, visit it, and enqueue its unexplored neighbors. 374 | 3. **Terminate**: Stop when the queue is empty. 375 | 376 | ### Visual Representation 377 | 378 | ![BFS Example](https://techdifferences.com/wp-content/uploads/2017/10/BFS-correction.jpg) 379 | 380 | ### Complexity Analysis 381 | 382 | - **Time Complexity**: $O(V + E)$ where $V$ is the number of vertices in the graph and $E$ is the number of edges. This is because each vertex and each edge will be explored only once. 383 | 384 | - **Space Complexity**: $O(V)$ since, in the worst case, all of the vertices can be inside the queue. 385 | 386 | ### Code Example: Breadth-First Search 387 | 388 | Here is the Python code: 389 | 390 | ```python 391 | from collections import deque 392 | 393 | def bfs(graph, start): 394 | visited = set() 395 | queue = deque([start]) 396 | 397 | while queue: 398 | vertex = queue.popleft() 399 | if vertex not in visited: 400 | print(vertex, end=' ') 401 | visited.add(vertex) 402 | queue.extend(neighbor for neighbor in graph[vertex] if neighbor not in visited) 403 | 404 | # Sample graph representation using adjacency sets 405 | graph = { 406 | 'A': {'B', 'D', 'G'}, 407 | 'B': {'A', 'E', 'F'}, 408 | 'C': {'F'}, 409 | 'D': {'A', 'F'}, 410 | 'E': {'B'}, 411 | 'F': {'B', 'C', 'D'}, 412 | 'G': {'A'} 413 | } 414 | 415 | # Execute BFS starting from 'A' 416 | bfs(graph, 'A') 417 | # Expected Output: 'A B D G E F C' 418 | ``` 419 |
420 | 421 | ## 11. Explain the _Depth-First Search_ (DFS) algorithm. 422 | 423 | **Depth-First Search** (DFS) is a graph traversal algorithm that's simpler and **often faster** than its breadth-first counterpart (BFS). While it **might not explore all vertices**, DFS is still fundamental to numerous graph algorithms. 424 | 425 | ### Algorithm Steps 426 | 427 | 1. **Initialize**: Select a starting vertex, mark it as visited, and put it on a stack. 428 | 2. **Loop**: Until the stack is empty, do the following: 429 | - Remove the top vertex from the stack. 430 | - Explore its unvisited neighbors and add them to the stack. 431 | 3. **Finish**: When the stack is empty, the algorithm ends, and all reachable vertices are visited. 432 | 433 | ### Visual Representation 434 | 435 | ![DFS Example](https://firebasestorage.googleapis.com/v0/b/dev-stack-app.appspot.com/o/graph-theory%2Fdepth-first-search.jpg?alt=media&token=37b6d8c3-e5e1-4de8-abba-d19e36afc570) 436 | 437 | ### Code Example: Depth-First Search 438 | 439 | Here is the Python code: 440 | 441 | ```python 442 | def dfs(graph, start): 443 | visited = set() 444 | stack = [start] 445 | 446 | while stack: 447 | vertex = stack.pop() 448 | if vertex not in visited: 449 | visited.add(vertex) 450 | stack.extend(neighbor for neighbor in graph[vertex] if neighbor not in visited) 451 | 452 | return visited 453 | 454 | # Example graph 455 | graph = { 456 | 'A': {'B', 'G'}, 457 | 'B': {'A', 'E', 'F'}, 458 | 'G': {'A'}, 459 | 'E': {'B', 'G'}, 460 | 'F': {'B', 'C', 'D'}, 461 | 'C': {'F'}, 462 | 'D': {'F'} 463 | } 464 | 465 | print(dfs(graph, 'A')) # Output: {'A', 'B', 'C', 'D', 'E', 'F', 'G'} 466 | ``` 467 |
468 | 469 | ## 12. What are the key differences between _BFS_ and _DFS_? 470 | 471 | **BFS** and **DFS** are both essential graph traversal algorithms with distinct characteristics in strategy, memory requirements, and use-cases. 472 | 473 | ### Core Differences 474 | 475 | 1. **Search Strategy**: BFS moves level-by-level, while DFS goes deep into each branch before backtracking. 476 | 2. **Data Structures**: BFS uses a Queue, whereas DFS uses a Stack or recursion. 477 | 3. **Space Complexity**: BFS requires more memory as it may need to store an entire level ($O(|V|)$), whereas DFS usually uses less ($O(\log n)$ on average). 478 | 4. **Optimality**: BFS guarantees the shortest path; DFS does not. 479 | 480 | ### Visual Representation 481 | 482 | #### BFS 483 | 484 | ![BFS Traversal](https://firebasestorage.googleapis.com/v0/b/dev-stack-app.appspot.com/o/graph-theory%2FBreadth-First-Search-Algorithm.gif?alt=media&token=68f81a1c-00bc-4638-a92d-accdc257adc2) 485 | 486 | #### DFS 487 | 488 | ![DFS Traversal](https://firebasestorage.googleapis.com/v0/b/dev-stack-app.appspot.com/o/graph-theory%2FDepth-First-Search.gif?alt=media&token=e0ce6595-d5d2-421a-842d-791eb6deeccb) 489 | 490 | ### Code Example: BFS & DFS 491 | 492 | Here is the Python code: 493 | 494 | ```python 495 | # BFS 496 | def bfs(graph, start): 497 | visited = set() 498 | queue = [start] 499 | while queue: 500 | node = queue.pop(0) 501 | if node not in visited: 502 | visited.add(node) 503 | queue.extend(graph[node] - visited) 504 | 505 | # DFS 506 | def dfs(graph, start, visited=None): 507 | if visited is None: 508 | visited = set() 509 | visited.add(start) 510 | for next_node in graph[start] - visited: 511 | dfs(graph, next_node, visited) 512 | ``` 513 |
514 | 515 | ## 13. Implement a method to check if there is a _Path between two vertices_ in a graph. 516 | 517 | ### Problem Statement 518 | 519 | Given an **undirected** graph, the task is to determine whether or not there is a **path** between two specified vertices. 520 | 521 | ### Solution 522 | 523 | The problem can be solved using **Depth-First Search (DFS)**. 524 | 525 | #### Algorithm Steps 526 | 527 | 1. Start from the source vertex. 528 | 2. For each adjacent vertex, if not visited, recursively perform DFS. 529 | 3. If the destination vertex is found, return `True`. Otherwise, backtrack and explore other paths. 530 | 531 | #### Complexity Analysis 532 | 533 | - **Time Complexity**: $O(V + E)$ 534 | $V$ is the number of vertices, and $E$ is the number of edges. 535 | - **Space Complexity**: $O(V)$ 536 | For the stack used in recursive DFS calls. 537 | 538 | #### Implementation 539 | 540 | Here is the Python code: 541 | 542 | ```python 543 | from collections import defaultdict 544 | 545 | class Graph: 546 | def __init__(self): 547 | self.graph = defaultdict(list) 548 | 549 | def add_edge(self, u, v): 550 | self.graph[u].append(v) 551 | self.graph[v].append(u) 552 | 553 | def is_reachable(self, src, dest, visited): 554 | visited[src] = True 555 | 556 | if src == dest: 557 | return True 558 | 559 | for neighbor in self.graph[src]: 560 | if not visited[neighbor]: 561 | if self.is_reachable(neighbor, dest, visited): 562 | return True 563 | 564 | return False 565 | 566 | def has_path(self, src, dest): 567 | visited = defaultdict(bool) 568 | return self.is_reachable(src, dest, visited) 569 | 570 | # Usage 571 | g = Graph() 572 | g.add_edge(0, 1) 573 | g.add_edge(0, 2) 574 | g.add_edge(1, 2) 575 | g.add_edge(2, 3) 576 | g.add_edge(3, 3) 577 | 578 | source, destination = 0, 3 579 | print(f"There is a path between {source} and {destination}: {g.has_path(source, destination)}") 580 | ``` 581 |
582 | 583 | ## 14. Solve the problem of printing all _Paths from a source to destination_ in a Directed Graph with BFS or DFS. 584 | 585 | ### Problem Statement 586 | 587 | Given a **directed graph** and two vertices $src$ and $dest$, the objective is to **print all paths** from $src$ to $dest$. 588 | 589 | ### Solution 590 | 591 | 1. **Recursive Depth-First Search (DFS)** Algorithm in Graphs: DFS is used because it can identify all the paths in a graph from source to destination. This is done by employing a **backtracking mechanism** to ensure that all unique paths are found. 592 | 593 | 2. To deal with **cycles**, a list of visited nodes is crucial. By utilizing this list, the algorithm can avoid revisiting and getting stuck in an infinite loop. 594 | 595 | #### Complexity Analysis 596 | 597 | - **Time Complexity**: $O(V + E)$ 598 | - $V$ is the number of vertices and $E$ is the number of edges. 599 | - We're essentially visiting every node and edge once. 600 | 601 | - **Space Complexity**: $O(V)$ 602 | - In the worst-case scenario, the entire graph can be visited, which would require space proportional to the number of vertices. 603 | 604 | #### Implementation 605 | 606 | Here is the Python code: 607 | 608 | ```python 609 | # Python program to print all paths from a source to destination in a directed graph 610 | 611 | from collections import defaultdict 612 | 613 | # A class to represent a graph 614 | class Graph: 615 | def __init__(self, vertices): 616 | # No. of vertices 617 | self.V = vertices 618 | 619 | # default dictionary to store graph 620 | self.graph = defaultdict(list) 621 | 622 | def addEdge(self, u, v): 623 | self.graph[u].append(v) 624 | 625 | def printAllPathsUtil(self, u, d, visited, path): 626 | # Mark the current node as visited and store in path 627 | visited[u] = True 628 | path.append(u) 629 | 630 | # If current vertex is same as destination, then print current path 631 | if u == d: 632 | print(path) 633 | else: 634 | # If current vertex is not destination 635 | # Recur for all the vertices adjacent to this vertex 636 | for i in self.graph[u]: 637 | if not visited[i]: 638 | self.printAllPathsUtil(i, d, visited, path) 639 | 640 | # Remove current vertex from path and mark it as unvisited 641 | path.pop() 642 | visited[u] = False 643 | 644 | # Prints all paths from 's' to 'd' 645 | def printAllPaths(self, s, d): 646 | # Mark all the vertices as not visited 647 | visited = [False] * (self.V) 648 | 649 | # Create an array to store paths 650 | path = [] 651 | path.append(s) 652 | 653 | # Call the recursive helper function to print all paths 654 | self.printAllPathsUtil(s, d, visited, path) 655 | 656 | # Create a graph given in the above diagram 657 | g = Graph(4) 658 | g.addEdge(0, 1) 659 | g.addEdge(0, 2) 660 | g.addEdge(0, 3) 661 | g.addEdge(2, 0) 662 | g.addEdge(2, 1) 663 | g.addEdge(1, 3) 664 | 665 | s = 2 ; d = 3 666 | print("Following are all different paths from %d to %d :" %(s, d)) 667 | g.printAllPaths(s, d) 668 | ``` 669 |
670 | 671 | ## 15. What is a _Bipartite Graph_? How to detect one? 672 | 673 | A **bipartite graph** is one where the vertices can be divided into two distinct sets, $U$ and $V$, such that every edge connects a vertex from $U$ to one in $V$. The graph is denoted as $G = (U, V, E)$, where $E$ represents the set of edges. 674 | 675 | ![Bipartite Graph Example](https://mathworld.wolfram.com/images/eps-gif/BipartiteGraph_1000.gif) 676 | 677 | ### Detecting a Bipartite Graph 678 | 679 | You can check if a graph is bipartite using several methods: 680 | 681 | #### Breadth-First Search (BFS) 682 | 683 | BFS is often used for this purpose. The algorithm colors vertices alternately so that no adjacent vertices have the same color. 684 | 685 | #### Code Example: Bipartite Graph using BFS 686 | 687 | Here is the Python code: 688 | 689 | ```python 690 | from collections import deque 691 | 692 | def is_bipartite_bfs(graph, start_node): 693 | visited = {node: False for node in graph} 694 | color = {node: None for node in graph} 695 | color[start_node] = 1 696 | queue = deque([start_node]) 697 | 698 | while queue: 699 | current_node = queue.popleft() 700 | visited[current_node] = True 701 | 702 | for neighbor in graph[current_node]: 703 | if not visited[neighbor]: 704 | queue.append(neighbor) 705 | color[neighbor] = 1 - color[current_node] 706 | elif color[neighbor] == color[current_node]: 707 | return False 708 | 709 | return True 710 | 711 | # Example 712 | graph = {'A': ['B', 'C'], 'B': ['A', 'C'], 'C': ['A', 'B', 'D'], 'D': ['C']} 713 | print(is_bipartite_bfs(graph, 'A')) # Output: True 714 | ``` 715 | 716 | #### Cycle Detection 717 | 718 | A graph is not bipartite if it contains an odd cycle. Algorithms like **DFS** or **Floyd's cycle-detection** algorithm can help identify such cycles. 719 |
720 | 721 | 722 | 723 | #### Explore all 50 answers here 👉 [Devinterview.io - Graph Data Structure](https://devinterview.io/questions/data-structures-and-algorithms/graph-data-structure-interview-questions) 724 | 725 |
726 | 727 | 728 | data-structures-and-algorithms 729 | 730 |

731 | 732 | --------------------------------------------------------------------------------