Lecture 5
Lecture 5
Lecture Overview
Games
Depth First Search Text Section 4.2 Breadth First Search Text Section 4.3
Modelling (cont)
We may have a lot of moves
Each node represents current state of game. ordered pair (i,j) i = # sticks on table j = maximum we can remove Each valid move is a directed edge
Start: (n,n-1)
Node (0,0) is a losing position Why?
Model (cont)
Graph representing the game of Nim
5:4 1:1
3:3
0:0
2:2
4:2 3:2
3:3
0:0
2:2 4:2
3:2
Node (3,2) is losing because player has to send opponent to a winning position
Graph: nodes = game states directed edges = legal moves Labels: win, lose, draw can be assigned to nodes
Labelling strategy
1. Label any terminal positions
often, if you cant move you lose sometimes if you cant move its a draw (eg. chess)
2. A non-terminal is winning if at least one edge leads to a losing position 3. A non-terminal is losing if all edges lead to winning positions
4. Any other non-terminal is a draw. (One of its successors must also be a draw.) Why?
Big Games
Labeling always works.... in principle. Consider chess state information is very large Graph may be extremely big May only want to explore a small section of the graph May not want to store whole graph
Searching graphs
The previous problems were just examples of searching graphs (topological sort, backtracking, games, etc) Goal: visit all nodes in the graph Two basic strategies: Depth first search every time you see a new node, stop and go and look at that node... Will end up far away from home node - brave heart approach Breadth first search look at all the nearest nodes, in the order you found them, before expanding out.... Always stay close to the home node - cautious approach Both work for directed and undirected graphs.
Depth-First Search
G = (V,E) Graph can be directed or undirected Each node marked visited or Time complexity Q(|V|+|E|); Why? Strictly speaking, the complexity will depend on data structure used to represent the graph
not-visited
procedure dfSearch(G) for each v V do visit[v] false for each v V do if !visit [v] then dfs(v)
procedure dfs(v) visit [v] true for each node w adjacent to v if !visit[w] then dfs(w)
Depth-First Search
DFS gives two different ordering of nodes: the order in which they are visited and the order in which they become a dead end procedure dfSearch(G) count1 0 count2 0 for each v V do visit1[v] 0 for each v V do if visit1[v] = 0 then dfs(v)
procedure dfs(v) count1 count1 + 1 visit1[v] count1 for each node w adjacent to v if visit1[w] = 0 then dfs(w) count2 count2 + 1 visit2[v] count2
DFS example
A
2,6 1,12 8,11
B
7,5
3,4
D I
E J
9,10
G
4,3
H
5,1
10,9
11,8
K
6,2
13,13
The black edges are called back edges and they connect a vertex with its ancestors in the DFS tree.
12,7
Input Parameters: adj Output Parameters: None dfs(adj,start) { n = adj.last for i = 1 to n visit[i] = false for i = 1 to n if (!visit[i]) dfs_recurs(adj,i) }
dfs_recurs(adj,i) { println(i) visit[i] = true trav = adj[i] while (trav != null) { i = trav.ver if (!visit[i]) dfs_recurs(adj,i) trav = trav.next
}
DFS - tree
A
Tree is not necessarily binary
B
F
D
I
E
J
spanning trees (not minimum) finding connected components analyzing graph structure (is graph acyclic?) finding cut vertices (articulation points) topological sorting backtracking
Breadth-first Search
DFS visits the neighbour of neighbour of... naturally recursive uses stack (possibly implicit) to order nodes LIFO BFS visits all neighbours before advancing not naturally recursive uses a queue of nodes FIFO
Breadth-First Search
G = (V,E) Graph can be directed or undirected Each node marked visited or Time complexity Q(|V|+|E|) why? procedure bfs(G) Q empty-queue visit [v] true enqueue v onto Q while Q not empty u Q.dequeue for each node w adjacent to u if !visit[w] then visit[w] true enqueue w
not-visited
procedure bfSearch(G) for each v V do visit[v] false for each v V do if !visit[v] then bfs(v)
BFS example
A B C D I E J The black edges are called cross edges and they connect vertices on the same or adjacent levels in the BFS trees.
Input Parameters: adj,start Output Parameters: None bfs(adj,start) { n = adj.last for i = 1 to n visit[i] = false visit[start] = true println(start) q.enqueue(start) // q is an initially empty queue while (!q.empty()) { current = q.front() q.dequeue() trav = adj[current] while (trav != null) { v = trav.ver if (!visit[v]) { visit[v] = true; println(v); q.enqueue(v) } trav = trav.next } } }
Two trees
A A
D
I
E
J F
D
I
E
J
G H
L BFS tree
DFS tree
BFS vs DFS
DFS rushes deep into graph, before even exploring nearby options. BFS visits nearby neighbours before going deeper.
Consider a game (like chess): BFS considers all options 1 move ahead before moving DFS makes a move, then another...
Using BFS
Graph/map colouring
A crazed Knights fan hires you to paint every room in their house alternatingly red & blue. They dont want a red room next to another red room nor a blue room next to another blue room
How could we represent this as a graph? How could we test if it was possible to paint the house as desired and if so, which rooms to paint which colour?
Paint 1st room red (or blue) All nodes reached by BFS in each iteration are painted opposite colour to previous iteration If we find a node which is already coloured, it must be opposite to the current colour Would DFS work?
6 5
1
6
5
9
4
Would you use DFS or BFS to get yourself out of the maze? Why?
BFS vs DFS
DFS
Data structures
Complexity
BFS
Queue Adjacency lists or adjacency matrix
Q(|V|+|E|) for adj. lists Q(|V|2) for adj. matrix Finding spanning trees, connected components, exploring graphs, shortest path
Applications Finding spanning trees, connected components, cut vertices (articulation points), exploring graphs, topological sort, backtracking