####.##.## #....#..## #.####.#.# #......#.# #####....# ##########
--- template:dfs What order will the DFS traversal visit the nodes in the graph on the right when we start DFS at A? -- There are more than one possibilities. The exact order is determine by how the graph is stored and how we access the nodes adjacent to a given node. Let's look at some possible traversals --- template:dfs - start with A (visited: A) -- - we have three choices for the next node: B, F, or H; let's pick F (visited: A, F) -- - from F, we can either go to D or H; let's pick H (visited A,F,H) -- - at H, the only choices are A and F and they are both visited, so we go back to F and look for other options; the only option is D (visited: A,F,H,D) -- - from D, we can got to E, G, or C (since F has been visited before); let's pick G (visited: A,F,H,D,G) -- - G has no un-visited neightbors, so we go back to D; let's pick E (visited: A,F,H,D,G,E) -- - E's only un-visited neighbor is B, so we go there; (visited: A,F,H,D,G,E,B) -- - B's unvisited neighbor is C, so we go there; (visited: A,F,H,D,G,E,B,C) -- - C's unvisited neighbor is J, so we go there; (visited: A,F,H,D,G,E,B,C,J) -- At this point we have visited all the nodes and our traversal is: A, F, H, D, G, E, B, C, J But an algorihtm would need to verify that all the nodes have been visited. --- template: dfs Here are two other DFS traversals starting at node A: - A, B, E, D, F, H, G, C, J - A, B, C, J, D, E, F, H, G -- __Task__ List all possible traversals starting at node with label J. --- template:dfs An algorithm using the adjacency list, `adj`, of a graph with `n` nodes and `m` edges, `O(n+m)` .below-column2[ ``` visited[N] - boolean array with all values set initially to false dfs ( start ) if visited[start] return //already processed this node visit/process the node visited[start] = true //mark node as visited for node in adj[start] dfs( node ) ``` ] --- name:bfs ## Breadth First Search Traversal .right-column2[ ] --- template:bfs - visits the nodes in order of their distance from a starting node (distance = path length between nodes) - visit all nodes whose distance from the start node is 1 - visit all nodes whose distance from the start node is 2 - ... -- What order will the BFS traversal visit the nodes in the graph on the right when we start BFS at A? -- Again, the exact ordering will depend on how we store the graph and how we access nodes adjacent to a given node. Here are possible traversals for the graph on the right. - A, B, F, H, C, E, D, G, J -- __Question__ Which of the values in the above traversal could be moved and to where so that it is still a BFS traversal? -- __Task__ Propose a BFS traversal of this graph starting at node J. --- template:bfs An algorithm using the adjacency list `adj` of a graph with `n` nodes and `m` edges, `O(n+m)` ``` visited[N] - boolean array with all values set initially to false queue - to store nodes that we need to go back to bfs ( start ) visited [ start ] = true visit/process the start node queue.push( start ) while queue is not empty node = queue.pop() for n in adj( node ) if ! visited[n] visited [n] = true visit/process n queue.push( n ) ``` --- template:section # Examples and Things to Think About --- ## Graph Implementation Graphs serve many different purposes and their exact implementation needs to suit a specific purpose, so there are generally not among data structures implemented in programming language libraries. Implement a graph. Pick any implementation you want (or try it with all of them). Assume that the graph nodes have integer labels starting with 1 up to N. The graph is undirected and its description is given as a list of edges: `a b` indicates that there is an edge from `a` to `b`. -- How would your implementation be different for a directed graph? -- Modify your code so that it can handle weighted graphs. In this case the graph description would be given as a list of edges: `a b w` indicates that there is an edge from `a` to `b` with weight `w`. --- template: challenge ## Challenge Design algorithms for the following problems. - Given a pair of nodes, find the length of the shortest path from one to the other. Solve this for unweighted and weighted graphs. - Given a node, find the length of the shortest path from that node to all other nodes in the graph. Solve this for unweighted and weighted graphs. - Determine if a graph is connected or disconnected (algorithmically, not visually). If it is disconnected, figure out the number of nodes in each connected component. - Determine if a graph is a tree (algorithmically, not visually). --- ## Solving Problems Try to use your graph implementation to solve the following problems: - [Ab Initio](https://open.kattis.com/problems/abinitio) - the description is long, but it is a fairly easy problem if you have a graph implementation already - [Flying Safely](https://open.kattis.com/problems/flyingsafely) - [Cantina of Babel](https://open.kattis.com/problems/cantinaofbabel) - this is not a difficult problem, but you need to first figure out how to map it to a graph. --- ## Connected Components in a Graph A __connected component__ (or simply a component) of an undirected graph is a subgraph in which any two vertices are connected to each other by paths, and which is connected to no additional vertices in the supergraph. - In simpler terms, if you can get from any node in a component to any other node in the same component, and you can't get out of that component to another part of the graph, then it's a connected component. -- .left-column2[.center[ ]] -- .right-column2[.center[ ]] --- ## Why do we care about connected components? -- __Social Network Analysis__: - Community Detection: Identifying groups of tightly knit individuals in a social network. Each connected component could represent a distinct community or isolated group. - Influence Propagation: If information or a virus spreads through a network, it will only spread within its connected component. This helps in understanding the potential reach of information or disease. - Identifying Disconnected Users: Finding users who are completely isolated from certain groups or the main network. -- __Network Reliability and Redundancy (Computer Networks)__: - Server Clusters: If a network representing server connections has multiple connected components, it means that some servers cannot communicate with others. This indicates a critical fault or partitioning in the network. - Resilience Planning: Understanding connected components helps in designing more robust networks by ensuring critical nodes are part of a well-connected component and identifying single points of failure that could split the network into multiple components. --- ## Why do we care about connected components? __Transportation and Logistics__: - Road Networks: Identifying if all cities or regions are reachable from each other. If the graph of roads has multiple connected components, it means some areas are completely isolated from others by road. - Airline Routes: Determining which airports are connected within a single airline's network or across multiple partner airlines. -- __Ecosystem Modeling__: - Habitat Connectivity: In ecological studies, graphs can represent patches of habitat and the corridors connecting them. Connected components identify areas where species can move freely, which is crucial for conservation efforts. - Disease Spread: Similar to social networks, understanding how connected components in an ecosystem relate to the spread of disease among animal populations. -- __and many more ...__ --- ## Finding Connected Components -- The most common algorithms to find connected components in a graph are Depth-First Search (DFS) and Breadth-First Search (BFS). Both algorithms can be adapted to identify all connected components. -- The general approach is as follows: - Initialize a `visited` array for all vertices to `false`. - Iterate through each vertex `v` in the graph. - If `v` has not been visited: - Start a DFS (or BFS) from `v`. This marks all vertices reachable from `v` as visited. - All vertices reachable from `v` during this traversal belong to the same connected component. - Store this set of vertices as a new connected component. Continue until all vertices have been visited.