• Home
  • Quick Bytes
  • Algorithms
  • Java
  • iOS
  • Android
  • Certifications
  • About Me

Lets Code Them Up!

  • [Android Tutorial #1] Introduction

    August 3rd, 2017
  • [Java] Comparable vs Comparator

    July 31st, 2017

    Arrays class in Java has a sort method which takes in an array and returns the sorted array. It works well for arrays of primitive types like int, char, float, double, etc., and even for String. But returns an error when we want to sort an array of objects.

     

    Comparable

     

    Consider the following example: Suppose we have a Player class which stores the player’s name, age, weight and number of his/her wins.

     

    /**
    * Player --- store the players information
    */
    class Player{
    
    	private String name;
    	private int age;
    	private int weight;
    	private int wins;
    
    	Player(String name, int age, int weight, int wins){
    		this.name = name;
    		this.age = age;
    		this.weight = weight;
    		this.wins = wins;
    	}
    
    	//Getter method for name
    	public String getName(){
    		return name;
    	}
    
    	//Setter method for name
    	public void setName(String n){
    		name = n;
    	}
    
    	//Getter method for age
    	public int getAge(){
    		return age;
    	}
    
    	//Setter method for age
    	public void setAge(int a){
    		age = a;
    	}
    
    	//Getter method for weight
    	public int getWeight(){
    		return weight;
    	}
    
    	//Setter method for weight
    	public void setWeight(int w){
    		weight = w;
    	}
    
    	//Getter method for wins
    	public int getWins(){
    		return wins;
    	}
    
    	//Setter method for wins
    	public void setWins(int wi){
    		wins = wi;
    	}
    
    	public String toString(){
    		String msg = name + " " + age + " " + weight + " " + wins;
    		return msg; 
    	}
    }

     

    We want to sort an array of such players according to their names in ascending order. We can do this by using Arrays.sort(Object[] array) method.

     

    public static void main(String[] args){
                   Player[] players = { new Player("John", 23, 45, 5),
    				    new Player("Andy", 20, 55, 7),
    	    			    new Player("Mani", 25, 47, 2),
    	 			    new Player("Bob", 19, 49, 9),
    				    new Player("Holly", 21, 50, 2)
    							};
    
    		Arrays.sort(players);
    
                    for(Player p: players)
    			System.out.println(p.toString());
    }

     

     

    But we receive an error when we run the above code:

     

    Exception in thread "main" java.lang.ClassCastException: Player cannot be cast to java.lang.Comparable
    	at java.util.ComparableTimSort.countRunAndMakeAscending(ComparableTimSort.java:320)
    	at java.util.ComparableTimSort.sort(ComparableTimSort.java:188)
    	at java.util.Arrays.sort(Arrays.java:1246)
    	at PlayerComparable.main(PlayerComparable.java:86)
    

     

    This error occurred because we didn’t define the criteria according to which the objects must be sorted.

    To sort objects, we have to compare them on the basis of their property. And to compare objects we need to make our object implement the Comparable interface and override its compare method.

    Therefore, our class now implements the comparable interface and overrides the compare method which compares the name of the current object with the name of the passed object using the String’s compareTo method and returns the result accordingly.

     

    class Player implements Comparable<Player>{
    
    	private String name;
    	private int age;
    	private int weight;
    	private int wins;
    
    	Player(String name, int age, int weight, int wins){
    		this.name = name;
    		this.age = age;
    		this.weight = weight;
    		this.wins = wins;
    	}
    
    	//Getter method for name
    	public String getName(){
    		return name;
    	}
    
    	//Setter method for name
    	public void setName(String n){
    		name = n;
    	}
    
    	//Getter method for age
    	public int getAge(){
    		return age;
    	}
    
    	//Setter method for age
    	public void setAge(int a){
    		age = a;
    	}
    
    	//Getter method for weight
    	public int getWeight(){
    		return weight;
    	}
    
    	//Setter method for weight
    	public void setWeight(int w){
    		weight = w;
    	}
    
    	//Getter method for wins
    	public int getWins(){
    		return wins;
    	}
    
    	//Setter method for wins
    	public void setWins(int wi){
    		wins = wi;
    	}
    
    	@Override
    	public int compareTo(Player p){
    	 	return name.compareTo(p.getName());
    	}
    
    	public String toString(){
    		String msg = name + " " + age + " " + weight + " " + wins;
    		return msg; 
    	}
    }

     

    Now when we sort the array using Arrays.sort(players), the players are sorted in ascending order of their names:

     

    Andy 20 55 7
    Bob 19 49 9
    Holly 21 50 2
    John 23 45 5
    Mani 25 47 2

     

    Comparator

     

    The comparable interface allows us to sort an object based on only one property. Another way to compare objects is to create a Comparator class using the Comparator interface. This method allows us to sort based on multiple properties of an object. So, using Comparator we can sort based on age, weight, and number of wins.

    Suppose we want to sort the array on the basis of Player’s age. We can do that by creating a class AgeComparator which implements Comparator interface and overrides its compare method. In that method, we compare the ages of both objects and returns the result accordingly. Here I converted the primitive int values to Integer values and called the Integer’s compareTo method on them.

     

    class AgeComparator implements Comparator<Player> {
    
    	@Override
    	public int compare(Player a, Player b){
    		return new Integer(a.getAge()).compareTo(new Integer(b.getAge()));
    	}
    }

     

    Player[] players = { new Player("John", 23, 45, 5),
    							 new Player("Andy", 20, 55, 7),
    							 new Player("John", 25, 47, 2),
    							 new Player("Bob", 19, 49, 9),
    							 new Player("Holly", 21, 50, 2)
    							};
    		Arrays.sort(players, new AgeComparator());
    
    		for(Player p: players)
    			System.out.println(p.toString());
    

     

    Now we call Arrays.sort(array, comparator) method for the player’s array. This method now sorts the array based on ages of the players.

     

    Bob 19 49 9
    Andy 20 55 7
    Holly 21 50 2
    John 23 45 5
    John 25 47 2

     

    Similarly, we can write comparators to sort the array according to weight and wins. Example:

     

    class WeightComparator implements Comparator<Player> {
    
    	@Override
    	public int compare(Player a, Player b){
    		return new Integer(a.getWeight()).compareTo(new Integer(b.getWeight()));
    	}
    }

     

    class WinsComparator implements Comparator<Player> {
    
    	@Override
    	public int compare(Player a, Player b){
    		return new Integer(a.getWins()).compareTo(new Integer(b.getWins()));
    	}
    }

     

    We can even sort on the basis of more than one property using the comparator.  One example is to sort Players according to their names and wins. If names are same, we sort the objects in decreasing order of their wins. We can do this by first comparing the string names of two players. If names are same then only we compare the wins of two players and return the result accordingly.

     

    class NameWinsComparator implements Comparator<Player>{
    	@Override
    	public int compare(Player a, Player b){
    		String nameA = a.getName();
    		String nameB = b.getName();
    
    		if(nameA.equals(nameB))
    			return new Integer(b.getWins()).compareTo(new Integer(a.getWins()));
    		else
    			return nameA.compareTo(nameB);
    	}
    }

     

    The output for the player’s array is:

     

    Andy 20 55 7
    Bob 19 49 9
    Holly 21 50 2
    John 23 45 5
    John 25 47 2

     

    Here the last two elements have the same name John and they are sorted according to their wins in decreasing order.

    Code for this post: PlayerComparable, PlayerComparator

     

    Summary

    Based on the above discussion the comparison between comparable and comparator can be summarized as below:

    java comparable vs comparator
    Table 1: Comparable vs Comparator

    If you have any questions and suggestions, please write them in the comments below.

    Thanks for stopping by. See you in the next post.

     

     

  • Graph Search: Breadth First Search

    July 24th, 2017

    In the previous post, we discussed Depth First Search and its implementation in Java. In this post, we learn how to use Breadth First Search to find whether there exists a path between two vertices in a graph.

    Again for this post also, we will create a graph for a directed unweighted graph as an adjacency list using the concepts discussed in this previous post. Then apply BFS on it.

     

    How does Breadth First Search (BFS) work?

     

    We have source node and a destination node in a graph. Our motive is to find a path between them. So we call BFS on our source and destination. In DFS, we traverse from node to its child, then to its grandchild and so on. In BFS we go level by level. First, we go through the first node(first layer), then it’s children (second layer) followed by its grandchildren(third layer) and so on until we reach the destination or we have traversed every node.

    Here we first add the first node to a queue. Then check whether the first node in our queue is the destination. If yes,  it returns true. Else remove the node from the queue. If the node is not in the visited set, add them to the visited set. Then we add the node’s children to our queue. Repeat the above process for every node in queue. If after traversing all nodes we don’t get a path, we return false.

     

    Graph Search Breadth First Search
    Fig 1: Directed Graph

     

    Graph Search Breadth First Search 1
    Fig 2: Adjacency List of Graph in Fig 1.

     

    Consider the graph in Fig 4. We have to find whether a path exists between A and H.

    First, we check if start is destination ie A is a destination. It is not. So we move on to A’s children B and F and check them sequentially whether they are the destination (Orange Color). They are not, so we traverse B’s children followed by F’s children i.e., C, F (Green Color), and G(Yellow Color). Next we traverse C’s, F’s and G’s children. We continue in this way till we reach our destination node H.

     

    Graph Search Breadth First Search 3
    Fig. 3: BFS on the graph in FIg 1. Each color representing a level. Blue representing the source and destination node.

     

    Implementation

     

    Now we code the above concept in Java. I will break down the code into few simple steps for ease in understanding

    Step 1: Create a Node class, which stores the Node’s id of primitive type along with a LinkedList/ArrayList to store adjacency list of that node.

    Step 2: Create a HashMap/ArrayList to store the vertices of a graph as Node objects. Here we can create a global variable to store graph or locally in methods and then pass them as parameters. Till now, we created a global variable for the graph, for the next two videos we will create a graph in a method and pass it as parameters.

    Step 3: Add all the edges into the graph by updating the adjacency list of source nodes.

    Step 4: Write method to find whether a path exists between source and destination using BFS.

     

    Now we go through each step in detail:

    Step 1:

    Create a Node class, which stores the Node id/name/label of primitive type along with a LinkedList/ArrayList to store adjacency list of that node. For a detailed explanation refer to this post.

     

            /**
    	* Node --- class to store each vertex along with its adjacent vertices
    	*/
    	static class Node{		
    		
    		private String id;
    		private LinkedList<Node> adjacent;
    		
    		public Node(String id){
    			this.id = id;
    			adjacent = new LinkedList<Node>();
    		}
    		
    		//Getter method for start vertex
    		public String getId(){
    			return id;
    		}
    		
    		//Getter method for end vertex
    		public LinkedList<Node> getAdjacent(){
    			return adjacent;
    		}
    		
    		//add node to the adajcent list
    		public void addAdjacent(Node vertex){
    			adjacent.add(vertex);
    		}
    
    		//To print Node 
            public String toString(){
         		String msg = id + " : ";
            	for(Node node: adjacent)
            		msg = msg + node.id + " ";
            	return msg;
                    }
    	}

     

    Step 2:

    Create a HashMap/ArrayList to store the vertices of a graph as Node objects.

    In our example graph in Fig 1, we have string nodes. So our HashMap will have keys of type String and Values of type Node.

     

        /**
        * Creates a HashMap with string key and Node value
        * @param input list of edges
        * @return HashMap<String,Node>
        */ 
    	public static HashMap<String, Node> createGraph(String[] input){
    		HashMap<String, Node> graph = new HashMap<String, Node>();
    		Node node;
    		for(String s: input){
    			String first = String.valueOf(s.charAt(0));
    			String second = String.valueOf(s.charAt(1));
    			add(graph, first, second);
    		}
    		return graph;
    	}

     

    Step 3: 

    Add all the edges into the graph by updating the adjacency list of source nodes.

    Our input is an array of strings where each string “XY” represents an edge from X to Y. So we traverse our list and do the following three steps on each of the string values.

     

    //Input edges of the graph.
    		 {"AB","AE","BC","BF","CG","CD","EF","FG","GH","DH"};
    

     

    Part 1: First we separate X and Y from our string “XY”. We can do that by using the String’s charAt(index) method and assign them to a first and second variable. Then add the edge to our graph by calling the add method.

     

    	public static void add(HashMap<String, Node> graph, String source, String destination){
    
    		//Get nodes corresponding to source and destination vertices.
    		Node s = getNode(graph, source);
    		Node d = getNode(graph, destination);
    
    		//add nodes to adjacent list
    		s.addAdjacent(d);
    	}

     

    Part 2: We access the node corresponding to string X and Y in the graph. If there is no such node in our graph then we create one. This method getNode() does exactly that. It checks whether string K is present in our graph, if yes, then returns the corresponding node, else creates a new node with id K and adds it to our graph with key K.

     

        
        private static Node getNode(HashMap<String, Node> graph, String id){
    		if(graph.containsKey(id))
    		    return graph.get(id);
            else{
            	Node node = new Node(id);
            	graph.put(id, node);
            	return node;
            }
        }

     

    Part 3: Once we have both the nodes, we can add the destination node to source node’s adjacent list. Now our graph is created.

    Step 4:

    Write method to find whether a path exists between source and destination.

    Here we need to traverse nodes in the order they come in. So we use a LinkedList queue of type Nodes. We add them as they come and always remove the first one in the list. To keep track of visited nodes, so that we don’t go inside an infinite loop, we use a HashSet visited.

    We start by adding the first node to the queue. Then we loop over this queue until the queue becomes empty. Inside the loop, we remove the first node in the list. Then check whether the node is same as the destination. If yes, we return true. If no, we add the node to visited set if it has not been added. Next, we loop through the node’s adjacent list to add all its children to the queue. If we didn’t find destination even after traversing all nodes, we return false.

     

    public static boolean pathExists(HashMap<String, Node> graph, String source, String destination){
    
    		//To store the children of nodes visited
            LinkedList<Node> queue = new LinkedList<Node>();
    
            //To store the visited nodes
            HashSet<String> visited = new HashSet<String>();
    
            //adding node of startId in the linkedlist
    		queue.add(getNode(graph, source));	
    
    		while(!queue.isEmpty()){
    
    			Node parent = queue.remove();
    
    			//Check if current node is the destination node
    			if(parent.getId().equals(destination))
    				return true;
    
    			//add source to visited set
    		    if(visited.contains(parent.getId()))
    				continue;
    			visited.add(parent.getId());
    
    			//add children to the queue
    			for(Node children: parent.getAdjacent())
    				queue.add(children);
    		}
    
    		return false;
    	}

     

    Example

    Consider the graph in Fig 1. We will apply the above BFS method on

    1. pathExists(graph, “A”, “H”)
    2. pathExists(graph, “H”, “D”)

     

    1. pathExists(graph, “A”, “H”)

     

    Graph Search Breadth First Search 2

     

    2. pathExists(graph, “H”, “D”)

     

    Graph Search Breadth First Search 4

    The code for this post is available here.

    Try BFS on a graph and experiment with different source and destination nodes. Drop your questions and thoughts in the comments below.

    Thanks for stopping by. See you in the next post.

  • How to run an app on emulator

    July 12th, 2017
  • Graph Search: Depth First Search

    July 11th, 2017

    Graph search or finding whether one vertex is reachable from another vertex in a graph is one of the operations we can apply on graphs. There are two basic methods for this:
    1. Depth First Search
    2. Breadth First Search

    In this post, we will study Depth First Search. We will create a graph for a directed unweighted graph as an adjacency list using the concepts discussed in this previous post. Then apply DFS on it.

     

    What does reachability mean?

     

    One vertex is reachable from another vertex if and only if there exists a path between them. For example in the directed graph given below, there exists a path between 1 and 6 but not between 3 and 4.

    Graph Search Depth First Search 4
    Fig 1: Directed Graph

     

    We can either go directly from node 1 to node 6 or go from node 1 to node 5 and then to node 6. The blue color highlights the path. And the gray color highlights the lack of path between 3 and 4 in the above graph.

     

    How does Depth First Search (DFS) work?

     

    We have source node and a destination node in a graph. Our motive is to find a path between them. So we call DFS on our source and destination. DFS then checks whether there is a path from any of the node’s children to the destination. If there is a path it returns true. Else it continues on the current node’s children until either we reach the destination (return true) or we reach a node already traversed ( return false).

    The following example will clarify this more.

     

    Graph Search Depth First Search 41
    Fig 2: Path does not exist

     

    Fig 2 shows a directed unweighted graph along with adjacency list of each node. Suppose we want to find whether a path exists between 3 and 4. As per DFS, we check whether a path exists between 3’s child i.e. 6 and 4. Which further checks whether a path exists between 6’s child i.e. 2 and 4. This then checks whether a path exists between 2’s child i.e. 3 and 4. 3 is our node already traversed and so we return false. Let’s look at an example where a path exists.

     

    Graph Search Depth First Search 42
    Fig 3: Path exists

     

    Suppose we want to find whether a path exists between 1 and 6. Again, we start with 1’s child i.e 2 and call our method pathExists on 2 and 6. Then again we call method pathExists method on 2’s child i.e. 3 and 6 and then finally on 6 and 6 which returns true. This True goes all the way back to our first call and we return the final result ie True.

     

    Implementation

     

    Now we code the above concept in Java. I will break down the code into few simple steps for ease in understanding

    Step 1: Create a Node class, which stores the Node’s id of primitive type along with a LinkedList/ArrayList to store adjacency list of that node.

    Step 2: Create a hashmap/ArrayList to store the vertices of a graph as Node objects. Here we can create a global variable to store graph or locally in methods and then pass them as parameters. Till now, we created a global variable for the graph, for the next two videos we will create a graph in a method and pass it as parameters.

    Step 3: Add all the edges into the graph by updating the adjacency list of source nodes.

    Step 4: Write method to find whether a path exists between source and destination using DFS.

    Now we go through each step in detail:

    Step 1:

    Create a Node class, which stores the Node id/name/label of primitive type along with a LinkedList/ArrayList to store adjacency list of that node. For a detailed explanation refer to this post.

     

            /**
    	* Node --- class to store each vertex along with its adjacent vertices
    	*/
    	static class Node{		
    		
    		private String id;
    		private LinkedList<Node> adjacent;
    		
    		public Node(String id){
    			this.id = id;
    			adjacent = new LinkedList<Node>();
    		}
    		
    		//Getter method for start vertex
    		public String getId(){
    			return id;
    		}
    		
    		//Getter method for end vertex
    		public LinkedList<Node> getAdjacent(){
    			return adjacent;
    		}
    		
    		//add node to the adajcent list
    		public void addAdjacent(Node vertex){
    			adjacent.add(vertex);
    		}
    
    		//To print Node 
            public String toString(){
         		String msg = id + " : ";
            	for(Node node: adjacent)
            		msg = msg + node.id + " ";
            	return msg;
                    }
    	}

     

    Graph Search Depth First Search 43
    Fig 4: Directed Graph

     

    Step 2:

    Create a HashMap/ArrayList to store the vertices of a graph as Node objects.

    In our example graph in Fig 4, we have string nodes. So our HashMap will have keys of type String and Values of type Node.

     

        /**
        * Creates a HashMap with string key and Node value
        * @param input list of edges
        * @return HashMap<String,Node>
        */ 
    	public static HashMap<String, Node> createGraph(String[] input){
    		HashMap<String, Node> graph = new HashMap<String, Node>();
    		Node node;
    		for(String s: input){
    			String first = String.valueOf(s.charAt(0));
    			String second = String.valueOf(s.charAt(1));
    			add(graph, first, second);
    		}
    		return graph;
    	}

     

    Step 3: 

    Add all the edges into the graph by updating the adjacency list of source nodes.

    Our input is an array of strings where each string “XY” represents an edge from X to Y. So we traverse our list and do the following three steps on each of the string values.

     

    //Input edges of the graph.
    		String[] input = {"AB","BC","BE","CF","DE","EF"};
    

     

     

    Part 1: First we separate X and Y from our string “XY”. We can do that by using the String’s charAt(index) method and assign them to a first and second variable. Then add the edge to our graph by calling the add method.

     

    	public static void add(HashMap<String, Node> graph, String source, String destination){
    
    		//Get nodes corresponding to source and destination vertices.
    		Node s = getNode(graph, source);
    		Node d = getNode(graph, destination);
    
    		//add nodes to adjacent list
    		s.addAdjacent(d);
    	}

     

    Part 2: We access the node corresponding to string X and Y in the graph. If there is no such node in our graph then we create one. This method getNode() does exactly that. It checks whether string K is present in our graph, if yes, then returns the corresponding node, else creates a new node with id K and adds it to our graph with key K.

     

        
        private static Node getNode(HashMap<String, Node> graph, String id){
    		if(graph.containsKey(id))
    		    return graph.get(id);
            else{
            	Node node = new Node(id);
            	graph.put(id, node);
            	return node;
            }
        }

     

    Part 3: Once we have both the nodes, we can add the destination node to source node’s adjacent list. Now our graph is created.

     

    Step 4:

    Write method to find whether a path exists between source and destination.

    We know that path exists between source and destination when the source becomes equal to the destination while traversing the graph. And we also know that if we encounter a node already visited while searching the destination then we don’t have a path between source and destination. So we need to keep track of the nodes visited so far. We do that using a HashSet visited.

    We create an empty HashSet in our helper method and pass it and the source and the destination node to our main recursive method. We can get the nodes from the hashmap using getNode() method described above.

    Once we are on a node, we check whether that node exists in our visited set. If it exists, we return false (no path between source and destination). If it doesn’t exist in visited, we add the node to the visited set.

    Then we check whether the current node is equal to our destination. If yes, we have found a path, so we return true. Else we traverse the adjacent list of the current node and check if a path exists between its children and the destination node. If a path does exist we return true, else we return false.

     

        /**
        * Helper method for pathExists recursive method
        * @param HashMap<String, Node> graph 
        * @param source start index
        * @param destinations end index
        * @return true or false
        */ 
    	public static boolean pathExists(HashMap<String, Node> graph, String source, String destination){
    		HashSet<Node> visited = new HashSet<Node>();
    		return pathExists(getNode(graph, source), getNode(graph, destination), visited);
    	}
    
    	/**
        * pathExists recursive method to find path between source and destination
        * @param source start index
        * @param destinations end index
        * @param visited set to store visited nodes
        * @return true or false
        */ 
    	public static boolean pathExists(Node source, Node destination, HashSet<Node> visited){
    		if(visited.contains(source))
    			return false;
    
    		visited.add(source);
    
    		if(source == destination)
    			return true;
    
    		for(Node neighbor : source.getAdjacent()){
    			if(pathExists(neighbor, destination, visited))
    				return true;
    		}
    		return false;
    	}

     

    Example

    We see recursive calls on two examples for the graph in Fig 4.

    1. pathExists(graph, “A”, “F”)
    2. pathExists(graph, “A”, “D”)

    The adjacency list of all nodes in the graph of Fig 4 is:

    Fig 5: Adjacency list of each node in the graph of Fig 4.

    1. pathExists(graph, “A”, “F”)

     

    Graph Search Depth First Search 45
    Fig 6: pathExists(graph, “A”, “F”)

     

    2. pathExists(graph, “A”, “D”)

     

    Graph Search Depth First Search 46
    Fig 6: pathExists(graph, “A”, “D”)

    The code for this post is available here.

    Try DFS on a graph and experiment with different source and destination nodes. Drop your questions and thoughts in the comments below.

    Thanks for stopping by. See you in the next post.

  • Graph Representation: Adjacency List

    July 5th, 2017

    In the last post, we used a 2D matrix to represent the graph. But found it inefficient when our graph consists of a huge number of vertices.

    Another way to represent graph is using adjacency list. Here we store the adjacent vertices of a given vertex as a list.

    Unweighted Undirected Graph

    Graph Introduction
    Fig 1: Unweighted Undirected Graph

     

    Graph Representation Adjacency List
    Fig 2: Adjacency List

     

    Graph Representation Adjacency Matrix
    Fig 3: Adjacency Matrix

     

    In the case of the adjacency matrix, we store 1 when there is an edge between two vertices else we store infinity. If you notice, we are storing those infinity values unnecessarily, as they have no use for us. So what we can do is just store the edges from a given vertex as an array or list. To store all the vertices and their adjacent lists we can either use a hashmap or an array or a list or a set.

    A better option is to store the 1’s and skip the 0’s, which will reduce the space. We can do that by storing the adjacent nodes in a list/array of the given node.

    We can either use a hashmap or an array or a list or a set to implement graph using adjacency list.

    Consider the undirected unweighted graph in figure 1. For the vertex 1, we only store 2, 4, 5 in our adjacency list, and skip 1,3,6 (no edges to them from 1). Similarly, for vertex 2, we store 1,3,5,6 and skip 2,4. And do the same for the remaining vertices.

    For our example, we will use a hashmap with vertices id(1, 2, 3, etc) as keys and an object node storing the vertex id and its adjacency list. The adjacency list is implemented using ArrayList.

     

    private static HashMap<Integer,Node> graph = new HashMap<Integer,Node>();

     

    The class Node is like this:

     

            /**
    	* Node --- class to store each vertex along with its adjacent vertices
    	*/
    	static class Node{		
    		
    		private int id;
    		private ArrayList<Node> adjacent;
    		
    		public Node(int id){
    			this.id = id;
    			adjacent = new ArrayList<Node>();
    		}
    		
    		//Getter method for start vertex
    		public int getId(){
    			return id;
    		}
    		
    		//Getter method for end vertex
    		public ArrayList<Node> getAdjacent(){
    			return adjacent;
    		}
    		
    		//add node to the adajcent list
    		public void addAdjacent(Node vertex){
    			adjacent.add(vertex);
    		}
    
    	}

     

     

    Add an edge

    In an undirected graph if we have an edge from a to b then we also have an edge b to a. Hence for an edge {a, b} we add b to the adjacency list of a, and a to the adjacency list of b.

    Note, we have an integer value for our source and destination as input but our adjacency list is of type Node. Therefore, first, we access the node corresponding to that integer using getNode() function and then add destination node to the adjacency list by calling the Node’s addAdjacent() method.

     

            public void add(int source, int destination){
    
    		//Get nodes corresponding to source and destination vertices.
    		Node s = getNode(source);
    		Node d = getNode(destination);
    
    		//add nodes to adjacent list
    		s.addAdjacent(d);
    		d.addAdjacent(s);
    	}

     

    The getNode() method first checks whether that integer is present in our graph’s keys. If it is present it returns the node object associated with that key inside the graph. But if it isn’t present, then it creates a new node using the integer and adds it to the graph with the integer as key. Every time, a new vertex comes, it’s id is first added to the graph along with its node and then we add the adjacent vertices to the node’s adjacent list.

     

    private static Node getNode(int id){
    		if(graph.containsKey(id))
    		    return graph.get(id);
                    else{
            	    Node node = new Node(id);
            	    graph.put(id, node);
            	    return node;
                    }
    }

     

    Consider an example: Our graph is currently empty. We call the method add(1, 2). First, we create Node s, by calling the getNode(1) method. The getNode(1) method checks whether 1 is present in our graph(hashmap). Since it is not present, it creates a new node with id 1 and puts the pair (1, Node(1)) inside the hashmap and returns Node(1). A similar process is followed by 2 to get Node d. Now we have both our nodes s and d as Node(1) and Node(2). We add Node(2) to the adjacent list of Node(1) and vice versa.

     

    Find Adjacent Vertices

     

    Finding adjacent vertices inside a graph using HashMap is easy. First, we get the node corresponding to the given index using the getNode method and then return that node’s adjacent list. The whole operation takes O(1) time, as accessing an object using the key in HashMap is constant time operation.

    For example: When we call findAdjacent(1). We will get an output of [2 4 5] as an ArrayList of type nodes.

     

    public ArrayList<Node> findAdjacent(int index){
    		Node node = getNode(index);
    		return node.getAdjacent();
    }

     

    Find whether two nodes are connected

     

    To find whether two nodes source and destination are connected or not, we just need to check whether source’s adjacent list contains destination or destination’s adjacent list contains source. If either of the conditions is true, source and destination are connected.

    The time complexity of this operation is O(k), k being the number of adjacent vertices of a given vertex. The time complexity of accessing a node in HashMap is O(1) but the complexity of searching an object in ArrayList is O(n) (where n is the size of the ArrayList. In the worst case, a vertex is connected to all vertices in the graph, then the complexity of this operation becomes O(V).

    For example: When we call isConnected(1,2), first we get the Nodes(1) and Node(2) using the getNode() method. Node(1) contains its adjacent as [2, 4, 5] and Node(2) contains its adjacent as [1, 3, 5, 6]. We check whether 2 is present in Node(1)’s adjacent. We see that it is present and return true.

     

    public boolean isConnected(int source, int destination){
    		Node s = getNode(source);
    		Node d = getNode(destination);
    
    		if(s.getAdjacent().contains(d) || d.getAdjacent().contains(s))
    			return true;
    		return false;
    	}

     

    The entire code for a unweighted undirected graph is available here.

     

    Weighted Undirected Graph

    Graph Representation Edges and Vertices List 1
    Fig 4: Weighted Undirected Graph

     

    The edges have a weight in the above graph. We notice it is a bit difficult to store weight of an edge as an instance of Node’s object in the present implementation of the node. Node object just stores all the adjacent nodes and no weights. What we can do is create another class Edge which stores the endVertex along with the weight and then add that Edge object to the adjacent list of the Node.

     

            /**
    	* Edge --- class to store an edge between End vertex and weight of the edge
    	*/
    	static class Edge{		
    		
    		private int endVertex;
    		private int weight;
    		
    		public Edge(int endVertex, int weight){
    			this.endVertex = endVertex;
    			this.weight = weight;
    		}
    		
    		//Getter method for start vertex
    		public int getEndVertex(){
    			return endVertex;
    		}
    		
    		//Getter method for end vertex
    		public int getWeight(){
    			return weight;
    		}
    		
    	}
    

     

    The Node class becomes:

     

            /**
    	* Node --- class to store each vertex along with adjacent vertices and weight of the edges.
    	*/
    	static class Node{		
    		
    		private int id;
    		private ArrayList<Edge> adjacent;
    		
    		public Node(int id){
    			this.id = id;
    			adjacent = new ArrayList<Edge>();
    		}
    		
    		//Getter method for start vertex
    		public int getId(){
    			return id;
    		}
    		
    		//Getter method for end vertex
    		public ArrayList<Edge> getAdjacent(){
    			return adjacent;
    		}
    		
    		//add node to the adajcent list
    		public void addAdjacent(int endVertex, int weight){
    			adjacent.add(new Edge(endVertex,weight));
    		}
            }

     

    The implementation of the graph using the HashMap remains same. There is a little difference while adding the edge between source and destination now. As now we add the weight also. You can see the addAdjacent method of Node class. It now takes an extra parameter weight. And we create an edge object using the endVertex and weight and then add that object to our adjacent list.

    The adjacent list of Node(1) is [ (2, 5), (4, 6), (5, 10)]. Here 2, 4, 5 are the adjacent vertices and 5, 6, 10 are the weights of edges to those vertices.

     

    public void add(int source, int destination, int weight){
    
    		//Get nodes corresponding to source and destination vertices.
    		Node s = getNode(source);
    		Node d = getNode(destination);
    
    		//add nodes to adjacent list
    		s.addAdjacent(destination,weight);
    		d.addAdjacent(source,weight);
    	}

     

    The logic for finding all the adjacent vertices is still the same and can be done in O(1) time. Let’s have a look on the logic of finding whether two nodes are connected or not.

     

    public boolean isConnected(int source, int destination){
    		Node s = getNode(source);
    		Node d = getNode(destination);
    
    		ArrayList<Edge> sourceList = s.getAdjacent();
    		for(Edge edge: sourceList){
    			if(edge.endVertex == destination)
    				return true;
    		}
    		return false;
    	}

     

    Suppose we need to find whether source and destination are connected or not. Next, we access the nodes corresponding to source and destination. Then we traverse the adjacent list of edges of source node linearly and check which edge has endVertex same as the destination. If there is a match, return true, else return false.

    The entire code for the weighted undirected graph is available here.

     

    Weighted Directed Graph

    Graph Introduction 2
    Fig 5: Weighted Directed Graph

     

    Implementation for a weighted directed graph is same as that of the weighted undirected graph. The only difference is in the way we create the adjacent list for each node. Now we just add a destination to the source’s adjacent list. We do not add the source to destination’s adjacent list.

     

    public void add(int source, int destination, int weight){
    
    		//Get nodes corresponding to source and destination vertices.
    		Node s = getNode(source);
    
    		//add nodes to adjacent list
    		s.addAdjacent(destination,weight);
    	}

     

    The whole code for directed weighted graph is available here.

    Conclusion

    The space complexity of using adjacency list is O(E), improves upon O(V*V) of the adjacency matrix. (E is the total number of edges, V is the total number of vertices). We store adjacent nodes of all nodes equivalent to storing all the edges. Hence the complexity is O(E).

←Previous Page
1 … 16 17 18 19 20 … 22
Next Page→

Proudly powered by WordPress

 

Loading Comments...