Remove Nth Node from Linked List

Remove 3rd node from below list
Input

Anbu -> Arul -> Arasu-> Bharath-> Catherine -> Davidson

Output

Anbu -> Arul -> Bharath-> Catherine -> Davidson

Idea: Traverse to the Kth Position and point the reference of temp to one next to it. We need to handle one edge case which is when the Kth position is first node then in such case the head should be changed to point temp next position.

public class RemoveNthNodeFromLL {
    public static void main(String[] args) {
        SimpleLinkedList simpleLinkedList = new SimpleLinkedList();

        simpleLinkedList.insertNodeAtEnd("Anbu");
        simpleLinkedList.insertNodeAtEnd("Arul");
        simpleLinkedList.insertNodeAtEnd("Arasu");
        simpleLinkedList.insertNodeAtEnd("Bharath");
        simpleLinkedList.insertNodeAtEnd("Catherine");
        simpleLinkedList.insertNodeAtEnd("Davidson");

        simpleLinkedList.printLinkedList();

        System.out.println("--------------After Removing 3rd Node--------------");

        removeNthNode(simpleLinkedList, 3);

        simpleLinkedList.printLinkedList();
    }


     public static void removeNthNode(SimpleLinkedList simpleLinkedList, Integer indexOfNodeToBeRemoved){
        Node temp = simpleLinkedList.head;

        //Edgecase1 : If Kth index is 1
        if(indexOfNodeToBeRemoved == 1){
            simpleLinkedList.head = temp.next;
            return;
        }

        for(int idx=1;idx<indexOfNodeToBeRemoved-1;idx++){
            temp = temp.next;
        }

        temp.next = temp.next.next;
    }
}

Output

Anbu
Arul
Arasu
Bharath
Catherine
Davidson
--------------After Removing 3rd Node--------------
Anbu
Arul
Bharath
Catherine
Davidson

Reverse a Linked List
Input

1 -> 2 -> 3 -> 4 -> 5 -> NULL 

Output

5 -> 4 -> 3 -> 2 -> 1 -> NULL 

Idea1: Using bruteforce copy the list in to Array and traverse the array backward and create new node
Idea2:

  1. Use 3 Variables. Prev, Curr, Temp and traverse through list items
  2. Start with Curr in Head and Prev as null
  3. Move forward by making Temp to Curr.next, Curr.next to Prev
  4. Prev should be set to Curr and Curr should be set to Temp

Refer Adv DSA 3 Note 5 Page 9

Previous -> Current -> Temp

Node.java

package com.mugil.org;
class Node{
	String content;
	Node next;
	Node head;
    public Node(String content){
	 this.content = content;
	}	
}

SimpleLinkedList.java

package com.mugil.org;

public class SimpleLinkedList {
    Node head;

    public void printLinkedList() {
        Node current = head;
        while (current != null) {
            System.out.println(current.content);
            current = current.next;
        }
    }
}

ReverseLinkedList.java

public class ReverseLinkedList {
    public static void main(String[] args) {
        SimpleLinkedList simpleLinkedList = new SimpleLinkedList();

        simpleLinkedList.insertNodeAtEnd("Anbu");
        simpleLinkedList.insertNodeAtEnd("Arul");
        simpleLinkedList.insertNodeAtEnd("Arasu");
        simpleLinkedList.insertNodeAtEnd("Bharath");
        simpleLinkedList.insertNodeAtEnd("Catherine");
        simpleLinkedList.insertNodeAtEnd("Davidson");

        simpleLinkedList.printLinkedList();

        reverseLinkedList(simpleLinkedList);
    }

    public static void reverseLinkedList(SimpleLinkedList simpleLinkedList){
        Node temp = simpleLinkedList.head;
        Node curr = temp;
        Node prev = null;


        while(temp != null){
            temp = curr.next;
            curr.next = prev;
            prev = curr;
            curr = temp;
        }

        simpleLinkedList.head = prev;

        System.out.println("-----------------------------------");
        simpleLinkedList.printLinkedList();
    }
}

Output

Anbu
Arul
Arasu
Bharath
Catherine
Davidson
-----------------------------------
Davidson
Catherine
Bharath
Arasu
Arul
Anbu
  1. Linked List is made of combination of Node
  2. The smallest unit of linked list is Node
  3. Node contains two things the content of the node And reference to next node
  4. If a linked list has only one element then it would have only one node

Node.java

class Node{
	String content;
	Node next;

        public Node(String content){
 	 this.content = content;
	}	
}
  1. To traverse linked list we should always start at the head
  2. After printing the content we should move to next node as mentioned in next variable in current node

Traversing a LL
LL node next always points to reference of next node if any or else would be null if only one element is present. While Traversing we always start with head position

public class Main{
	public static void main(String args[]){
		Node node1 = new Node("Anbu");
				
		Node node2 = new Node("Arul");
		node1.next = node2;

		Node node3 = new Node("Arasu");
		node2.next = node3;

		printLinkedList(node1);
	}

	public static void printLinkedList(Node startingNode){
		while(startingNode.next !=null){
			System.out.println(startingNode.content);
			startingNode = startingNode.next;
		}

		if(startingNode.next == null){
			System.out.println(startingNode.content);
		}
	}
}

The above code can be refactored as below using recursion calling same function again and again

public class Main{
	public static void main(String args[]){
		Node node1 = new Node("Anbu");
				
		Node node2 = new Node("Arul");
		node1.next = node2;

		Node node3 = new Node("Arasu");
		node2.next = node3;

		printLinkedList(node1.head);
	}
        
        public static void printLinkedList(Node startingNode){
		//Note: We are traversing to next null node before exit without this last element wont be printed
		if(startingNode == null){ 			
		  return;
		}

		System.out.println(startingNode.content);
		printLinkedList(startingNode.next);
	}
}

Output

Anbu
Arul
Arasu

Now let’s move the linked list methods to a separate class called SimpleLinkedList.java

SimpleLinkedList.java

public class SimpleLinkedList {
    Node head;

    public static void traverseLinkedList(Node startingNode){
        //Note: We are traversing to next null node before exit without this last element wont be printed
        if(startingNode == null){
            return;
        }

        System.out.println(startingNode.content);
        printLinkedList(startingNode.next);
    }
}

1.Why we are unable to add primitives as generic type
Allowed

 List<Integer> arrAges = new ArrayList<Integer>();

Not allowed

 List<int> arrAges = new ArrayList<int>();

This is to maintain backwards compatibility with previous JVM runtimes in the sense it could be referred by parent class instance Object. Generics in Java are encountered at compile time The compiler converts from generic type to right type as shown in the example below

List<ClassA> list = new ArrayList<ClassA>();
list.add(new ClassA());
ClassA a = list.get(0);

will be turned in to

List list = new ArrayList();
list.Add(new ClassA());
ClassA a = (ClassA)list.get(0);

So anything that is used as generics has to be convert able to Object (in this example get(0) returns an Object), and the primitive types aren’t. So they can’t be used in generics.

2.How to have a Ordered Collections

  1. keep the insertion order: LinkedHashSet and CopyOnWriteArraySet (thread-safe)
  2. keep the items sorted within the set: TreeSet, EnumSet (specific to enums) and ConcurrentSkipListSet (thread-safe)

Similarities between TreeMap and TreeSet in Java

  1. Both TreeMap and TreeSet are sorted data structure, which means they keep there element in predefined Sorted order. Sorting order can be natural sorting order defined by Comparable interface or custom sorting Order defined by Comparator interface. Both TreeMap and TreeSet has overloaded constructor which accept a Comparator, if provided all elements inside TreeSet or TreeMap will be compared and Sorted using this Comparator.
  2. Both TreeSet and TreeMap implements base interfaces e.g. TreeSet implements Collection and Set interface so that they can be passed to method where a Collection is expected and TreeMap implements java.util.Map interface, which means you can pass it when a Map is expected
  3. TreeSet is practically implemented using TreeMap instance, similar to HashSet which is internally backed by HashMap instance.
  4. Both TreeMap and TreeSet are non synchronized Collection, hence can not be shared between multiple threads. You can make both TreeSet and TreeMap synchronized by wrapping them into Synchronized collection by calling Collections.synchroinzedMap() method.
  5. Iterator returned by TreeMap and TreeSet are fail-fast, means they will throw ConcurrentModificationException when TreeMap or TreeSet is modified structurally once Iterator is created.
  6. Both TreeMap and TreeSet are slower than there Hash counter part like HashSet and HashMap and instead of providing constant time performance for add, remove and get operation they provide performance in O(log(n)) order.

TreeSet vs TreeMap

  1. Major difference between TreeSet and TreeMap is that TreeSet implements Set interface while TreeMap implements Map interface in Java.
  2. TreeMap and TreeSet is the way they store objects. TreeSet stores only one object while TreeMap uses two objects called key and Value. Objects in TreeSet are sorted while keys in TreeMap remain in sorted Order.
  3. former implements NavigableSet while later implements NavigableMap in Java.
  4. duplicate objects are not allowed in TreeSet but duplicates values are allowed in TreeMap.

1.How to get elements from HashMap

public static void printMap(Map mp) 
{
    Iterator it = mp.entrySet().iterator();

    while (it.hasNext()) 
    {
        Map.Entry pairs = (Map.Entry)it.next();
        System.out.println(pairs.getKey() + " = " + pairs.getValue());

        //Avoids a ConcurrentModificationException
        it.remove(); 
    }
}

2.Adding keys to HashMap Finding Next Key
To find the next key while using HashMap with Integer as Key the following function can be used.

  1. Iterate through List of Keys
  2. Sort the Keys
  3. Find the Highest value by looking into Key at size-1
  4. The next key to be used is received by adding 1 to Key(lastMaxElem) at size-1
private Integer getNextKey()
{
    List<Integer> keyList = new ArrayList<Integer>();
    int lastMaxElem = 0;
		
    HashMap WaterfallHM = (HashMap) getFromWorkFlowScope("WaterfallHM");		
    Set<Integer> keys = WaterfallHM.keySet();
        
    for ( Integer key : keys) {
	keyList.add(key);
    }
		
    Collections.sort(keyList); // Sort the arraylist
    lastMaxElem = keyList.get(keyList.size() - 1);
    lastMaxElem++; 
		
    return new Integer(lastMaxElem);
}

3.How to Initialize a Constants in HashMap

public class Test 
{
    private static final Map<Integer, String> MY_MAP = createMap();

    private static Map<Integer, String> createMap() {
        Map<Integer, String> result = new HashMap<Integer, String>();
        result.put(1, "one");
        result.put(2, "two");
        return Collections.unmodifiableMap(result);
    }
}

4.Why Map Interface doesnot extend Collections Framework
Collection assume elements of one value. Map assumes entries of key/value pairs. They could have been engineered to re-use the same common interface however some methods they implement are incompatible e.g.

Collection.remove(Object) - removes an element.
Map.remove(Object) - removes by key, not by entry.

There are some methods in common; size(), isEmpty(), clear(), putAll/addAll()

Collection interface is largely incompatible with the Map interface. If Map extended Collection, what would the add(Object) method do

5.Why need ConcurrentHashMap and CopyOnWriteArrayList
he synchronized collections classes, Hashtable, and Vector, and the synchronized wrapper classes, Collections.synchronizedMap() and Collections.synchronizedList(), provide a basic conditionally thread-safe implementation of Map and List. However, several factors make them unsuitable for use in highly concurrent applications, for example, their single collection-wide lock is an impediment to scalability and it often becomes necessary to lock a collection for a considerable time during iteration to prevent ConcurrentModificationException.ConcurrentHashMap(uses Segments) and CopyOnWriteArrayList implementations provide much higher concurrency while preserving thread safety, with some minor compromises in their promises to callers.

Why LinkedList is Faster than ArrayList?
Adding Element in middle of ArrayList requires reshuffling of other elements.Whereas in Linked list only two nodes(Nodes between which the element is added) needs to be changed.

Types of LinkedList?

  1. singly linked list
  2. doubly-linked list
  3. Circular linked list

singly linked list doubly-linked list
Pros:Simple in implementation, requires relatively lesser memory for storage, assuming you need to delete/insert (at) next node – deletion/insertion is faster. Can be iterated in forward as well as reverse direction. In case of needing to delete previous node, there is no need to traverse from head node, as the node to be deleted can be found from ‘.previous’ pointer.
Cons:Cannot be iterated in reverse, need to maintain a handle to the head node of the list else, the list will be lost in memory. If you’re deleting previous node or inserting at previous node, you will need to traverse list from head to previous node to be able to carry out those operations – O(N) time Relatively complex to implement, requires more memory for storage (1 ‘.previous’ pointer per node). Insertions and deletions are relatively more time consuming (assigning/reassigning ‘.previous’ pointer for neighbor nodes)

Time complexity of a linked list

Operation Metrics
Indexing O(n)
Inserting / Deleting at end O(1) or O(n)
Inserting / Deleting in middle O(1) with iterator O(n) with out

The time complexity for the Inserting at the end depends if you have the location of the last node, if you do, it would be O(1) other wise you will have to search through the linked list and the time complexity would jump to O(n).

  1. Internally an ArrayList uses an Object[] Array.
           private transient Object[] elementData;
         
  2. As you add items to an ArrayList, the list checks to see if the backing array has room left. If there is room, the new item is just added at the next empty space. If there is not room, a new, larger, array is created, and the old array is copied into the new one.
  3. When we actually create an arrayList following piece of code is executed –
      this.elementData=new Object[initial capacity];
         
  4. ArrayList can be created in two ways-

    List<String> myList=new ArrayList<String>();
     
  5. When we create an ArrayList in this way, default constructor is invoked and will internally create an array of Object with default size, which is 10.

    List<String> myList=new ArrayList<String>(5);
     

    When we create an ArrayList in this way, constructor with an integer argument is invoked and will internally create an array of Object with the size, specified in the constructor argument, which happens to be 5 in this case.

  6. Inside add() method Check is made, before adding element into the array it will check what is the current size of filled elements and what is the maximum size of the array. If size of filled elements is greater than maximum size of the array then size of the array must be increased. But we know that the size of the array cannot be increased dynamically.

    So what happens internally is a new Array is created with size 1.5*currentSize and the data from old Array is copied into this new Array.

Internally an ArrayList uses an Object[].

Capacity vs Size

The capacity is how many elements the list can potentially accommodate without reallocating its internal structures.

The size is the number of elements in the list

If you allocate a new array with arr = new Employee[100], the size of that array (arr.length) is going to be 100. It has 100 elements. All the elements are initially null (as this is an array of object references), but still, there are 100 elements.

If you do something like list = new ArrayList (100), and try to check list.size(), you’ll get 0. There are no elements in the list.

Internally, it’s true that the ArrayList allocates enough place to put 100 items before it needs to extend its capacity, but that’s an internal implementation detail, and the list presents its content to you as “no items stored”. Only if you actually do list.add(something), you’ll have items in the list.

So although the list allocates storage in advance, the API with which it communicates with the program tells you there are no items in it. The null items in its internal array are not available to you – you cannot retrieve them or change them.

An ArrayList is just one way to represent an abstract list, and the capacity of an ArrayList is an implementation detail of how the system implements the logical list.

An ArrayList stores the elements of a list by using an actual array “under the covers.” The actual realization of the array in computer memory has a certain size when it is allocated; this size is the ArrayList’s capacity. The ArrayList emulates a variable-sized list by storing the logical length of the list in addition to the fixed-length array. Thus if you have an ArrayList with a capacity 10 which contains 4 logical elements, the ArrayList can be represented as a length and an array

(4) | e1 | e2 | e3 | e4 | __ | __ | __| __ | __ | __ |

where the (4) is the logical length of the list and ‘__’ represent data that is ignored because it is not part of the logical list. If you attempt to access the 5th element of this ArrayList, it will throw an exception because it knows that the fifth element has not been initialized. If we then append an extra element e5 to the list, the ArrayList becomes

(5) | e1 | e2 | e3 | e4 | e5 | __ | __ | __ | __ | __ |

Note that the capacity has not changed, while the logical length has, because the underlying array is still able to handle all the data in the logical list.

If you manage to add more than ten elements to this list, the ArrayList will not break. The ArrayList is an abstraction meant to be compatible with all array operations. Rather, the ArrayList changes its capacity when its logical length exceeds its original capacity. If we were to add the elements (a1, a2, …, a7) to the above list, the resulting ArrayList might look like

(12) | e1 | e2 | e3 | e4 | e5 | a1 | a2 | a3 | a4 | a5 | a6 | a7 | __ | __ | __ | __ | __ | __ | __ | __ |

with a capacity of 20.

Once you have created an ArrayList, you can ignore the capacity in all programming that follows; the logic is unaffected. However, the performance of the system under certain kinds of operations can be affected. Increasing the capacity, for instance, might well involved allocating a larger array, copying the first array into the second and then performing the operations. This can be quite slow compared to, e.g. the same operation on a linked list. Thus it is sensible to choose the capacity of an ArrayList to be bigger than, or at least comparable to, the actual number of elements expected in the real runtime environment.

Is there a Way to Create ArrayList of Fixed size in Java

 List<String> fixedSizeList = Arrays.asList(new String[100]);

You cannot insert new Strings to the fixedSizeList (it already has 100 elements). You can only set its values like this:

fixedSizeList.set(7, "new value");

What would be the Output of the Following Code

List<Employee> employees = new ArrayList<>(100);
int size = employes.size();

size will be 0 while the initial capacity is 100.

modcount of the list lets you know if there has been a structural modification made that might cause the current operation to give incorrect results.

  List<String> arrNames = new ArrayList<String>();
  arrNames.add("Mugil");
  arrNames.add("Vinu");
  arrNames.add("Bala");
  arrNames.add("Madhu");		
  arrNames.remove(0);		
  arrNames.add("Swathi");		
  arrNames.remove(1);

When the control reaches
Line 6 The Size and Modcount of the arrNames would be 4
Line 7 The Size and Modcount would be 5 and size would be 3
Line 7 The Size and Modcount would be 6 and size would be 4

Fine the Screenshots below for further Details

Set

  • Set prevents duplication.The common use of set is to check for duplication
  • Since its is helpful for lookups for duplicate value HashSet provides an optimized implementation
  • If you want the result to be sorted use TreeSet instead of HashSet
Posted in Set.