Showing posts with label Data Structure and Algorithms. Show all posts
Showing posts with label Data Structure and Algorithms. Show all posts

Friday, May 30, 2014

Producer Consumer Problem's solution in Java

From the Wikipedia definition,  the producer–consumer problem (also known as the bounded-buffer problem) is a classic example of a multi-process synchronization problem. The problem describes two processes, the producer and the consumer, who share a common, fixed-size buffer used as a queue. The producer's job is to generate a piece of data, put it into the buffer and start again. At the same time, the consumer is consuming the data (i.e., removing it from the buffer) one piece at a time. The problem is to make sure that the producer won't try to add data into the buffer if it's full and that the consumer won't try to remove data from an empty buffer.
The solution for the producer is to either go to sleep or discard data if the buffer is full. The next time the consumer removes an item from the buffer, it notifies the producer, who starts to fill the buffer again. In the same way, the consumer can go to sleep if it finds the buffer to be empty. The next time the producer puts data into the buffer, it wakes up the sleeping consumer. The solution can be reached by means of inter-process communication. An inadequate solution could result in a deadlock where both processes are waiting to be awakened. The problem can also be generalized to have multiple producers and consumers.

In this post I am sharing my producer-consumer solution written in Java.

Producer class:

import java.util.Vector;

public class MyProducer implements Runnable{
 /*taking vector to use as buffer, 
         *it will keep numbers produced by Producer 
  *and work as synchronisation object
        */
 Vector<Integer> sharedQ;
 // Size will store the maximum size of buffer
 int SIZE;
 /*
  * Constructor for Producer class
  */
 public MyProducer(Vector<Integer> sharedQ, final int size) {
  this.sharedQ =sharedQ;
  this.SIZE= size;
 }
 @Override
 public void run() {
  int i=0;
  while(true){
   i++;
   produce(i);
  }
 }
 /*
  * This method has the logic for producing numbers 
         *and synchronization with consumer
  */
 public void produce(int i) {
  //testing if buffer is already full
  while(sharedQ.size() == SIZE) {
   synchronized (sharedQ) {
    try {
     System.out.println("producer waiting...");
     //waiting until some space is made in buffer
     sharedQ.wait();
    } catch (InterruptedException e) {
     // TODO Auto-generated catch block
     e.printStackTrace();
    }
   }
  }
  //adding numbers in buffer
  synchronized(sharedQ) {
   sharedQ.add(i);
   System.out.println("Producer produced: "+i);
   //notifying other thread which are waiting on sharedQ
   sharedQ.notifyAll();
  }
  try {
   /*
    * making thread to sleep for random time after producing  
    *so as to make output more readable and understandable
    *we can set any arbitrary sleeping time
    */
   Thread.sleep(((long)((Math.random())*10000)));
  } catch (InterruptedException e) {
   e.printStackTrace();
  }
 }
}


Consumer class:

import java.util.Vector;

public class MyConsumer implements Runnable {
 
        /*taking vector to use as buffer, 
         *it will keep numbers produced by Producer 
  *and work as synchronisation object
        */ 
        Vector<Integer> sharedQ;
 
 public MyConsumer(Vector<Integer> sharedQ) {
  this.sharedQ = sharedQ;
 }
 
 @Override
 public void run() {
  /*
   * loop will go on forever
   */
  while(true) {
      consumed();
     /*
      * making thread to sleep for random time after consuming  
      *so as to make output more readable and understandable
      *we can set any arbitrary sleeping time
      */
      try {
   Thread.sleep(((long)((Math.random())*20000)));
   
      } catch (InterruptedException e) {   
   e.printStackTrace();
      }
       }
 }
 /*
  * This method has the logic for consuming numbers 
         *and synchronization with producer
  */
 public void consumed() {
  // cheacking if buffer is empty-then consumer has to wait
   while(sharedQ.isEmpty()) {
    synchronized(sharedQ) {
     try {
      System.out.println(Thread.currentThread().getName()+" waiting...");
      sharedQ.wait();
     } catch (InterruptedException e) {
      
      e.printStackTrace();
     }
     
    }
   }
   //consuimng a number from the buffer
   synchronized(sharedQ) {
      System.out.println(Thread.currentThread().getName()+" consumed "+sharedQ.remove(0));
      //notifying other thread which are waiting on sharedQ
      sharedQ.notifyAll();
   }
  }
 
}

  Test class:

import java.util.Vector;

public class MyProducerConsumerTest { 
 
 public static void main(String args[]) {
  
        /*taking vector to use as buffer, 
         *it will keep numbers produced by Producer 
  *and work as synchronisation object
         */  
        Vector<Integer> sharedQ = new Vector<Integer>();
        // Size will store the maximum size of buffer
  final int SIZE = 5;
 //Making producer and consumer thread
 Thread producer = new Thread(new MyProducer(sharedQ,SIZE),"producer");
 Thread consumer = new Thread(new MyConsumer(sharedQ),"consumer");
  
 //starting producer/consumer threads
 producer.start();
 consumer.start();
 /*
  * this join is optional- 
  * after join this main thread will never finish as consumer/producer willl run forever
  * to see effect of join we need to make consumer/producer to run for some specific time
  */
  
 try {
  producer.join();
  consumer.join();
   
 } catch (InterruptedException e) {   
  e.printStackTrace();
 }
 /*
  * this will never get printed 
  * unless we remove join call made above or finish execution of producer/consumer threads
  */
 System.out.println("all done");
 }
}

We can make this code to work for multiple producer/consumer scenario just by changing test class and creating multiple threads for producer and consumer.

Modified Test class:

import java.util.Vector;

public class MyProducerConsumerTest { 
 
 public static void main(String args[]) {
  
        /*taking vector to use as buffer, 
         *it will keep numbers produced by Producer 
  *and work as synchronisation object
         */  
        Vector<Integer> sharedQ = new Vector<Integer>();
        // Size will store the maximum size of buffer
  final int SIZE = 5; 
 
     //Making producer and consumer thread
 Thread producer1 = new Thread(new MyProducer(sharedQ,SIZE),"producer1");
        Thread producer2 = new Thread(new MyProducer(sharedQ,SIZE),"producer2"); 
        Thread producer3 = new Thread(new MyProducer(sharedQ,SIZE),"producer3");
        Thread consumer1 = new Thread(new MyConsumer(sharedQ),"consumer1");
        Thread consumer2 = new Thread(new MyConsumer(sharedQ),"consumer2");
        Thread consumer3 = new Thread(new MyConsumer(sharedQ),"consumer3");
        Thread consumer4 = new Thread(new MyConsumer(sharedQ),"consumer4"); 
  
       //starting producer/consumer threads
 producer1.start();
        producer2.start();
        producer3.start(); 
        consumer1.start();
        consumer2.start();
        consumer3.start();
       consumer4.start(); 
        /*
  * this join is optional- 
  * after join this main thread will never finish as consumer/producer willl run forever
  * to see effect of join we need to make consumer/producer to run for some specific time
  */
  
 try {
  producer1.join();
                producer2.join();
               producer3.join(); 
                consumer1.join();
                consumer2.join();
                consumer3.join();
                consumer4.join(); 
        } catch (InterruptedException e) {   
  e.printStackTrace();
 }
 /*
  * this will never get printed 
  * unless we remove join call made above or finish execution of producer/consumer threads
  */
 System.out.println("all done");
 }
}

 

 

Saturday, March 29, 2014

Java Program to merge two sorted Arrays

To merge two sorted array is one of the very commonly asked Interview question.

below is my code of merging two sorted arrays with explanation




import java.util.Arrays;
public class MergeSortedArray {  

  int [] a = {1,3,5,7,9,10};// -----> first sorted array
  int [] b = {2,4,6,8,8};   // -----> Second sorted array 

  public static void main(String[] args) {
    MergeSortedArray msa = new MergeSortedArray();
    /*

     * printing all three arrays 

     */  

  System.out.println("First Sorted Array is:");
  System.out.println(Arrays.toString(msa.a));
  System.out.println();
  System.out.println("Second Sorted Array is:");
  System.out.println(Arrays.toString(msa.b));
  System.out.println();
  System.out.println("Merged Sorted Array is:");
  System.out.println(Arrays.toString(

                            msa.getSortedMergedArray(msa.a, msa.b)));
  } 

   /*

    *Method to merged array which will be to get combined sorted array

    */

  public  int[] getSortedMergedArray(int[] a, int[] b) {  

   // defining new array of size first+second array 

    int[] mergedArray = new int[a.length + b.length];
    int i = 0, j = 0, k = 0;  

    /*

    *iterating till last element of any of the array

    * We only loop till we reach at the end of any of the two arrays

    */ 


    while (i < a.length && j < b.length) {
         if (a[i] < b[j]) {
             mergedArray[k++] = a[i++];
         }

         else {       
             mergedArray[k++] = b[j++];  
         }
     }  

     /* 

      * loop to add reaming elements of either of the remaining array  
     */  

     while (i < a.length) { 
         mergedArray[k++] = a[i++];
     }

     while (j < b.length) {
         mergedArray[k++] = b[j++];
     }

     return mergedArray;
 }
}

OUTPUT:
===========================================================================

First Sorted Array is:
[1, 3, 5, 7, 9, 10]

Second Sorted Array is:
[2, 4, 6, 8, 8]

Merged Sorted Array is:
[1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 10]

Java Program to find all combinations of a string

To find All combinations of a string is another commonly asked Interview question.

Below is my code for finding all the combinations of a given string with explanation:

public class AllStringCombinations { 
// taking String Builder object as we need to modify many time
     private StringBuilder outputString = new StringBuilder(); 
 
    private final String inputString; 
 
     /*
     *Constructor 
     */

    public AllStringCombinations( final String str ){
        inputString = str;
        System.out.println("The input string  is  : " + inputString);
    }    
    
    public static void main (String args[])
    {
        AllStringCombinations allStringCombinations =
                                    new AllStringCombinations("wxyz"); 
System.out.println("");
        System.out.println("All possible combinations are :  ");
        System.out.println("");
        allStringCombinations.getCombination(0);
    } 
     /*
      *Method to print all possible combinations recursively
      */ 

     public void getCombination(int start) { 
      
     for( int i = start; i <= inputString.length()-1; ++i ){
            outputString.append( inputString.charAt(i) );
            System.out.println( outputString );
            if ( i <= inputString.length() ) 

            // Recursive call

            getCombination( i + 1);
            outputString.setLength( outputString.length() - 1 );
        }
    }
}
 
OUTPUT:
===========================================================================
The input string  is  : wxyz

All possible combinations are :  

w
wx
wxy
wxyz
wxz
wy
wyz
wz
x
xy
xyz
xz
y
yz
z

java Program to find if two Strings are Anagrms



To find if two given Strings are Anagram of each other is very commonly asked interview Question.

Any word or phrase that exactly reproduces the letters in another order is an anagram.
for example "William Shakespeare " and  "I am a weakish speller " are Anagrams.

In a perfect anagram, every letter must be used, with exactly the same number of occurrences as in the anagrammed word or phrase.

Below is my code of checking if two given strings are anagrams.




public class Anagram { 
 
        boolean isAnagram(String s1,String s2) {       

           char a1[]= s1.toCharArray();
           char a2[]= s2.toCharArray();
           int []index1= new int[26];
           int []index2= new int[26];

           int i = 0;
           for(i=0;i<a1.length;i++) {
               index1[a1[i]-'a']++;
           }

           for(i=0;i<a2.length;i++) {
               index2[a2[i]-'a']++;           
           }

           for(i=0;i<26;i++) {
               if(index1[i]!=index2[i])
                     return false;
           }
           return true;
      }

      public static void main(String[] args) {
    
          String s1="xyzabc";
          String s2 = "abcxyz";

          Anagram anagram = new Anagram();

          if(anagram.isAnagram(s1,s2)==true)
               System.out.println(" String "+s1+" is anagram of "+s2);
          else
               System.out.println(" String "+s1+" is not anagram of "+s2);
      }
}

Output:
==================================================================
 String xyzabc is anagram of abcxyz


Wednesday, March 26, 2014

Tree Terminology

This is my quick recap guide for commonly used tree terminologies:



  • The depth of a node is the number of edges from the root to the node.
  • The height of a node is the number of edges from the node to the deepest leaf.
  • The height of a tree is a height of the root.
  • A full binary tree is a binary tree in which each node has exactly zero or two children.
  • A complete binary tree is a binary tree, which is completely filled, with the possible exception of the bottom level, which is filled from left to right.
 

 

Traversals

A traversal is a process that visits all the nodes in the tree. Since a tree is a nonlinear data structure, there is no unique traversal. We will consider several traversal algorithms with we group in the following two kinds
  • depth-first traversal
  • breadth-first traversal
There are three different types of depth-first traversals, :
  • PreOrder traversal - visit the parent first and then left and right children;
  • InOrder traversal - visit the left child, then the parent and the right child;
  • PostOrder traversal - visit left child, then the right child and then the parent;
There is only one kind of breadth-first traversal--the level order traversal. This traversal visits nodes by levels from top to bottom and from left to right.
As an example consider the following tree and its four traversals:

PreOrder - 8, 5, 9, 7, 1, 12, 2, 4, 11, 3
InOrder - 9, 5, 1, 7, 2, 12, 8, 4, 3, 11
PostOrder - 9, 1, 2, 12, 7, 5, 3, 11, 4, 8
LevelOrder - 8, 5, 4, 9, 7, 11, 1, 12, 3, 2
   
The next picture demonstrate the order of node visitation. Number 1 denote the first node in a particular traversal and 7 denote the last node.

 

  Binary Search Trees

   A BST is a binary tree where nodes are ordered in the following way:
  • each node contains one key (also known as data)
  • the keys in the left subtree are less then the key in its parent node, in short L < P;
  • the keys in the right subtree are greater the key in its parent node, in short P < R;
  • duplicate keys are not allowed.
  In the following tree all nodes in the left subtree of 10 have keys < 10 while all nodes in the right subtree > 10. Because both the left and right subtrees of a BST are again search trees; the above definition is recursively applied to all internal nodes:

Insertion


We start at the root and recursively go down the tree searching for a location in a BST to insert a new node. If the element to be inserted is already in the tree, we are done (we do not insert duplicates). The new node will always replace a NULL reference.
 

 

Searching


Searching in a BST always starts at the root. We compare a data stored at the root with the key we are searching for. If the node does not contain the key we proceed either to the left or right child depending upon comparison. If the result of comparison is negative we go to the left child, otherwise - to the right child.

 

Deletion


There are few cases to consider.
  • is not in a tree;
  • is a leaf;
  • has only one child;
  • has two children.
If  node to delete is not in the tree, there is nothing to delete. If  node to delete has only one child the procedure of deletion is identical to deleting a node from a linked list - we just bypass that node being deleted


Deletion of an internal node with two children is less straightforward. If we delete such a node, we split a tree into two subtrees and therefore, some children of the internal node won't be accessible after deletion. In the picture below we delete 8:


We replace the node being deleted with the largest node in the left subtree and then delete that largest node. By symmetry, the node being deleted can be swapped with the smallest node is the right subtree
**taken from http://www.cs.cmu.edu

Thursday, February 20, 2014

Doubly Link List In Java

In my previous post  I made a simple link list which holds integer values. Moving ahead in  this post I am creating my own doubly link list .

A doubly-linked list is a linked data structure that consists of a set of sequentially linked records called nodes. Each node contains two fields, called links, that are references to the previous and to the next node in the sequence of nodes.

The beginning and ending nodes' previous and next links, respectively, point to some kind of terminator, typically a sentinel node or null, to facilitate traversal of the list.

It can be conceptualized as two singly linked lists formed from the same data items, but in opposite sequential orders.
A doubly-linked list whose nodes contain three fields: an integer value, the link to the next node, and the link to the previous node.

A doubly-linked list whose nodes contain three fields: an integer value, the link to the next node, and the link to the previous node.

Sections in this post: 
  1. Link List Interface
  2. Link List Node
  3. Implementation

Link List Interface

Below is my Link List Interface. It is not at  all mandatory to write Interface for link list . I have written so as to make code more understandable and  to follow good code writing practices.

public interface LLInterface {
 /*
  * different overloaded insert
  */
 public boolean insertAtStart(Node nodeToInsert);
 public boolean insertAtStart(int data);
 public boolean insertAtEnd(Node nodeToInsert);
 public boolean insertAtEnd(int data);
 public boolean insert(Node nodeToInsert, int loc);
 public boolean insert(int data, int loc);

 /*
  * different overloaded delete
  */
 public Integer deleteFirst();
 public Integer deleteLast();
 public Integer delete(int loc);
 public boolean deleteThis(int data);

 /*
  * ADT method to know if list is empty
  * we will be using this method at many places such as
  * before deleting a node to make sure list is already not empty
  */
 public boolean isEmpty();

 /*
  * ADT method to print entire list
  *
  */
 public void printList(Node head);


 /* ADT method to get number of nodes present after the given node.
        *If head is passed as argument then it will return size of list
         *This method should be avoided at places such as,
         *to validate given insertion or deletion location as it
         *internally traverse complete list once.
  */
 public int getSize(Node head);
}


Link List Node

In Link list data is stored in nodes. therefore let's see how to implement a node. For the sake of simplicity I am considering nodes having only integer values but this can be changed easily.

public class Node {
 // data variable will hold integer value 
 private int data;
 //Reference(Pointer) to next node in list 
 private Node next;
 //Reference(Pointer) to previous node in list 
 private Node prev;
 
 public Node(int data) { 
  super();
  this.data = data; 
  this.next = null; 
  this.prev = null;
 }
 
 public Node(){ 
  super();
 }

 /*
  * Getter and setters 
  */
 public int getData() { 
  return data;
 } 

 public void setData(int data) { 
  this.data = data;
 }

 public Node getNext()
 {
  return next;
 }

 public void setNext(Node next) { 
  this.next = next;
 }

 public Node getPrev() { 
  return prev;
 }

 public void setPrev(Node prev) { 
  this.prev = prev;
 }
}


Implementation

package single;

public class SLL implements LLInterface {

    private Node head;

    @Override
    public boolean insertAtStart(Node nodeToInsert) {
        // Check if node is not null else return
        // We add this check as there can be situation where object is set to
        // null or
        // some one has used default constructor make the node object etc.
        if (nodeToInsert == null) {
            System.out.println("Node you are trying to insert is null");
            return false;
        }
        // check if list is empty then set node as head
        if (isEmpty()) {
            head = nodeToInsert;
        }
        // make nodeToInsert as head and head as second
        // node(nodeToInsert-->head-->....)
        else {
            nodeToInsert.setNext(head);
            head = nodeToInsert;
        }
        return true;
    }

    @Override
    public boolean insertAtStart(int data) {

        /*
         * we can have two approaches to get new node
         * 
         * First Approach: Use default constructor and then set values using
         * setters
         * 
         * Node nodeToInsert = new Node(); nodeToInsert.setData(data);
         * nodeToInsert.setNext(null);
         * 
         * Second approach: Use overloaded constructor of Node class
         */
        Node nodeToInsert = new Node(data);

        // As we already have a method to insert node at beginning having node
        // object
        // as parameter we will just call that
        // else we can add all the code of that method here itself
        return insertAtStart(nodeToInsert);

    }

    @Override
    public boolean insertAtEnd(Node nodeToInsert) {
        // Check if node is not null else return
        // We add this check as there can be situation where object is set to
        // null or
        // one has used default constructor make the node object etc.
        if (nodeToInsert == null) {
            System.out.println("Node you are trying to insert is null");
            return false;
        }
        // Check if list is empty then set node as head
        if (isEmpty()) {
            head = nodeToInsert;
            return true;
        }
        Node currentNode = head;
        while (currentNode.getNext() != null) {
            currentNode = currentNode.getNext();
        }
        currentNode.setNext(nodeToInsert);
        return true;
    }

    @Override
    public boolean insertAtEnd(int data) {

        /*
         * we can have two approaches to get new node
         * 
         * First Approach: Use default constructor and then set values using
         * setters
         * 
         * Node nodeToInsert = new Node(); nodeToInsert.setData(data);
         * nodeToInsert.setNext(null);
         * 
         * Second approach: Use overloaded constructor of Node class
         */
        Node nodeToInsert = new Node(data);

        // As we already have a method to insert node at end having node object
        // as
        // parameter we will just call that
        // else we can add all the code of that method here itself
        return insertAtEnd(nodeToInsert);
    }

    @Override
    public boolean insert(Node nodeToInsert, int loc) {
        // Check if node is not null else return
        // We add this check as there can be situation where object is set to
        // null or
        // some one has used default constructor make the node object etc.
        if (nodeToInsert == null) {
            System.out.println("Node you are trying to insert is null");
            return false;
        }
        // validate given location to add node, we wont check with list size
        // here as
        // it internally traverse complete list once
        if (loc < 1) {
            System.out.println("Invalid Location specifed to insert node");
            return false;
        }
        if (loc == 1) {
            insertAtStart(nodeToInsert);
        } else {
            Node currentNode = head;
            // Now we will reach the required location so that we can add node
            // there
            // note that we will stop one place before we want to add node and
            // keep in
            // mind current node is already at first node i.e head
            // e.g. if we want to add new node at location five, then we will
            // traverse
            // till node four
            // we will also check whether current node is not null as location
            // given
            // can be more then list size
            for (int i = 0; i < loc - 2 && currentNode != null; i++) {
                currentNode = currentNode.getNext();
            }
            if (currentNode == null) {
                System.out.println("Invalid Location specifed to insert node");
                return false;
            }
            // Now we need to insert new node at this location
            // so we will make new node's next to point to current node's next
            // and current node will point to new node
            // Note that order is important here
            nodeToInsert.setNext(currentNode.getNext());
            currentNode.setNext(nodeToInsert);
            return true;
        }
        return false;
    }

    @Override
    public boolean insert(int data, int loc) {
        /*
         * we can have two approaches to get new node
         * 
         * First Approach: Use default constructor and then set values using
         * setters
         * 
         * Node nodeToInsert = new Node(); nodeToInsert.setData(data);
         * nodeToInsert.setNext(null);
         * 
         * Second approach: Use overloaded constructor of Node class
         */
        Node nodeToInsert = new Node(data);

        // As we already have a method to insert node at end having node object
        // as parameter we will just call that
        // else we can add all the code of that method here itself
        return insert(nodeToInsert, loc);
    }

    @Override
    public Integer deleteFirst() {
        // Check if list is already Empty
        if (isEmpty()) {
            System.out.println("List is empty, can not eprform delete operation");
            return null;
        }
        /* First save head value to return then Make head to point next node in the list
         *Again order is important
        */
        int deletedNodeValue = head.getData();
        if(null != head.getNext()) {
           head.getNext().setPrev(null);
    head = head.getNext();
 }
 else {
    head = null;
 }
        return deletedNodeValue;
    }

    @Override
    public Integer deleteLast() {
        // Check if list is already Empty
        if (isEmpty()) {
            System.out.println("List is empty, can not eprform delete operation");
            return null;
        }
        Node currentNode = head;
        // TODO
        return null;
    }

    @Override
    public Integer delete(int loc) {
        // validate given location to delete node, we wont check with list size
        // here
        // as it internally traverse complete list once
        if (loc < 1) {
            System.out.println("Invalid Location specifed to delete node");
            return null;
        }
        Node currentNode = head;
        if (loc == 1) {
            return deleteFirst();
        }
        // we will also check whether current node is not null as location given
        // can
        // be can be more then list size
        for (int i = 0; i < loc - 2 && currentNode != null; i++) {
            currentNode = currentNode.getNext();
        }
        if (currentNode == null) {
            System.out.println("Invalid    Location specifed to delete node");
            return null;
        }
        int deletedNodeValue = currentNode.getData();
        currentNode.setNext(currentNode.getNext().getNext());
        return deletedNodeValue;
    }

    @Override
    public boolean deleteThis(int data) {

        if (head.getData() == data) {            
            return (deleteFirst()!= null);
        }
        // Check if list is already Empty
        // We are checking isEmpty after checking for head data as deleteFirst
        // also
        // has list empty check
        // and we do'nt want to execute this check twice by putting below
        // isEmpty
        // check at start of this method call
        if (isEmpty()) {
            System.out.println("List is empty, can not eprform delete operation");
            return false;
        }

        Node currentNode = head;

        //Iterate list until node to delete is reached or list ends
        while (currentNode.getNext() != null) {
            if (currentNode.getNext().getData() == data) {
                currentNode.setNext(currentNode.getNext().getNext());
                return true;
            }
            currentNode = currentNode.getNext();
        }
        System.out.println("data not present in the list");
        return false;
    }

    /*
     * This method will return true if List is Empty else false List will be
     * empty when head is null
     */
    @Override
    public boolean isEmpty() {
        return null == head;
    }

    @Override
    public void printList(Node head) {
        if (head == null) {

            System.out.println("Invalid node given");
            return;
        }
        Node currentNode = head;
        System.out.print("List is: [");
        while (currentNode != null) {
            System.out.print("    "+currentNode.getData());
            currentNode = currentNode.getNext();
        }
        System.out.println("    ]");
    }

    public void printList() { 
 if(head == null) { 
  System.out.println("Invalid node given"); 
  return;
 }
 Node currentNode = head;
 System.out.print("List is: [ "); 
 while(currentNode != null) {
  System.out.print(currentNode.getData() +" ");
  currentNode = currentNode.getNext();
 }
 System.out.println("]");
  }
 
     @Override
    public int getSize(Node head) {
        int count = 0;
        Node currentNode = head;
        while (currentNode != null) {
            count++;
            currentNode = currentNode.getNext();
        }
        return count;
    }

    public static void main(String[] args) {
        SLL myLst = new SLL();

        myLst.insert(new Node(41),0);
        myLst.insert(new Node(42),2);
        myLst.insert(new Node(43),1);
        myLst.insert(new Node(44),1);
        myLst.insert(new Node(45),2);
        myLst.insert(new Node(46),4);
        myLst.insert(null,3);

        myLst.insert(51,0);
        myLst.insert(52,2);
        myLst.insert(53,1);
        myLst.insert(54,1);
        myLst.insert(55,2);
        myLst.insert(56,4);

        myLst.insertAtStart(1);
        myLst.insertAtStart(2);
        myLst.insertAtStart(3);
        myLst.insertAtStart(4);

        myLst.insertAtStart(new    Node(11));
        myLst.insertAtStart(new    Node(12));
        myLst.insertAtStart(new    Node(13));
        myLst.insertAtStart(new    Node(14));
        myLst.insertAtStart(null);

        myLst.insertAtEnd(21);
        myLst.insertAtEnd(22);
        myLst.insertAtEnd(23);
        myLst.insertAtEnd(24);

        myLst.insertAtEnd(new Node(31));
        myLst.insertAtEnd(new Node(32));
        myLst.insertAtEnd(new Node(33));
        myLst.insertAtEnd(new Node(34));
        myLst.insertAtEnd(null);

        myLst.printList(myLst.head);
        System.out.println("===========================deletefirst=================");
        myLst.deleteFirst();
        myLst.deleteFirst();
        myLst.deleteFirst();
        myLst.printList(myLst.head);
        System.out.println("=========delete from location==============");        
        myLst.delete(0);
        myLst.delete(1);
        myLst.delete(30);        
        myLst.delete(2);
        myLst.printList(myLst.head);
        System.out.println("=========delete this(data)========");
        myLst.deleteThis(31);
        myLst.deleteThis(131);
        myLst.deleteThis(45);
        myLst.printList(myLst.head);
    }
}

If we run the program we will get output like this:

Output
==========================================================================
Invalid Location specifed to insert node
Invalid Location specifed to insert node
Node you are trying to insert is null
Invalid Location specifed to insert node
Node you are trying to insert, is null
Node you are trying to insert is null
List is: [14    13    12    11    4    3    2    1    54    55    53    56    44    52    45    43    46    21    22    23    24    31    32    33    34    ]
===========================delecefirst================================
List is: [11    4    3    2    1    54    55    53    56    44    52    45    43    46    21    22    23    24    31    32    33    34    ]
 ============================deleteloc================================
Invalid Location specifed to delete node
Invalid Location specifed to delete node
List is: [4    2    1    54    55    53    56    44    52    45    43    46    21    22    23    24    31    32    33    34    ]
============================deletethis(data)============================
data not present in the list
List is: [4    2    1    54    55    53    56    44    52    43    46    21    22    23    24    32    33    34    ]
 ============================deletelast================================
34
List is: [4    2    1    54    55    53    56    44    52    43    46    21    22    23    24    32    33    ]

In next post we will talk about circular link list and then we will see some commonly asked question on link list.



Link list Implementation in Java

linked list is a data structure consisting of a group of nodes which together represent a sequence. Under the simplest form, each node is composed of a data and a reference (in other words, a link) to the next node in the sequence. This structure allows for efficient insertion or removal of elements from any position in the sequence.

Singly-linked-list.svg
A linked list whose nodes contain two fields: an integer value and a link to the next node. The last node is linked to a terminator used to signify the end of the list.

Linked lists are among the simplest and most common data structures. They can be used to implement several other common abstract data types, including lists (the abstract data type), stacks, queues, associative arrays.

Sections in this post: 
  1. Link List Interface
  2. Link List Node
  3. Implementation

Link List Interface

Below is my Link List Interface. It is not at  all mandatory to write Interface for link list . I have written so as to make code more understandable and  to follow good code writing practices.

public interface LLInterface {
 /*
  * different overloaded insert
  */
 public boolean insertAtStart(Node nodeToInsert);
 public boolean insertAtStart(int data);
 public boolean insertAtEnd(Node nodeToInsert);
 public boolean insertAtEnd(int data);
 public boolean insert(Node nodeToInsert, int loc);
 public boolean insert(int data, int loc);

 /*
  * different overloaded delete
  */
 public Integer deleteFirst();
 public Integer deleteLast();
 public Integer delete(int loc);
 public boolean deleteThis(int data);

 /*
  * ADT method to know if list is empty
  * we will be using this method at many places such as
         * before deleting a node to make sure list is already not empty
  */
 public boolean isEmpty();

 /*
  * ADT method to print entire list
  *
  */
 public void printList(Node head);


 /* ADT method to get number of nodes present after the given node.
        *If head is passed as argument then it will return size of list
         *This method should be avoided at places such as,
         *to validate given insertion or deletion location as it
         *internally traverse complete list once.
  */
 public int getSize(Node head);
}


Link List Node

In Link list data is stored in nodes. therefore let's see how to implement a node. For the sake of simplicity I am considering nodes having only integer values but this can be changed easily.

public class Node {

    // data variable will hold integer
    private int data;

    // Reference(Pointer) to next node
    private Node next;

    public Node(int data) {
        super();
        this.data = data;
        this.next = null;
    }

    public Node() {
        super();
    }
    /*
     * Getter and setters
     */

    public int getData() {
        return data;
    }

    public void setData(int data) {
        this.data = data;
    }

    public Node getNext() {
        return next;
    }

    public void setNext(Node next) {
        this.next = next;
    }
}


Implementation

Now lets see how to implement link list. Whenever I am saying link list I am referring to singly link list. refer my other posts for Doubly link list and circular link list .

public class SLL implements LLInterface {

    private Node head;

    @Override
    public boolean insertAtStart(Node nodeToInsert) {
       /* Check if node is not null else return
        * We add this check as there can be situation where object is set
        * to null or some one has used default constructor make the node
        * object etc.
        */

        if (nodeToInsert == null) {
            System.out.println("Node you are trying to insert is null");
            return false;
        }

        // check if list is empty then set node as head

        if (isEmpty()) {
            head = nodeToInsert;
        }

        // make nodeToInsert as head and head as second
        // node(nodeToInsert-->head-->....)

        else {
            nodeToInsert.setNext(head);
            head = nodeToInsert;
        }
        return true;
    }

    @Override
    public boolean insertAtStart(int data) {

        /*
         * we can have two approaches to get new node
         * 
         * First Approach: Use default constructor and then set values
         * using setters
         * Node nodeToInsert = new Node();
         * nodeToInsert.setData(data);
         * nodeToInsert.setNext(null);
         *
         * Second approach: Use overloaded constructor of Node class
         */

        Node nodeToInsert = new Node(data);

        /* As we already have a method to insert node at beginning having
         * node object as parameter we will just call that
         * else we can add all the code of that method here itself
         */

        return insertAtStart(nodeToInsert);
     }

    @Override
    public boolean insertAtEnd(Node nodeToInsert) {

        /* Check if node is not null else return
         * We add this check as there can be situation where object 
        * is set to null or one has used default constructor make the node
        * object etc.
         */

        if (nodeToInsert == null) {

            System.out.println("Node you are trying to insert is null");
            return false;
        }

        // Check if list is empty then set node as head

        if (isEmpty()) {

            head = nodeToInsert;
            return true;
        }

        Node currentNode = head;
        while (currentNode.getNext() != null) {
            currentNode = currentNode.getNext();
        }

        currentNode.setNext(nodeToInsert);
        return true;
    }

    @Override
    public boolean insertAtEnd(int data) {

        /*
         * we can have two approaches to get new node 

         * First Approach: Use default constructor and then set

         * values using setters 

         * Node nodeToInsert = new Node(); 
         * nodeToInsert.setData(data);
         * nodeToInsert.setNext(null);
         * 
         * Second approach: Use overloaded constructor of Node class
         */

        Node nodeToInsert = new Node(data);

        /* As we already have a method to insert node at end having
         * node object as parameter we will just call that
         * else we can add all the code of that method here itself
         */
        
         return insertAtEnd(nodeToInsert);
    }

    @Override
    public boolean insert(Node nodeToInsert, int loc) {

        /* Check if node is not null else return
         * We add this check as there can be situation where
         * object is set to null or some one has used default constructor
         * make the node object etc.
         */
         if (nodeToInsert == null) {

            System.out.println("Node you are trying to insert is null");
            return false;
        }

        /* validate given location to add node, we wont check with list
         * size here as it internally traverse complete list once
         */
         if (loc < 1) {
           System.out.println("Invalid Location specified to insert node");
           return false;
        }

        if (loc == 1) {
            insertAtStart(nodeToInsert);
        } else {

            Node currentNode = head;

            /* Now we will reach the required location so that we can add
             * node there.Note that we will stop one place before we want
             * to add node and keep in mind current node is already at head
             * e.g. if we want to add new node at location five, then we
             * will traverse till node four.
             * We will also check whether current node is not null
             * as location given can be more then list size
             */
            
             for (int i = 0; i < loc - 2 && currentNode != null; i++) {
                currentNode = currentNode.getNext();
            }

          if (currentNode == null) {
            System.out.println("Invalid Location specified to insert node");
            return false;
          }

            /* Now we need to insert new node at this location.
             * So we make new node's next to point to current node's next
             * and current node will point to new node.
             * Note that order is important here.
             */
            nodeToInsert.setNext(currentNode.getNext());
            currentNode.setNext(nodeToInsert);

            return true;
        }

        return false;
    }

    @Override
    public boolean insert(int data, int loc) {

        /*
         * we can have two approaches to get new node
         *
         * First Approach: Use default constructor and
         * then set values using setters
         *
         * Node nodeToInsert = new Node();
         * nodeToInsert.setData(data);
         * nodeToInsert.setNext(null);
         * 
         * Second approach: Use overloaded constructor of Node class
         */

        Node nodeToInsert = new Node(data);

        /* As we already have a method to insert node at end
         * having node object as parameter we will just call that
         * else we can add all the code of that method here itself 
         */

        return insert(nodeToInsert, loc);
    }

    @Override
    public Integer deleteFirst() {

        // Check if list is already Empty

        if (isEmpty()) {
          System.out.println("List empty,can not perform delete operation");
          return null;
        }

        /* First save head value to return,then Make head to point next
         * node in the list
         * Again order is important 
         */

        int deletedNodeValue = head.getData();
        head = head.getNext();
        return deletedNodeValue;
    }

    @Override
    public Integer deleteLast() {

        //Check if list is already Empty 
 if(isEmpty()) {
  System.out.println("List is empty, return null, can not perform delete operation");  
  return null;
 }

 if(head.getNext() == null) {
  int deletedNode = head.getData() ;
  head = null;
  return deletedNode;
 }
 Node currentNode = head.getNext();
 Node prevNode = head;
 while(currentNode.getNext() != null) { 
  prevNode =currentNode;
  currentNode = currentNode.getNext();
 }
 int deletedNode = currentNode.getData(); 
 currentNode = null; 
 prevNode.setNext(null); 
 return deletedNode;
    }

    @Override
    public Integer delete(int loc) {

        /* validate given location to delete node,
         * we wont check with list size here
         * as it internally traverse complete list once
         */
        if (loc < 1) {

            System.out.println("Invalid Location specified to delete");
            return null;
        }

        Node currentNode = head;

        if (loc == 1) {
            return deleteFirst();
        }

        /* we will also check whether current node is not null
         * as location given can be can be more then list size
         */
        for (int i = 0; i < loc - 2 && currentNode != null; i++) {
            currentNode = currentNode.getNext();
        }

        if (currentNode == null) {
            System.out.println("Invalid Location specified to delete");
            return null;
        }

        int deletedNodeValue = currentNode.getData();
        currentNode.setNext(currentNode.getNext().getNext());
        
       return deletedNodeValue;

    }

    @Override
    public boolean deleteThis(int data) {

        if (head.getData() == data) {
            return (deleteFirst()!= null);
        }

        /* Check if list is already Empty
         * We are checking isEmpty after checking for head data as
         * deleteFirst also has list empty check
         * and we don't want to execute this check twice
         * by putting below isEmpty check at start of this method call
         */
        
        if (isEmpty()) {

          System.out.println("List empty,can not perform delete operation");
            return false;
        }
        Node currentNode = head;

        //Iterate list until node to delete is reached or list ends

        while (currentNode.getNext() != null) {

            if (currentNode.getNext().getData() == data) {

                currentNode.setNext(currentNode.getNext().getNext());
                return true;
            }
            currentNode = currentNode.getNext();
        }

        System.out.println("data not present in the list");
        return false;
    }

    /*
     * This method will return true if List is Empty else false.
     * List will be empty when head is null
     */

    @Override
    public boolean isEmpty() {
        return null == head;
    }

    @Override
    public void printList(Node head) {

        if (head == null) {
            System.out.println("Invalid node given");
            return;
        }

        Node currentNode = head;
        System.out.print("List is: [");

        while (currentNode != null) {
            System.out.print("    "+currentNode.getData());
            currentNode = currentNode.getNext();
        }
        System.out.println("    ]");
    }
 
     public void printList() {
 if (head == null) {

  System.out.println("Invalid node given");
  return;
 }
 Node currentNode = head;
 System.out.print("List is: [");
 while (currentNode != null) {
  System.out.print(" "+currentNode.getData());
  currentNode = currentNode.getNext();
 }
 System.out.println(" ]");
   }
 
      @Override
    public int getSize(Node head) {

        int count = 0;
        Node currentNode = head;

        while (currentNode != null) {

            count++;
            currentNode = currentNode.getNext();
        }
        return count;
    }

    public static void main(String[] args) {

        SLL myLst = new SLL();

        myLst.insert(new Node(41),0);
        myLst.insert(new Node(42),2);
        myLst.insert(new Node(43),1);
        myLst.insert(new Node(44),1);
        myLst.insert(new Node(45),2);
        myLst.insert(new Node(46),4);
        myLst.insert(null,3);
        
        myLst.insert(51,0);
        myLst.insert(52,2);
        myLst.insert(53,1);
        myLst.insert(54,1);
        myLst.insert(55,2);
        myLst.insert(56,4);

        myLst.insertAtStart(1);
        myLst.insertAtStart(2);
        myLst.insertAtStart(3);
        myLst.insertAtStart(4);

        myLst.insertAtStart(new Node(11));
        myLst.insertAtStart(new Node(12));
        myLst.insertAtStart(new Node(13));
        myLst.insertAtStart(new Node(14));

        myLst.insertAtStart(null);

        myLst.insertAtEnd(21);
        myLst.insertAtEnd(22);
        myLst.insertAtEnd(23);
        myLst.insertAtEnd(24);

        myLst.insertAtEnd(new Node(31));
        myLst.insertAtEnd(new Node(32));
        myLst.insertAtEnd(new Node(33));
        myLst.insertAtEnd(new Node(34));
        myLst.insertAtEnd(null);

        myLst.printList(myLst.head);
        System.out.println("=================deletefirst===============");

        myLst.deleteFirst();
        myLst.deleteFirst();
        myLst.deleteFirst();
        myLst.printList(myLst.head);

        System.out.println("=========delete from location==============");       

        myLst.delete(0);
        myLst.delete(1);
        myLst.delete(30);
        myLst.delete(2);

        myLst.printList(myLst.head);
        System.out.println("=========delete this(data)=================");

        myLst.deleteThis(31);
        myLst.deleteThis(131);
        myLst.deleteThis(45);

        myLst.printList(myLst.head);
    }
}
If we run the program we will get output like this:


Output
==========================================================================
Invalid Location specified to insert node
Invalid Location specified to insert node
Node you are trying to insert is null
Invalid Location specified to insert node
Node you are trying to insert is null
Node you are trying to insert is null
List is: [    14    13    12    11    4    3    2    1    54    55    53    56    44    52    45    43    46    21    22    23    24    31    32    33    34    ]
===========================deletefirst=============================
List is: [    11    4    3    2    1    54    55    53    56    44    52    45    43    46    21    22    23    24    31    32    33    34    ]
==========================delete from location========================
Invalid Location specified to delete node
Invalid Location specified to delete node
List is: [    4    2    1    54    55    53    56    44    52    45    43    46    21    22    23    24    31    32    33    34    ]
==========================delete this(data)==========================
data not present in the list
List is: [    4    2    1    54    55    53    56    44    52    43    46    21    22    23    24    32    33    34    ]

In next post we will talk about doubly Link List and Circular Link List and then some commonly asked question based on link list.