cse 373 graph traversal
play

CSE 373: Graph traversal Visited: a, b, d, c, e, f, g, h, i, d c - PDF document

CSE 373: Graph traversal Visited: a, b, d, c, e, f, g, h, i, d c e f g h i j Current node: a b d c e f g h i Queue: a, b, d, c, e, f, g, h, i, 4 a Breadth-fjrst search (BFS) Breadth-fjrst traversal, core idea: 1. Use something (e.g.


  1. CSE 373: Graph traversal Visited: a, b, d, c, e, f, g, h, i, d c e f g h i j Current node: a b d c e f g h i Queue: a, b, d, c, e, f, g, h, i, 4 a Breadth-fjrst search (BFS) Breadth-fjrst traversal, core idea: 1. Use something (e.g. a queue) to keep track of every vertex to visit 2. Add and remove nodes from queue until it’s empty 3. Use a set to store nodes we don’t want to recheck/revisit 4. Runtime: queue 5 Breadth-fjrst search (BFS) Pseudocode: Michael Lee b 6 For example, can we... Friday, Feb 16, 2018 1 Warmup Warmup Given a graph, assign each node one of two colors such that no two adjacent vertices have the same color. (If it’s impossible to color the graph this way, your algorithm should say so). Solution: This algorithm is known as the 2-color algorithm. We can solve it by using any graph traversal algorithm, and alternating 2 Goal: How do we traverse graphs? Today’s goal: how do we traverse graphs? Idea 1: Just get a list of the vertices and loop over them Problem: What if we want to traverse graphs following the edges? colors as we go from node to node. to another? and depth-fjrst search Breadth-fjrst search (BFS) example other node? 3 Solution: Use graph traversal algorithms like breadth-fjrst search search(v): visited = empty set queue.enqueue(v) visited.add(v) while (queue is not empty): curr = queue.dequeue() ◮ Traverse a graph to fjnd if there’s a connection from one node for (w : v.neighbors()): if (w not in visited): queue.enqueue(w) ◮ Determine if we can start from our node and touch every visited.add(curr) ◮ Find the shortest path between two nodes? search(v): visited = empty set queue.enqueue(v) visited.add(v) while (queue is not empty): curr = queue.dequeue() ◮ We visit each node once. ◮ For each node, check each edge to see if we should add to for (w : v.neighbors()): if (w not in visited): queue.enqueue(w) ◮ So we check each edge at most twice visited.add(curr) So, O ( | V | + 2 | E | ) , which simplifjes to O ( | V | + | E | ) .

  2. An interesting property... i 10 Visited: a, b, d, e, f, g, h, i, e, c, Stack: a, b, d, e, f, g, h, i, c, Current node: adgihfecb j c h Depth-fjrst traversal, core idea: g f e d b a Note: We visited the nodes in “rings” – maintained a gradually Depth-fjrst search (DFS) 1. Instead of using a queue, use a stack. Otherwise, keep 9 d j i h g f e c b everything the same. a “backtracked”. wandered through the graph until we got stuck, then Note: Rather the growing the node in “rings”, we randomly An interesting property... 11 Pseudocode: Depth-fjrst search (DFS) example 12 and removes works! For example, what if we try using a stack? h Question: Why a queue? Can we use other data structures? Depth-fjrst search (DFS) 8 The algorithm traverses the width, or “breadth” of the tree What does this look like for trees? The DFS algorithm: An interesting property... 7 j i g The BFS algorithm: f e c d b a growing “frontier” of nodes. Answer: Yes! Any kind of list-like thing that supports appends search(v): visited = empty set stack.push(v) while (stack is not empty): curr = stack.pop() visited.add(curr) search(v): search(v): for (w : v.neighbors()): visited = empty set visited = empty set if (w not in visited): stack.push(v) stack.push(w) queue.enqueue(v) visited.add(v) visited.add(v) while (stack is not empty): while (queue is not empty): curr = stack.pop() curr = queue.dequeue() visited.add(curr) for (w : v.neighbors()): for (w : v.neighbors()): if (w not in visited): if (w not in visited): queue.enqueue(w) stack.push(w) visited.add(curr) visited.add(v) 2. Runtime: also O ( | V | + | E | ) for same reasons as BFS search(v): visited = empty set stack.push(v) visited.add(v) while (stack is not empty): curr = stack.pop() for (w : v.neighbors()): if (w not in visited): stack.push(w) visited.add(curr)

  3. An interesting property... b Question: What if the edges have weights? Design challenge: pathfjnding 17 Now, start from any node, follow arrows, then reverse to get path. z y x w 100 a E S between every node? Question: How would you modify BFS to fjnd the shortest path Design challenge: pathfjnding 16 After BFS is done, backtrack . 100 100 Idea: when we enqueue, store where we came from in some way. x always!) used to indicate the “cost” of traveling down that edge. This number can represent anything, but is often (but not numerical “weight” associated with it. A weighted graph is a kind of graph where each edge has a Weighted graph z y w 2 b a E S 2 2 2 2 (e.g. mark node, use a dictionary...) node via the path of length 3 fjrst. What does this look like for trees? list? Related question: how much memory do they use when we want to How much memory does BFS and DFS use in the average case? Compare and contrast 14 They only difger in what order they visit the nodes. worst-case runtime and memory usage. So, in the worst case, BFS and DFS both have the same the worst case? Observation: Since BFS moves out in rings, we will reach the end Related question: How much memory does BFS and DFS use in Question: When do we use BFS vs DFS? Compare and contrast 13 Note: rest of algorithm omitted “depth” of the tree The algorithm traverses to the bottom fjrst: it prioritizes the traverse a tree? 18 b x w a E S For graphs: between every node? Question: How would you modify BFS to fjnd the shortest path Design challenge 15 recursively!) good default choice. (It’s also possible to implement DFS In practice, graphs are often large/very wide, so DFS is often a y z ◮ BFS: O ( | V | ) – what if every node is connected to the start? ◮ DFS: O ( | V | ) – what if the nodes are arranged like a linked ◮ BFS: O ( “width” of tree ) = O ( num leaves ) ◮ DFS: O ( height ) ◮ Use BFS if graph is “narrow”, or if solution is “near” start ◮ Use DFS if graph is “wide”

  4. Pathfjnding and DFS 5 c e g 2 1 4 10 h 2 9 11 2 7 1 d f 2 1 2 7 1 3 2 We can use BFS to correctly fjnd the shortest path between two We initially assign all nodes a cost of infjnity. b And we’re done! Now, to fjnd the shortest path, from a to a node, start at the end, trace the red arrows backwards, and reverse the list. 21 Dijkstra’s algorithm Suppose we start at vertex “a”: a 3 3 9 1 10 2 9 11 2 7 3 4 2 3 1 Next, update all adjacent node costs as well as the backpointers. And we’re done! Now, to fjnd the shortest path, from a to a node, start at the end, trace the red arrows backwards, and reverse the list. 5 1 1 Suppose we start at vertex “a”: Next, assign the starting node a cost of 0. And we’re done! Now, to fjnd the shortest path, from a to a node, start at the end, trace the red arrows backwards, and reverse the list. 21 Dijkstra’s algorithm a 2 b f h d c e g 11 3 2 g a b f h d c e 2 Dijkstra’s algorithm 1 4 5 10 10 9 11 Suppose we start at vertex “a”: 20 7 2. Set our starting node’s cost to 0 nodes in an unweighted graph... ...but it fails if the graph is weighted! We need a better algorithm. Today: Dijkstra’s algorithm 19 Dijkstra’s algorithm Core idea: 3. Update all adjacent vertices costs to the minimum known cost Pronunciation: DYKE-struh (“dijk” rhymes with “bike”) 4. Mark the current node as being “done” 5. Pick the next unvisited node with the minimum cost. Go to step 3. Metaphor: Treat edges as canals and edge weights as distance. Imagine opening a dam at the starting node. How long does it take for the water to reach each vertex? Caveat: Dijkstra’s algorithm only guaranteed to work for graphs with no negative edge weights. 2 2 1 a 5 4 1 2 g e c d h f 3 b 21 start at the end, trace the red arrows backwards, and reverse the 2 3 1 Dijkstra’s algorithm And we’re done! Now, to fjnd the shortest path, from a to a node, list. Suppose we start at vertex “a”: 21 1. Assign each node an initial cost of ∞ ∞ ∞ ∞ ∞ ∞ ∞ ∞ ∞ 0 ∞ ∞ ∞ 0 2 ∞ ∞ ∞ ∞ ∞ ∞ ∞ ∞ 4 1

Recommend


More recommend