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 pathor shortest path source
dest
Blue path:200 m / 40 kmh + 300 m / 50 kmh + 1 turn 18 s + 21.6 s + 15 s = 54.6 s
200 m
300 m
Red path:100 m / 40 kmh + 200 m / 40 kmh + 200 m / 50 kmh + 2 turns 9 s + 18 s + 14.4 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.
Yonge St.
15 s penalty
No penalty
Internet Routing
source
dest
Nodes: computers and switchesEdges: cables Find a path to connect computers Typical minimum weight path definition:• Fewest routers• Or least congested
Circuit Board Design
Nodes: small square for metalEdges: squares we can connectFind paths to connect chip I/Os
Integrated Circuits
Nodes: small grid squares for metalEdges: squares we can connectFind paths to connect gatesHuge graph (tens of millions of nodes) need fast algorithms
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
1
2
3
3
4
Recursion?
sourcedest
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?
sourcedest
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?
sourcedest
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
sourcedest
bool findPath (Node* currNode, int destID, list<Edge> path) { if (currNode->id == destID) return true; currNode->visited = true;
for each (outEdge of currNode) { Node *toNode = outEdge.toNode; if (!toNode->visited) { list<Edge> 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<Edge> 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<Edge> newPath = path; newPath.push_back (outEdge); bool found = findPath (toNode, destID, newPath); if (found)
return (true); } } return (false); }
Easy Fix Part 2
sourcedest
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<Edge> 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<Edge> 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(N2)– 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!