Skip to main content
Add classes tag
Link
JimmyHu
  • 7.6k
  • 2
  • 11
  • 48
Source Link

Graph Implementation with Dijkstra's Algorithm

Graph Interface:

import java.util.*;

public interface Graph<T> {


    boolean addEdge(T src, Edge edge);

    boolean removeEdge(T source, T destination);

    boolean addVertex(T vertex);

    boolean removeVertex(T vertex);

    void printAdjacencyList();

    List<Edge> getNeighbours(T vertex);


    Map<T, List<Edge>> getAdjacencyList();
}

Undirected Graph class:



import java.util.*;

public class UndirectedGraph<T> implements Graph<T> {
    protected final Map<T, List<Edge>> adjacencyList = new HashMap<>();

    @Override
    public boolean addVertex(T vertex) {
        if (!adjacencyList.containsKey(vertex)) {
            adjacencyList.put(vertex, new LinkedList<Edge>());
            return true;
        }
        return false;
    }

    @Override
    public boolean removeVertex(T vertex) {
        if (adjacencyList.containsKey(vertex)) {
            adjacencyList.remove(vertex);
            for (List<Edge> entry : adjacencyList.values()) {
                List<Edge> list = entry;
                for (Edge e : list) {
                    if (e.getDestination().equals(vertex)) {
                        list.remove(e);
                    }
                }
            }
            return true;
        }
        return false;
    }

    @Override
    public List<Edge> getNeighbours(T vertex) {
        return adjacencyList.get(vertex);
    }

    @Override
    public boolean addEdge(T src, Edge dst) {
        if (adjacencyList.containsKey(src) && adjacencyList.containsKey(dst.getDestination())) {
            adjacencyList.get(src).add(dst);
            adjacencyList.get(dst.getDestination()).add(new Edge(src, dst.getWeight()));
            return true;
        }
        return false;
    }


    //Prints the adjacecny list
    public void printAdjacencyList() {
        adjacencyList.entrySet().forEach(entry -> {
            System.out.println(entry.getKey() + " " + entry.getValue().toString());
        });
    }

    //Removes the edge from the list of the source and destination
    @Override
    public boolean removeEdge(T source, T destination) {
        if (adjacencyList.containsKey(source) && adjacencyList.containsKey(destination)) {
            for (Edge e : adjacencyList.get(source)) {
                if (e.getDestination().equals(destination)) {
                    adjacencyList.get(source).remove(e);
                    return true;
                }
            }
        }
        return false;
    }

    public Map<T, List<Edge>> getAdjacencyList() {
        return Collections.unmodifiableMap(adjacencyList);
    }

    @Override
    public String toString() {
        return "UndirectedGraph{" +
                "adjacencyList=" + adjacencyList +
                '}';
    }
}

Directed Graph Class:

import java.util.*;

public class DirectedGraph<T> implements Graph<T> {
    protected final Map<T, List<Edge>> adjacencyList = new HashMap<>();

    @Override
    public boolean addVertex(T vertex) {
        if (!adjacencyList.containsKey(vertex)) {
            adjacencyList.put(vertex, new LinkedList<Edge>());
            return true;
        }
        return false;
    }

    @Override
    public boolean removeVertex(T vertex) {
        if (adjacencyList.containsKey(vertex)) {
            adjacencyList.remove(vertex);
            for (List<Edge> entry : adjacencyList.values()) {
                List<Edge> list = entry;
                for (Edge e : list) {
                    if (e.getDestination().equals(vertex)) {
                        list.remove(e);
                    }
                }
            }
            return true;
        }
        return false;
    }

    @Override
    public boolean addEdge(T src, Edge dst) {
        if (adjacencyList.containsKey(src) && adjacencyList.containsKey(dst.getDestination())) {
            adjacencyList.get(src).add(dst);
            return true;
        }
        return false;
    }


    @Override
    public boolean removeEdge(T source, T destination) {
        if (adjacencyList.containsKey(source)) {
            for (Edge e : adjacencyList.get(source)) {
                if (e.getDestination().equals(destination)) {
                    adjacencyList.get(source).remove(e);
                    return true;
                }
            }
        }
        return false;
    }
    public Map<T, List<Edge>> getAdjacencyList() {
        return Collections.unmodifiableMap(adjacencyList);
    }
    public List<Edge> getNeighbours(T node) {
        return adjacencyList.get(node);
    }

    //Prints the adjacecny list
    public void printAdjacencyList() {
        adjacencyList.entrySet().forEach(entry -> {
            System.out.println(entry.getKey() + " " + entry.getValue().toString());
        });
    }


}

Edge Class:

public class Edge<T> {

    private T destination;
    private int weight;

    public Edge(T destination) {
        this.destination = destination;

    }

    public Edge(T destination, int weight) {
        this.destination = destination;
        this.weight = weight;
    }


    public T getDestination() {
        return destination;
    }

    public int getWeight() {
        return weight;
    }

    @Override
    public String toString() {
        return "Edge{" +
                "destination=" + destination +
                ", weight=" + weight +
                '}';
    }
}

Pathfind class:

import java.util.*;


public class Pathfind<T> {


    //Dijkstra's Algorithm
    public void findShortestPath(Graph graph, T source) {
        //gets the adjacency list
        Map<T, List<Edge>> adj = graph.getAdjacencyList();
        int verticesCount = adj.size();
        //Storing the distances here
        Map<Node<T>, Integer> distances = new HashMap<>();
        //Keeping track of visited vertices here
        List<T> visited = new ArrayList<>();
        PriorityQueue<Node> pq = new PriorityQueue<Node>(verticesCount, Node::compareTo);
        //Adding all vertices to distances map and setting the value of distance to max
        for (T vertex : adj.keySet()) {
            distances.put(new Node(vertex), Integer.MAX_VALUE);
        }
        distances.put(new Node(source, 0), 0);

        pq.add(new Node(source, 0));

        while (!pq.isEmpty()) {
            Node<T> node = pq.poll();
            T vertex = node.getNode();
            visited.add(vertex);

            for (Edge<T> edge : adj.get(vertex)) {
                if (!visited.contains(edge.getDestination())) {
                    int totalCost = node.getCost() + edge.getWeight();
                    T destNode = edge.getDestination();

                    if (totalCost < distances.get(new Node(destNode))) {
                        Node<T> node1 = new Node<T>(destNode, vertex);
                        distances.remove(node1);
                        distances.put(node1, totalCost);

                    }
                    pq.add(new Node(destNode, totalCost, vertex));
                }
            }

        }

        for (Node<T> node : distances.keySet()) {
            if (distances.get(node) < Integer.MAX_VALUE) {
                System.out.print(node.getNode().toString() + " ||| Cost = " + distances.get(node));

            }
            if (node.getPredecessor() != null) {
                System.out.println(" ||| Predecessor = " + node.getPredecessor().toString());
            }
        }

    }


    public class Node<T> implements Comparable<Node<T>> {
        private T node;
        private int cost;
        private T predecessor;

        public Node(T node) {
            this.node = node;
        }

        public Node(T node, int cost) {
            this.node = node;
            this.cost = cost;
        }

        public Node(T node, T predecessor) {
            this.node = node;
            this.predecessor = predecessor;
        }

        public Node(T node, int cost, T predecessor) {
            this.node = node;
            this.cost = cost;
            this.predecessor = predecessor;
        }

        public T getNode() {
            return node;
        }


        public int getCost() {
            return cost;
        }

        public T getPredecessor() {
            return predecessor;
        }

        public void setPredecessor(T predecessor) {
            this.predecessor = predecessor;
        }


        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            Node<?> node1 = (Node<?>) o;
            return Objects.equals(node, node1.node);
        }

        @Override
        public int hashCode() {
            return Objects.hash(node);
        }

        @Override
        public int compareTo(Node<T> other) {
            return Integer.compare(cost, other.getCost());
        }
    }
}

What could I have done better ? I have so much duplicated code in UndirectedGraph and DirectedGraph. I'm also planning to implement other pathfinding algorithms and visualizing them.