└── README.md
/README.md:
--------------------------------------------------------------------------------
1 | # 50 Essential Graph Data Structure Interview Questions in 2025
2 |
3 |
4 |
5 |
6 |
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 | 
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 | 
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 | 
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 | 
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 | 
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 | 
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 | 
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 | 
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 | 
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 | 
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 | 
263 |
264 | ### Example: Incidence Matrix for an Undirected Multigraph
265 |
266 | 
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 | 
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 | 
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 | 
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 | 
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 | 
485 |
486 | #### DFS
487 |
488 | 
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 | 
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 |
729 |
730 |
731 |
732 |
--------------------------------------------------------------------------------