This chapter implementations of basic algorithms related to graphs.
This section provides exercises for better understanding in disjoint sets.
Update the numberOfCycles()
method that returns the number of all cycles in the graph.
Make sure to find all atomic cycles; do not count cycles that can be created by simply combining other cycles.
Write a report quiz6.pdf
that includes the followings:
Explain the worst-case complexity of the algorithm for your numberOfCycle()
method.
For the topological_sort()
method in the Graph
class, explain why the condition for the exception indicates that the graph includes a cycle.
This section describes an algorithm to detect a cycle in a graph.
A cycle in a graph can be detected by traversing through the graph:
Let us define the containsCycle()
method under the Graph
class that returns true if the graph contains any cycle; otherwise, false:
L2
: initially all vertices are not visited.
L4
: iterates until all vertices are visitied:
L5-6
: if the recursive call finds a cycle, returns true
.
What is the worst-case complexity of the
containsCycle()
method?
L2-3
: marks the target vertex visited.
L5
: for each incoming edge of the target vertex:
L6-7
: returns true if the source vertex of this edge has seen before.
L9-10
: returns true if the recursive call finds a cycle.
Why do we need to pass the new
HashSet
for every call inL5
?
This section describes how to implement a graph structure.
There are several types of graphs:
Can we represent undirected graphs using an implementation of directed graphs?
Let us create the Edge
class representing a directed edge under the graph
package:
L1
: inherits the Comparable
interface.
L2-3
: the source and target vertices are represented by unique IDs in int
.
L10
: constructor overwriting.
L14
: copy constructor.
Let us the define the init()
method, getters, and setters:
Let us override the compareTo()
method that compares this edge to another edge by their weights:
L4
: returns 1
if the weight of this edge is greater.
L5
: returns -1
if the weight of the other edge is greater.
L6
: returns 0
is the weights of both edges are the same.
Let us create the Graph
class under the graph
package:
L2
: a list of edge lists where each dimension of the outer list indicates a target vertex and the inner list corresponds to the list of incoming edges to that target vertex.
L5
: creates an empty edge list for each target vertex.
L9
: copies the list of edge lists from g
to this graph.
L13
: returns true if all incoming edge lists are empty using the allMatch()
method.
L17
: the size of the graph is the number of vertices in the graph.
For example, a graph with 3 vertices 0
, 1
, and 2
and 3 edges 0 -> 1
, 2 -> 1
, and 0 -> 2
, the incoming_edges
is as follows:
Let us define setters for edges:
L2-4
: retrieves the incoming edge list of target
, and adds the new edge to the edge list.
L9-10
: add edges to both directions.
Let us then define getters:
L6
: uses the flatMap()
method that merges the list of edge lists into one list of edges.
What is the worst-case complexity of the
getVerticesWithNoIncomingEdges()
method?
Let us define the getOutgoingEdges()
method that returns the list of outgoing edges:
L1
: returns a list of edge deque, where each dimension in the outer list represents the deque of outgoing edges for the corresponding source vertex.
L2
: generates the list of empty edge deques.
L4-7
: visits every target vertex to retrieve the incoming edge lists, and assigns edges to appropriate source vertices.
What is the worst-case complexity of the
getOutgoingEdges()
method?
This section discusses two algorithms for topological sorting.
The task of topological sorting is to sort vertices in a linear order such that for each vertex, all target vertices associated with its incoming edges must appear prior to it.
The followings demonstrate how to perform a topological sort by incoming scores, where the incoming score of a vertex is define as the sum of all source vertices:
The followings demonstrate how to perform a topological sort by depth-first traversing:
Let us define the topological_sort()
method under the Graph
class that performs topological sort by either incoming scores or depth-first search:
L2
: starts with vertices with no incoming edges.
L3
: retrieves outgoing edges of each source vertex.
L4
: consists of the list of vertices in topological order.
L6
: iterates until no more vertex to visit:
L8-9
: adds a source vertex to order
.
L12
: iterates through all the outgoing edges of the source vertex:
L17
: removes the outgoing edge from the graph.
L20-21
: adds the target vertex if it has no incoming edge left to local
.
L25-27
: adds all vertices in local
to global
.
What is the worst-case complexity of the
topological_sort()
method?