Tuesday, September 2, 2014

Reverse the LinkedList in different ways in Java, iterative, recursive and others

Reversing a linked list is one of the famous interview questions, and many will stumble with this. It is not as simple as it sounds, need to take extra care while establishing the new links.

Lets assume we have a very basic "Node" class, which has one data field (int field) and one reference field to the next.

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

Also, we have a very basic LinkedList class which holds the 'head' of our LinkedList.

public class LinkedList{
   Node head;
   public void add() {
       ....
   }

   public void remove() {
       ....
   }
}

Assume, we have initialized our LinkedList with data 10->20->30->40->50, where 10 is the head. Our goal is to reverse this list, and the desired output would be 50->40->30->20->10

Recursive approach:
In general, understanding the recursion will take some time because of little complexity involved with the method stack. I am not going into too many details of the recursion here, but recursion helps us going to the very end of the list and back track from there.

public Node reverseRecursive(Node current) {
     if(current == null || current.next == null) {
          //This is our base case, we have reached the end of the list here, just return null
          return current;
     }
     //recursive call, what is this method returning here? we need to make this method return the 'head' of a so far reversed List.
     Node reversed = reverseRecursive(current.next);
     //back tracking
     //the following two lines are most important and most confusing parts of this approach. We will trace this down with the example
     current.next.next = current;
     current.next = null;
     return reversed;
}

This approach would give us the linear time and memory complexity which is O(n) in both the cases. The memory is O(n) because of the  recursive call stack.

Here, lets go step by step.

  •  How do we reveres a list with no items: we just return null (if(current == null) return current;) 
  •  How do we reverse a list with one item: we just return the one item (it is already reversed)(if(current.next == null) return current;) 
  •  How do we reverse a list with two items: Lets say our initial list 10 -> 20 -> null 
    1.  We need to reach to the end (recursive) (Node reversed = reverseRecursive(current.next);) 
    2.  Now in the stack, we have current as 10, current.next is 20 
    3.  Here we need to set 20.next = 10 correct? so current.next.next = current; 
    4.  Also, we need to remove the link between 10 and 20, that's why current.next = null; 
    5.  Last, but not least, we need to return the 'reversed', because that is our new head.
A little more explanation about those critical two lines, if somebody is still confused.
This time, lets a take a bigger list 10 -> 20 -> 30 -> 40 -> null.
stack 1 -> reverseRecursive(10) (current 10, current.next 20)
stack 2 -> reverseRecursive(20) (current 20, current.next 30)
stack 3 -> reverseRecursive(30) (current 30, current.next 40)
stack 4 -> reverseRecursive(40) (current 40, current.next null)
returns 40 (reversed is 40)
current.next.next = current; this becomes 30.next.next = 30; that means, 40.next = 30. (reversing the link)
current.next = null; this means 30.next = null 
Here our intermediate list looks like this, 10 -> 20 -> 30 <- 40. (note: both 20's next and 40's next is pointing to 30)
Here, current.next.next = current; this becomes 20.next.next = 20; that means, 30.next = 20
Intermediate list here looks like 10 -> 20 <- 30 <- 40. (note: both 10's next and 30's next is pointing to 20)
In the final call stack, our list becomes 10 <- 20 <- 30 <- 40
current.next = null makes the list look like null <- 10 <- 20 <- 30 <- 40

If we observe, our 'reversed' never changed once we initialized for the first time which was with node 40, which is our new head. Hope this will make little clear.

Iterative approach:
It is straight forward, but need to pay attention to the little details. The idea would be keep track of the previous item in each iteration, point the current's next to the previous and re initialize previous with current.

public Node reverse(Node n) {
 Node current= n;
 Node previous = null;
 while(current != null) {
  Node next = current.next; //keep the reference of the next element as we are going to set the current.next to something else (previous)
  current.next = previous; //back link, current's next should point to previous
  previous = current; // make the current as previous (new intermediate header) for next iterations
  current = next; // continue with the next
 }
 return previous; // this is our new header.
}

With this approach, we get O(n) time complexity as we need visit each element in the list once, and O(1) memory complexity as we are storing one extra "next" variable.

Look and say sequence puzzle in Java (1 11 21 1211 .....)

I recently came across with a question from one of my friends: The question is simple generate "nth" number in "Look and Say Sequence" given "1" as the initial string to start with. Here are more details about this sequence http://en.wikipedia.org/wiki/Look-and-say_sequence.

Look and Say Sequence is something like 1, 11, 21, 1211, 111221, 312211 and so on.. We need to find the nth number with in that sequence.

I thought it would be a very straight forward iterative approach and I could solve it in few minutes. However I faced few hiccups because of less paid attention.

Here is my solutions and I will explain the mistake I made in my first go.

private List<String> lookAndSayOf(int n) {
        List<String> results = new ArrayList<String>();
        results.add("1");
        for(int i=0; i<n; i++) {
            String token = results.get(i);
            char[] chars = token.toCharArray();
            String currentToken = "";
            int count = 0;
            char current = chars[0];
            for(int k=0; k<chars.length; k++) {
                if(current == chars[k]) {
                    count++;
                } else {
                    currentToken = currentToken+""+count+current;
                    count = 1;
                    current = chars[k];
                }
            }
            currentToken = currentToken+""+count+current;
            results.add(currentToken);
        }
        return results;
    }

Here two important things that we may miss in our first (I missed the second one)
- currentToken = currentToken+""+count+current; is required in both places (one is inside the loop and other is at the end of the loop. The inner one is to append the characters when there is a switch. The outer one is for the last switch. The work around to avoid the second statement (after the inner loop), add an extra space (extra character) at the end of the token something like String token = results.get(i)+" ";. This will make the loop run one extra time and we dont need the second statement.
- count = 1; We need to set the count back to 1 instead of zero.

Friday, August 29, 2014

Quick Select Algorithm implementation in Java.

Quickselect: Is an algorithm to find kth smallest or largest element in an unsorted array. More information is here http://en.wikipedia.org/wiki/Quickselect.

The Quickselect algorithm is almost same as Quicksort except we ignore one half in each recursion in Quickselect. Hence, the complexity of Quickselect is definitely lesser than O(n^2). The average complexity would be O(n) with a constant multiplication factor.

Basic idea here is to choose a pivot and alter the array such that all the left elements should be lesser or equal to the pivot and right side elements should be greater than the pivot.

There are much better algorithms to choose the appropriate pivot, but here I am taking the first element as the pivot. After an iteration moving the pivot to the center (its appropriate position).

I have added comments in critical sections of the code.


package com.rp.algos;

import java.util.Arrays;

public class QuickSelect {

    /**
     * @param args
     */
    public static void main(String[] args) {
        QuickSelect q = new QuickSelect();
        q.test();
    }
    
    private void test() {
        int[] test = {45, 34, 78, 3, 7, 14, 24};
        System.out.println("Original Array: \n" + Arrays.toString(test));
        System.out.println("------------------");
        
        int ind;
        ind = quickSelect(test, 0, test.length - 1, 0);
        System.out.println( "1st smallest = " + test[ind]); 
        ind = quickSelect(test, 0, test.length - 1, 1);
        System.out.println( "2nd smallest = " + test[ind]); 
        ind = quickSelect(test, 0, test.length - 1, 2);
        System.out.println( "3rd smallest = " + test[ind]); 
        ind = quickSelect(test, 0, test.length - 1, 6);
        System.out.println( "7th smallest = " + test[ind]); 
        
    }

    private int quickSelect(int[] a, int first, int last, int k) {
        //base case
        if(first >= last) {
            return first;
        }
        //partition the array (re arrange the array) and get the index of pivot
        int pivot = partition(a, first, last);       
        // Here it is different from Quicksort, in quick sort, we need sort both the halves ignoring the pivot (as the pivot is already sorted). 
        // In Quickselect we really interested only in one half and we ignore other half.
        // If k < pivot, our element is in the left portion  
        if(k < pivot) {
            //recursively select the pivot for the left portion
            return quickSelect(a, first, pivot, k);
        } 
        if(k > pivot) {
            return quickSelect(a, pivot+1, last, k);
        }
        return first;
    }

    
    private int partition(int[] a, int first, int last) {
        //define right and left for this recursion to re arrange the elements with in the array (infact right or left portion of the array)
        int left = first, right = last;
        //here taking the first element as the pivot
        int pivot = a[first];
        
        while (left < right) {
            //find an element which is bigger than pivot to push right portion of the array
            while (pivot >= a[left] && left < right) {
                left++;
            }
            
            //find an element from right which is lower than pivot to push it to left portion of the array
            while(pivot < a[right]) {
                right--;
            }
            
            if(left < right) {
                swap(a, left, right);
            }
        }
        //by the time we reach here, 'right' will be the seperator of left and right portion of the array. 
        //From index 1 to right all elements are lesser than the pivot and the index from right+1 to left are the higher than pivot.
        //We need to swap right and pivot to fit the pivot as the separator.
        swap(a, first, right);
        return right;
    }

    private void swap(int[] a, int left, int right) {
        int temp = a[left];
        a[left] = a[right];
        a[right] = temp;
    }
}