lecture13–part01...

31
Lecture 13 – Part 01 Graph Search Strategies

Upload: others

Post on 07-Jun-2020

1 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Lecture13–Part01 GraphSearchStrategiessierra.ucsd.edu/dsc40b-2020-sp/lectures/13-BFS-I/build/lecture.pdfTimeComplexity def full_bfs(graph): status = {node: 'undiscovered' for node

Lecture 13 – Part 01Graph Search Strategies

Page 2: Lecture13–Part01 GraphSearchStrategiessierra.ucsd.edu/dsc40b-2020-sp/lectures/13-BFS-I/build/lecture.pdfTimeComplexity def full_bfs(graph): status = {node: 'undiscovered' for node

How do we:

▶ determine if there is a path between two nodes?

▶ check if graph is connected?

▶ count connected components?

Page 3: Lecture13–Part01 GraphSearchStrategiessierra.ucsd.edu/dsc40b-2020-sp/lectures/13-BFS-I/build/lecture.pdfTimeComplexity def full_bfs(graph): status = {node: 'undiscovered' for node

Search Stategies

▶ A search strategy is a procedure for exploring agraph.

▶ Different strategies have different properties.

Page 4: Lecture13–Part01 GraphSearchStrategiessierra.ucsd.edu/dsc40b-2020-sp/lectures/13-BFS-I/build/lecture.pdfTimeComplexity def full_bfs(graph): status = {node: 'undiscovered' for node

Node Statuses

At any point during a search, a node is in exactly oneof three states:▶ visited▶ pending (discovered, but not yet visited)▶ undiscovered

Page 5: Lecture13–Part01 GraphSearchStrategiessierra.ucsd.edu/dsc40b-2020-sp/lectures/13-BFS-I/build/lecture.pdfTimeComplexity def full_bfs(graph): status = {node: 'undiscovered' for node

Rules

▶ At every step, next visited node chosen fromamong pending nodes.

▶ When a node is marked as visited, all of itsneighbors have been marked as pending.

Page 6: Lecture13–Part01 GraphSearchStrategiessierra.ucsd.edu/dsc40b-2020-sp/lectures/13-BFS-I/build/lecture.pdfTimeComplexity def full_bfs(graph): status = {node: 'undiscovered' for node

Choosing the next Node

How to choose among pending nodes?▶ Visit newest pending (depth-first search).

▶ Visit oldest pending (breadth-first search).

Page 7: Lecture13–Part01 GraphSearchStrategiessierra.ucsd.edu/dsc40b-2020-sp/lectures/13-BFS-I/build/lecture.pdfTimeComplexity def full_bfs(graph): status = {node: 'undiscovered' for node

Breadth-First Search

At every step:1. Visit oldest pending node.

2. Mark its undiscovered neighbors as pending.To store pending nodes, use a FIFO queue.

Page 8: Lecture13–Part01 GraphSearchStrategiessierra.ucsd.edu/dsc40b-2020-sp/lectures/13-BFS-I/build/lecture.pdfTimeComplexity def full_bfs(graph): status = {node: 'undiscovered' for node

Queues in Python

▶ Want Θ(1) time pops/appends on either side.

▶ from collections import deque (“deck”).▶ .popleft() and .pop()▶ list doesn’t have right time complexity!▶ import queue isn’t what you want!

▶ Keep track of node status attribute usingdictionary.

Page 9: Lecture13–Part01 GraphSearchStrategiessierra.ucsd.edu/dsc40b-2020-sp/lectures/13-BFS-I/build/lecture.pdfTimeComplexity def full_bfs(graph): status = {node: 'undiscovered' for node

BFSfrom collections import dequedef bfs(graph, source):

”””Start a BFS at `source`.”””status = {node: 'undiscovered' for node in graph.nodes}status[source] = 'pending'pending = deque([source])# while there are still pending nodeswhile pending:

u = pending.popleft()for v in graph.neighbors(u):

# explore edge (u,v)if status[v] == 'undiscovered':

status[v] = 'pending'# append to rightpending.append(v)

status[u] = 'visited'

Page 10: Lecture13–Part01 GraphSearchStrategiessierra.ucsd.edu/dsc40b-2020-sp/lectures/13-BFS-I/build/lecture.pdfTimeComplexity def full_bfs(graph): status = {node: 'undiscovered' for node

Example

1

2

3

4

5

6

Page 11: Lecture13–Part01 GraphSearchStrategiessierra.ucsd.edu/dsc40b-2020-sp/lectures/13-BFS-I/build/lecture.pdfTimeComplexity def full_bfs(graph): status = {node: 'undiscovered' for node

Example

1

2

3

4

5

6

pending = [1]

Before iterating.

Page 12: Lecture13–Part01 GraphSearchStrategiessierra.ucsd.edu/dsc40b-2020-sp/lectures/13-BFS-I/build/lecture.pdfTimeComplexity def full_bfs(graph): status = {node: 'undiscovered' for node

Example

1

2

3

4

5

6

pending = [2,3,4]

After 1st iteration.

Page 13: Lecture13–Part01 GraphSearchStrategiessierra.ucsd.edu/dsc40b-2020-sp/lectures/13-BFS-I/build/lecture.pdfTimeComplexity def full_bfs(graph): status = {node: 'undiscovered' for node

Example

1

2

3

4

5

6

pending = [3,4]

After 2nd iteration.

Page 14: Lecture13–Part01 GraphSearchStrategiessierra.ucsd.edu/dsc40b-2020-sp/lectures/13-BFS-I/build/lecture.pdfTimeComplexity def full_bfs(graph): status = {node: 'undiscovered' for node

Example

1

2

3

4

5

6

pending = [4]

After 3rd iteration.

Page 15: Lecture13–Part01 GraphSearchStrategiessierra.ucsd.edu/dsc40b-2020-sp/lectures/13-BFS-I/build/lecture.pdfTimeComplexity def full_bfs(graph): status = {node: 'undiscovered' for node

Example

1

2

3

4

5

6

pending = [6]

After 4th iteration.

Page 16: Lecture13–Part01 GraphSearchStrategiessierra.ucsd.edu/dsc40b-2020-sp/lectures/13-BFS-I/build/lecture.pdfTimeComplexity def full_bfs(graph): status = {node: 'undiscovered' for node

Example

1

2

3

4

5

6

pending = [5]

After 5th iteration.

Page 17: Lecture13–Part01 GraphSearchStrategiessierra.ucsd.edu/dsc40b-2020-sp/lectures/13-BFS-I/build/lecture.pdfTimeComplexity def full_bfs(graph): status = {node: 'undiscovered' for node

Example

1

2

3

4

5

6

pending = []

After 6th iteration.

Page 18: Lecture13–Part01 GraphSearchStrategiessierra.ucsd.edu/dsc40b-2020-sp/lectures/13-BFS-I/build/lecture.pdfTimeComplexity def full_bfs(graph): status = {node: 'undiscovered' for node

Note

▶ BFS works just as well for directed graphs.

Page 19: Lecture13–Part01 GraphSearchStrategiessierra.ucsd.edu/dsc40b-2020-sp/lectures/13-BFS-I/build/lecture.pdfTimeComplexity def full_bfs(graph): status = {node: 'undiscovered' for node

Claim

▶ bfs with source 𝑢 will visit all nodes reachablefrom 𝑢 (and only those nodes).

▶ Useful!▶ Is there a path between 𝑢 and 𝑣?▶ Is graph connected?

Page 20: Lecture13–Part01 GraphSearchStrategiessierra.ucsd.edu/dsc40b-2020-sp/lectures/13-BFS-I/build/lecture.pdfTimeComplexity def full_bfs(graph): status = {node: 'undiscovered' for node

Lecture 13 – Part 02Analysis of BFS

Page 21: Lecture13–Part01 GraphSearchStrategiessierra.ucsd.edu/dsc40b-2020-sp/lectures/13-BFS-I/build/lecture.pdfTimeComplexity def full_bfs(graph): status = {node: 'undiscovered' for node

Exploring with BFS

▶ BFS will visit all nodes reachable from source.

▶ If disconnected, BFS will not visit all nodes.

▶ We can do so with a full BFS.▶ Idea: “re-start” BFS on undiscovered node.▶ Must pass statuses between calls.

Page 22: Lecture13–Part01 GraphSearchStrategiessierra.ucsd.edu/dsc40b-2020-sp/lectures/13-BFS-I/build/lecture.pdfTimeComplexity def full_bfs(graph): status = {node: 'undiscovered' for node

Making Full BFS

Modify bfs to accept statuses:

def bfs(graph, source, status=None):”””Start a BFS at `source`.”””if status is None:

status = {node: 'undiscovered' for node in graph.nodes}# ...

Page 23: Lecture13–Part01 GraphSearchStrategiessierra.ucsd.edu/dsc40b-2020-sp/lectures/13-BFS-I/build/lecture.pdfTimeComplexity def full_bfs(graph): status = {node: 'undiscovered' for node

Making Full BFS

Call bfs multiple times:def full_bfs(graph):

status = {node: 'undiscovered' for node in graph.nodes}for node in graph.nodes:

if status[node] == 'undiscovered'bfs(graph, node, status)

Page 24: Lecture13–Part01 GraphSearchStrategiessierra.ucsd.edu/dsc40b-2020-sp/lectures/13-BFS-I/build/lecture.pdfTimeComplexity def full_bfs(graph): status = {node: 'undiscovered' for node

Example

Page 25: Lecture13–Part01 GraphSearchStrategiessierra.ucsd.edu/dsc40b-2020-sp/lectures/13-BFS-I/build/lecture.pdfTimeComplexity def full_bfs(graph): status = {node: 'undiscovered' for node

Observation

▶ If there are 𝑘 connected components, bfs in line5 is called exactly 𝑘 times.

1 def full_bfs(graph):2 status = {node: 'undiscovered' for node in graph.nodes}3 for node in graph.nodes:4 if status[node] == 'undiscovered'5 bfs(graph, node, status)

Page 26: Lecture13–Part01 GraphSearchStrategiessierra.ucsd.edu/dsc40b-2020-sp/lectures/13-BFS-I/build/lecture.pdfTimeComplexity def full_bfs(graph): status = {node: 'undiscovered' for node

Key Properties of full_bfs

▶ Each node added to queue exactly once.

▶ Each edge is explored exactly:▶ once if graph is directed.▶ twice if graph is undirected.

Page 27: Lecture13–Part01 GraphSearchStrategiessierra.ucsd.edu/dsc40b-2020-sp/lectures/13-BFS-I/build/lecture.pdfTimeComplexity def full_bfs(graph): status = {node: 'undiscovered' for node

Time Complexity of full_bfs

▶ Analyzing full_bfs is easier than analyzing bfs.▶ full_bfs visits all nodes, no matter the graph.

▶ Result will be upper bound on time complexityof bfs.

▶ We’ll use a aggregate analysis.

Page 28: Lecture13–Part01 GraphSearchStrategiessierra.ucsd.edu/dsc40b-2020-sp/lectures/13-BFS-I/build/lecture.pdfTimeComplexity def full_bfs(graph): status = {node: 'undiscovered' for node

BFSfrom collections import deque

def bfs(graph, source):”””Start a BFS at `source`.”””status = {node: 'undiscovered' for node in graph.nodes}

status[source] = 'pending'pending = deque([source])

# while there are still pending nodeswhile pending:

u = pending.popleft()for v in graph.neighbors(u):

# explore edge (u,v)if status[v] == 'undiscovered':

status[v] = 'pending'# append to rightpending.append(v)

status[u] = 'visited'

Page 29: Lecture13–Part01 GraphSearchStrategiessierra.ucsd.edu/dsc40b-2020-sp/lectures/13-BFS-I/build/lecture.pdfTimeComplexity def full_bfs(graph): status = {node: 'undiscovered' for node

Time Complexitydef full_bfs(graph):

status = {node: 'undiscovered' for node in graph.nodes}for node in graph.nodes:

if status[node] == 'undiscovered'bfs(graph, node, status)

def bfs(graph, source):”””Start a BFS at `source`.”””status = {node: 'undiscovered' for node in graph.nodes}

status[source] = 'pending'pending = deque([source])

# while there are still pending nodeswhile pending:

u = pending.popleft()for v in graph.neighbors(u):

# explore edge (u,v)if status[v] == 'undiscovered':

status[v] = 'pending'# append to rightpending.append(v)

status[u] = 'visited'

Page 30: Lecture13–Part01 GraphSearchStrategiessierra.ucsd.edu/dsc40b-2020-sp/lectures/13-BFS-I/build/lecture.pdfTimeComplexity def full_bfs(graph): status = {node: 'undiscovered' for node

Time Complexity of Full BFS

▶ Θ(𝑉 + 𝐸)

▶ If |𝑉| > |𝐸|: Θ(𝑉)

▶ If |𝑉| < |𝐸|: Θ(𝐸)

▶ Namely, if graph is complete: Θ(𝑉2).

▶ Namely, if graph is very sparse: Θ(𝑉).

Page 31: Lecture13–Part01 GraphSearchStrategiessierra.ucsd.edu/dsc40b-2020-sp/lectures/13-BFS-I/build/lecture.pdfTimeComplexity def full_bfs(graph): status = {node: 'undiscovered' for node

Next Time

▶ Finding shortest paths using BFS.