Milestone 3: Finding Routes ECE 297
Directions: How?
Model as Graph Problem (Node) (Edge) Bathurst Path: sequence of connected nodes from a source to a dest source dest
Model as Graph Problem Path: store as sequence of nodes? Bathurst source dest back lane Now sequence of nodes doesn’t uniquely describe path
Model as Graph Problem Bathurst Better: sequence of connected edges from a source to a dest source dest
Finding Routes Find path from source to dest –Is there only one path? Any path? Fewest nodes? Fewest edges? Minimum travel time! Graph texts: minimum weight path or shortest path source dest
Blue path: 200 m / 40 kmh m / 50 kmh + 1 turn 18 s s + 15 s = 54.6 s 200 m 300 m Red path: 100 m / 40 kmh m / 40 kmh m / 50 kmh + 2 turns 9 s + 18 s s + 2*15 s = 71.4 s 100 m Minimum Travel Time Definition Distance / speed limit + 15 s per street change Which is better? source dest
Minimum Travel Time Definition Don’t left turns take longer than right turns? –Yes, we’re ignoring to keep simple Name change with no turn? –15 s Bloor St. EastBloor St. West Yonge St. 15 s penalty No penalty
Other Shortest Path Applications Any Ideas?
Internet Routing source dest Nodes: computers and switches Edges: cables Find a path to connect computers Typical minimum weight path definition: Fewest routers Or least congested
Circuit Board Design Nodes: small square for metal Edges: squares we can connect Find paths to connect chip I/Os
Integrated Circuits Nodes: small grid squares for metal Edges: squares we can connect Find paths to connect gates Huge graph (tens of millions of nodes) need fast algorithms
Depth First Search Shortest Path Algorithm #1
Recursion? int main () { Node *sourceNode = getNodebyID (sourceID); bool found = findPath (sourceNode, destID);... } bool findPath (Node* currNode, int destID) {... } source dest
bool findPath (Node* currNode, int destID) { if (currNode->id == destID) return (true); for each (outEdge of currNode) { Node *toNode = outEdge.toNode; bool found = findPath (toNode, destID); if (found) return (true); } return (false); } Recursion? source dest
Recursion? source dest bool findPath (Node* currNode, int destID) { if (currNode->id == destID) return (true); for each (outEdge of currNode) { Node *toNode = outEdge.toNode; bool found = findPath (toNode, destID); if (found) return (true); } return (false); } Infinite Loop!
How to Fix? source dest bool findPath (Node* currNode, int destID) { if (currNode->id == destID) return (true); currNode->visited = true; for each (outEdge of currNode) { Node *toNode = outEdge.toNode; if (!toNode->visited) { bool found = findPath (toNode, destID); if (found) return (true); } } return (false); } toNode visited
Output? source dest bool findPath (Node* currNode, int destID) {... return (true);... } Says whether or not a path was found But not what the path is! Worst directions ever: yes, a path exists! How to fix?
Easy Fix source dest bool findPath (Node* currNode, int destID, list path) { if (currNode->id == destID) return true; currNode->visited = true; for each (outEdge of currNode) { Node *toNode = outEdge.toNode; if (!toNode->visited) { list newPath = path; newPath.push_back (outEdge); bool found = findPath (toNode, destID, newPath); if (found) return (true); } } return (false); } Anything Missing? a b c d {} {a} {a,b} {a,b,c,d} {a,b,c}
bool findPath (Node* currNode, int destID, list path) { if (currNode->id == destID) print out or save the path, then return (true); currNode->visited = true; for each (outEdge of currNode) { Node *toNode = outEdge.toNode; if (!toNode->visited) { list newPath = path; newPath.push_back (outEdge); bool found = findPath (toNode, destID, newPath); if (found) return (true); } return (false); } Easy Fix Part 2 source dest
Depth First Search (DFS) Developed depth first search in a graph –Need a visited flag –Unlike a tree (can go in circles otherwise) Graph is big (>100,000 nodes, N) –Complexity of DFS?
bool findPath (Node* currNode, int destID, list path) { if (currNode->id == destID) print out or save the path, then return (true); currNode->visited = true; for each (outEdge of currNode) { Node *toNode = outEdge.toNode; if (!toNode->visited) { list newPath = path; newPath.push_back (outEdge); bool found = findPath (toNode, destID, newPath); if (found) return (true); } return (false); } Complexity Analysis At most one findPath call per Node Executes once per outgoing edge Average: about 4
Complexity findPath executes at most O(N) times –Constant, O(1), work in each call –Except copying path – path could have O(N) nodes –Total worst-case complexity: O(N 2 ) –Average path shorter Average case somewhat better, but hard to analyze
Complexity Can we do better? –Don’t pass entire path around –One way: each Node stores the edge used to reach it –Reconstruct path when you reach the dest By following the previous edges / “bread crumbs” –Now work per findPath is O(1) Total complexity is O(N) Good for a graph algorithm!
DFS - Demo