arrow-left

All pages
gitbookPowered by GitBook
1 of 5

Loading...

Loading...

Loading...

Loading...

Loading...

7.1. Implementation

This section describes how to implement a graph structure.

hashtag
Types of Graphs

There are several types of graphs:

Can we represent undirected graphs using an implementation of directed graphs?

hashtag
Edge

Let us create the class representing a directed edge under the package:

  • L1: inherits the interface.

  • L2-3: the source and target vertices are represented by unique IDs in int.

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.

hashtag
Graph

Let us create the class under the 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

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 method that merges the list of edge lists into one list of edges.

  • L10: uses the and methods to find vertices with no incoming 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

What is the worst-case complexity of the getOutgoingEdges() method?

L10: constructor overwriting.
  • L14: copy constructor.

  • L6: returns 0 is the weights of both edges are the same.
    : copies the list of edge lists from
    g
    to this graph.
  • L13: returns true if all incoming edge lists are empty using the method.

  • L17: the size of the graph is the number of vertices in the graph.

  • : visits every target vertex to retrieve the incoming edge lists, and assigns edges to appropriate source vertices.
    Edgearrow-up-right
    grapharrow-up-right
    Comparablearrow-up-right
    Grapharrow-up-right
    grapharrow-up-right
    flatMap()arrow-up-right
    filter()arrow-up-right
    boxed()arrow-up-right
    public class Edge implements Comparable<Edge> {
        private int source;
        private int target;
        private double weight;
    
        public Edge(int source, int target, double weight) {
            init(source, target, weight);
        }
    
        public Edge(int source, int target) {
            this(source, target, 0);
        }
    
        public Edge(Edge edge) {
            this(edge.getSource(), edge.getTarget(), edge.getWeight());
        }
    }
    private void init(int source, int target, double weight) {
        setSource(source);
        setTarget(target);
        setWeight(weight);
    }
    
    public int getSource() { return source; }
    public int getTarget() { return target; }
    public double getWeight() { return weight; }
    
    public void setSource(int vertex) { source = vertex; }
    public void setTarget(int vertex) { target = vertex; }
    public void setWeight(double weight) { this.weight = weight; }
    public void addWeight(double weight) { this.weight += weight; }
    @Override
    public int compareTo(Edge edge) {
        double diff = weight - edge.weight;
        if (diff > 0) return 1;
        else if (diff < 0) return -1;
        else return 0;
    }
    public class Graph {
        private final List<List<Edge>> incoming_edges;
    
        public Graph(int size) {
            incoming_edges = Stream.generate(ArrayList<Edge>::new).limit(size).collect(Collectors.toList());
        }
        
        public Graph(Graph g) {
            incoming_edges = g.incoming_edges.stream().map(ArrayList::new).collect(Collectors.toList());
        }
        
        public boolean hasNoEdge() {
            return IntStream.range(0, size()).allMatch(i -> getIncomingEdges(i).isEmpty());
        }
        
        public int size() {
            return incoming_edges.size();
        }
    }
    incoming_edges.set(0, new ArrayList());
    incoming_edges.set(1, new ArrayList(new Edge(0, 1), new Edge(2, 1));
    incoming_edges.set(2, new ArrayList(new Edge(0, 2)));
    public Edge setDirectedEdge(int source, int target, double weight) {
        List<Edge> edges = getIncomingEdges(target);
        Edge edge = new Edge(source, target, weight);
        edges.add(edge);
        return edge;
    }
    
    public void setUndirectedEdge(int source, int target, double weight) {
        setDirectedEdge(source, target, weight);
        setDirectedEdge(target, source, weight);
    }
    public List<Edge> getIncomingEdges(int target) {
        return incoming_edges.get(target);
    }
    
    public List<Edge> getAllEdges() {
        return incoming_edges.stream().flatMap(List::stream).collect(Collectors.toList());
    }
    
    public Deque<Integer> getVerticesWithNoIncomingEdges() {
        return IntStream.range(0, size()).filter(i -> getIncomingEdges(i).isEmpty()).boxed().collect(Collectors.toCollection(ArrayDeque::new));
    }
    public List<Deque<Edge>> getOutgoingEdges() {
        List<Deque<Edge>> outgoing_edges = Stream.generate(ArrayDeque<Edge>::new).limit(size()).collect(Collectors.toList());
    
        for (int target = 0; target < size(); target++) {
            for (Edge incoming_edge : getIncomingEdges(target))
                outgoing_edges.get(incoming_edge.getSource()).add(incoming_edge);
        }
    
        return outgoing_edges;
    }
    allMatch()arrow-up-right

    7. Graphs

    This chapter implementations of basic algorithms related to graphs.

    hashtag
    Contents

    1. Implementation

    hashtag
    References

    Cycle Detection
    Topological Sorting
    Quiz
    Cycle Detectionarrow-up-right
    Topological Sortingarrow-up-right

    7.2. Cycle Detection

    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:

    public boolean containsCycle() {
        Deque<Integer> notVisited = IntStream.range(0, size()).boxed().collect(Collectors.toCollection(ArrayDeque::new));
    
        while (!notVisited.isEmpty()) {
            if (containsCycleAux(notVisited.poll(), notVisited, new HashSet<>()))
                return true;
        }
    
        return 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:

    Why do we need to pass the new HashSet for every call in L5?

    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.

  • private boolean containsCycleAux(int target, Deque<Integer> notVisited, Set<Integer> visited) {
        notVisited.remove(target);
        visited.add(target);
    
        for (Edge edge : getIncomingEdges(target)) {
            if (visited.contains(edge.getSource()))
                return true;
    
            if (containsCycleAux(edge.getSource(), notVisited, new HashSet<>(visited)))
                return true;
        }
    
        return false;
    }

    7.4. Quiz

    This section provides exercises for better understanding in disjoint sets.

    hashtag
    Implementation

    • Create the GraphQuizarrow-up-right class under the grapharrow-up-right package.

    • 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.

    hashtag
    Report

    Write a report quiz6.pdf that includes the followings:

    • Explain the worst-case complexity of the algorithm for your numberOfCycle() method.

    • For the method in the Graph class, explain why the condition for the exception indicates that the graph includes a cycle.

    topological_sort()arrow-up-right

    7.3. Topological Sorting

    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.

    hashtag
    By Incoming Scores

    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:

    hashtag
    By Depth-First

    The followings demonstrate how to perform a topological sort by depth-first traversing:

    hashtag
    Implementation

    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

    What is the worst-case complexity of the topological_sort() method?

    : 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.

  • public List<Integer> topological_sort(boolean depth_first) {
        Deque<Integer> global = getVerticesWithNoIncomingEdges();
        List<Deque<Edge>> outgoingEdgesAll = getOutgoingEdges();
        List<Integer> order = new ArrayList<>();
    
        while (!global.isEmpty()) {
            Deque<Integer> local = new ArrayDeque<>();
            int vertex = global.poll();
            order.add(vertex);
            Deque<Edge> outgoingEdges = outgoingEdgesAll.get(vertex);
            
            while (!outgoingEdges.isEmpty()) {
                Edge edge = outgoingEdges.poll();
    
                //Remove edge in all incomingEdges of the target vertex
                List<Edge> incomingEdges = getIncomingEdges(edge.getTarget());
                incomingEdges.remove(edge);
    
                //If the vertex has no incoming edges, add it to the local queue awaited to be added to the global deque
                if (incomingEdges.isEmpty())
                    local.add(edge.getTarget());
            }
    
            //Transfer all vertices in local to global
            while (!local.isEmpty())
                if (depth_first) global.addFirst(local.removeLast());
                else global.addLast(local.removeFirst());
        }
    
        if (!hasNoEdge()) throw new IllegalArgumentException("Cyclic graph.");
        return order;
    }