
Warmup Warmup CSE 373: Graph traversal 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). Michael Lee Solution: This algorithm is known as the 2-color algorithm. We Friday, Feb 16, 2018 can solve it by using any graph traversal algorithm, and alternating colors as we go from node to node. 1 2 Goal: How do we traverse graphs? Breadth-first search (BFS) example Today’s goal: how do we traverse graphs? search(v): Idea 1: Just get a list of the vertices and loop over them visited = empty set f queue.enqueue(v) Problem: What if we want to traverse graphs following the edges? visited.add(v) g d a j For example, can we... while (queue is not empty): i curr = queue.dequeue() h I Traverse a graph to find if there’s a connection from one node for (w : v.neighbors()): e b to another? if (w not in visited): queue.enqueue(w) I Determine if we can start from our node and touch every visited.add(curr) c other node? Current node: a b d c e f g h i I Find the shortest path between two nodes? Queue: a, b, d, c, e, f, g, h, i, Solution: Use graph traversal algorithms like breadth-first search Visited: a, b, d, c, e, f, g, h, i, and depth-first search 3 4 Breadth-first search (BFS) Breadth-first search (BFS) Breadth-first traversal, core idea: 1. Use something (e.g. a queue) to keep track of every vertex to Pseudocode: visit search(v): visited = empty set 2. Add and remove nodes from queue until it’s empty queue.enqueue(v) 3. Use a set to store nodes we don’t want to recheck/revisit visited.add(v) 4. Runtime: while (queue is not empty): curr = queue.dequeue() I We visit each node once. I For each node, check each edge to see if we should add to for (w : v.neighbors()): if (w not in visited): queue queue.enqueue(w) I So we check each edge at most twice visited.add(curr) So, O (jV j + 2jEj), which simplifies to O (jV j + jEj). 5 6 An interesting property... An interesting property... Note: We visited the nodes in “rings” – maintained a gradually What does this look like for trees? growing “frontier” of nodes. f g d a j i h e b c The algorithm traverses the width, or “breadth” of the tree 7 8 Depth-first search (DFS) Depth-first search (DFS) example Question: Why a queue? Can we use other data structures? search(v): visited = empty set f Answer: Yes! Any kind of list-like thing that supports appends stack.push(v) and removes works! For example, what if we try using a stack? while (stack is not empty): g curr = stack.pop() d The BFS algorithm: The DFS algorithm: visited.add(curr) i a j search(v): search(v): visited = empty set visited = empty set for (w : v.neighbors()): h if (w not in visited): e stack.push(v) b queue.enqueue(v) visited.add(v) stack.push(w) visited.add(v) while (stack is not empty): c while (queue is not empty): curr = stack.pop() curr = queue.dequeue() visited.add(curr) Current node: adgihfecb for (w : v.neighbors()): for (w : v.neighbors()): Stack: a, b, d, e, f, g, h, i, c, if (w not in visited): if (w not in visited): queue.enqueue(w) stack.push(w) visited.add(curr) visited.add(v) Visited: a, b, d, e, f, g, h, i, e, c, 9 10 Depth-first search (DFS) An interesting property... Depth-first traversal, core idea: Note: Rather the growing the node in “rings”, we randomly 1. Instead of using a queue, use a stack. Otherwise, keep wandered through the graph until we got stuck, then everything the same. “backtracked”. 2. Runtime: also O (jV j + jEj) for same reasons as BFS f Pseudocode: search(v): g d visited = empty set a j i stack.push(v) visited.add(v) h e b while (stack is not empty): curr = stack.pop() c for (w : v.neighbors()): if (w not in visited): stack.push(w) visited.add(curr) 11 12 An interesting property... Compare and contrast What does this look like for trees? Question: When do we use BFS vs DFS? Related question: How much memory does BFS and DFS use in the worst case? I BFS: O (jV j) – what if every node is connected to the start? I DFS: O (jV j) – what if the nodes are arranged like a linked list? So, in the worst case, BFS and DFS both have the same The algorithm traverses to the bottom first: it prioritizes the worst-case runtime and memory usage. “depth” of the tree They only differ in what order they visit the nodes. Note: rest of algorithm omitted 13 14 Compare and contrast Design challenge How much memory does BFS and DFS use in the average case? Question: How would you modify BFS to find the shortest path between every node? Related question: how much memory do they use when we want to traverse a tree? x y I BFS: O (“width” of tree) = O (num leaves) w z I DFS: O (height) For graphs: S E a b I Use BFS if graph is “narrow”, or if solution is “near” start I Use DFS if graph is “wide” Observation: Since BFS moves out in rings, we will reach the end In practice, graphs are often large/very wide, so DFS is often a node via the path of length 3 first. good default choice. (It’s also possible to implement DFS Idea: when we enqueue, store where we came from in some way. recursively!) (e.g. mark node, use a dictionary...) 15 16 After BFS is done, backtrack. Design challenge: pathfinding Design challenge: pathfinding Question: What if the edges have weights? Question: How would you modify BFS to find the shortest path 2 y between every node? 2 x 2 w z x y 2 2 w z S E 100 a b 100 S E 100 a b Weighted graph A weighted graph is a kind of graph where each edge has a Now, start from any node, follow arrows, then reverse to get path. numerical “weight” associated with it. This number can represent anything, but is often (but not always!) used to indicate the “cost” of traveling down that edge. 17 18 And we’re done! Now, to find the shortest path, from a to a node, And we’re done! start at the end, trace the red arrows backwards, and reverse the Now, to find the shortest path, from a to a node, start at the end, list. trace the red arrows backwards, and reverse the list. And we’re done! Now, to find the shortest path, from a to a node, start at the end, trace And we’re done! Now, to find the shortest path, from a to a node, the red arrows backwards, and reverse the list. start at the end, trace the red arrows backwards, and reverse the list. Pathfinding and DFS Dijkstra’s algorithm Core idea: We can use BFS to correctly find the shortest path between two 1. Assign each node an initial cost of 1 nodes in an unweighted graph... 2. Set our starting node’s cost to 0 3. Update all adjacent vertices costs to the minimum known cost 4. Mark the current node as being “done” 5. Pick the next unvisited node with the minimum cost. Go to ...but it fails if the graph is weighted! step 3. We need a better algorithm. 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? Today: Dijkstra’s algorithm Caveat: Dijkstra’s algorithm only guaranteed to work for graphs with no negative edge weights. 19 Pronunciation: DYKE-struh (“dijk” rhymes with “bike”) 20 Dijkstra’s algorithm Dijkstra’s algorithm Suppose we start at vertex “a”: Suppose we start at vertex “a”: 2 2 3 1 1 1 1 a b f h 2 2 3 a b f h 1 10 1 4 5 1 10 2 4 5 1 9 2 1 9 2 11 1 d c e g 2 11 d c e g 3 1 1 1 1 3 7 7 We initially assign all nodes a cost of infinity. 21 21 Dijkstra’s algorithm Dijkstra’s algorithm Suppose we start at vertex “a”: Suppose we start at vertex “a”: 0 1 1 1 0 2 1 1 2 2 3 2 2 3 a b f h a b f h 1 1 10 10 4 5 1 4 5 1 2 2 9 9 1 1 2 11 2 11 d c e g d c e g 1 1 1 1 1 1 3 4 1 3 7 7 Next, assign the starting node a cost of 0. Next, update all adjacent node costs as well as the backpointers. 21 21 And we’re done! Now, to find the shortest path, from a to a node, start at the end, trace the red arrows backwards, and reverse the And we’re done! Now, to find the shortest path, list. from a to a node, start at the end, trace the red arrows backwards, and reverse the list.
Details
-
File Typepdf
-
Upload Time-
-
Content LanguagesEnglish
-
Upload UserAnonymous/Not logged-in
-
File Pages7 Page
-
File Size-