Monday, March 03, 2014

Sorting Algorithms

Some Notes on Sorting Algorithms...

Big O Complexity Cheat Sheet
http://bigocheatsheet.com/

Comparator Interface

When comparing two objects we'll be using the Comparator interface:
http://docs.oracle.com/javase/7/docs/api/java/util/Comparator.html

Comparator takes two arguments: left and right. Implementation is typically:

left < right = -1
left > right = 1
left = right = 0

String implements the Comparable interface and the examples below use Strings.
http://docs.oracle.com/javase/7/docs/api/java/lang/String.html

So you can compare strings like this:

String s1 = "A";
String s2 = "B";
s1.compareTo(s2);

A < B = -1
A > B = 1
A = B = 0

This could be more generic than using Strings but using a particular data type makes the examples shorter for the moment.

Terminology

Stability: When two items with the same value stay in the same relative position after sort

In Place:  Whether or not algorithm uses separate data structure to sort

Bubble Sort

Implementation:

- start at the beginning of the list
- compare the first and second items
- if the first is bigger than the second, swap
- repeat with each subsequent pair until end of list
- biggest item will be at the bottom
- start again at the beginning of the list and skip the last item as already sorted
- continue starting over at beginning of list and skipping already sorted items until all items sorted

Sample Code:

package com.radicalsoftware.example.sort;

import java.util.ArrayList;
import java.util.List;

public class BubbleSort {

    public static void main(String[] args) {
      
        ArrayList<String> list = new ArrayList<String>();
        
        list.add("Zero");
        list.add("Alpha");
        list.add("Cat");
        list.add("Boy");
        list.add("Dog");
        list.add("Rose");
        list.add("Lion");
        list.add("Fire");
        list.add("Dragon");
        list.add("Math");
        list.add("Logic");

        sort(list);
      
        for (String s: list){
            System.out.println(s);
        }
   
    }
  
    public static List<String> sort(List<String> list){
      
        assert list != null : "list cannot be null";
      
        int size = list.size();
      
        for (int pass = 1; pass < size; ++pass){
            for (int left = 0; left < (size - pass); ++left){
                int right = left + 1;
                if (list.get(left).compareTo(list.get(right)) > 0){
                    swap(list, left, right);
                }
            }
        }
      
        return list;
      
    }
  
    private static <T> void swap(List<T> list, int left, int right){
        T obj = list.get(left);
        list.set(left,  list.get(right));
        list.set(right, obj);
    }
  

}



Selection Sort

- similar to sorting books on a book shelf
- unstable
- for each spot in the list
- scan all items in list to find the smallest (or largest) item
- put the smallest (or largest) item in that spot
- move to the next spot and repeat skipping already sorted items

Sample Code:

package com.radicalsoftware.sort;

import java.util.ArrayList;
import java.util.List;

public class SelectionSort {

    public static void main(String[] args) {
       
        ArrayList<String> list = new ArrayList<String>();
        list.add("Zero");
        list.add("Alpha");
        list.add("Cat");
        list.add("Boy");
        list.add("Dog");
        list.add("Rose");
        list.add("Lion");
        list.add("Fire");
        list.add("Dragon");
        list.add("Math");
        list.add("Logic");
        sort(list);
       
        for (String s: list){
            System.out.println(s);
        }
       
   
    }
   
    public static List<String> sort(List<String> list){
       
        assert list != null : "list cannot be null";
       
        int size = list.size();
       
        for (int slot = 0; slot < size-1; ++slot){
           
            int smallest = slot;
           
            //find the smallest in remaining items
            for (int checkSlot = slot + 1; checkSlot < size; ++checkSlot){
                if (list.get(slot).compareTo(list.get(checkSlot)) > 0){
                    smallest = checkSlot;
                    break;
                }
            }
           
            //if smallest does not equal current slot then swap
            if (smallest != slot)
                swap(list, smallest, slot);
        }
       
        return list;
       
    }
   
    private static <T> void swap(List<T> list, int left, int right){
       
        T obj = list.get(left);
        list.set(left,  list.get(right));
        list.set(right, obj);
   
    }

}


Insertion Sort

- similar to sorting cards where you sort all the numbers within each suit
- create a new list and insert items into it
- use an iterator instead of an index
- insert the first item into the list
- use linked list so can scan and insert each item into correct relative location

Sample Code:

package com.radicalsoftware.sort;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

public class InsertionSort {

    public static void main(String[] args) {
      
        ArrayList<String> list = new ArrayList<String>();
      
        list.add("Zero");
        list.add("Alpha");
        list.add("Cat");
        list.add("Boy");
        list.add("Dog");
        list.add("Rose");
        list.add("Lion");
        list.add("Fire");
        list.add("Dragon");
        list.add("Math");
        list.add("Logic");
      
        List<String> sortedList = sort(list);
      
        for (String s: sortedList){
            System.out.println(s);
        }
      
   
    }
   
    public static List<String> sort(List<String> list){
      
        assert list != null : "list cannot be null";
      
        final List<String> sortedList = new LinkedList<String>();
      
        for (String s: list){
   
            int slot = sortedList.size();
      
            while (slot > 0){
                if (s.compareTo(sortedList.get(slot - 1)) >= 0){
                    break;
                }
                --slot;
            }
          
          
            sortedList.add(slot, s);
        }
      
      
        return sortedList;
      
    }
   
}


Shell Sort

- breaks large lists into smaller lists
- based on concept of H-sorting - every Hth item is sorted relative to other items
- not stable

Sample Code: 

package com.radicalsoftware.sort;

import java.util.ArrayList;
import java.util.List;

public class ShellSort {

    private static final int[] _increments = {1, 3, 5, 7, 11, 20};
   
    public static void main(String[] args) {
       
        ArrayList<String> list = new ArrayList<String>();
       
        list.add("Zero");
        list.add("Alpha");
        list.add("Cat");
        list.add("Boy");
        list.add("Dog");
        list.add("Rose");
        list.add("Lion");
        list.add("Fire");
        list.add("Dragon");
        list.add("Math");
        list.add("Logic");
        list.add("Money");
        list.add("Food");
        list.add("Stars");
        list.add("Moon");
        sort(list);
       
        for (String s: list){
            System.out.println(s);
        }
       
   
    }
   
    public static List<String> sort(List<String> list){
       
        assert list != null : "list cannot be null";
       
        for (int i = 0; i < _increments.length; i++){
            int increment = _increments[i];
            if (list.size() < increment * 2)
                break;
            hSort(list, increment);
        }
       
        return list;
       
    }
   
    private static void hSort(List<String> list, int increment){

        for (int startIndex = 0; startIndex < increment; ++startIndex){
            for (int i = startIndex + increment; i < list.size(); i += increment){
               
                String value = list.get(i);
               
                int j;
               
                for (j = i; j > startIndex; j -= increment){
                   
                    String previousValue = list.get(j - increment);
                   
                    if (value.compareTo(previousValue) >=0)
                        break;
                   
                    list.set(j, previousValue);
                   
                }
               
                list.set(j, value);
               
            }
        }
    }
   
}


Quick Sort

- uses recursion
- does the work first, then the recursion
- divide and conquer, recursively sorting smaller parts of list
- place an item in final position, smaller items left, larger items right
- divided into two parts (not necessarily two halves)
- not stable
- use quick sort until length of list is 10 or less and then use insertion sort

Best case: O(n Log(n)) -- partition element is middle element

Worst Case: O(n2) -- completely unbalanced

Implementation

- Choose partition item (last, mean of first, last and middle, etc.)
- indexes start at beginning and end (less the partition item) and advance towards each other
- left index stops when finds item larger than partitioning item
- right index stops when finds item smaller than partitioning item
- when both stop swap items
- point where indexes meet - insert partitioning item
- then sort the sublists to the right and left of the partitioning item

Sample Code:

package com.radicalsoftware.sort;

import java.util.ArrayList;
import java.util.List;

public class QuickSort {

    public static void main(String[] args) {
      
        ArrayList<String> list = new ArrayList<String>();
        list.add("Alpha");
        list.add("Zero");
        list.add("Cat");
        list.add("Boy");
        list.add("Dog");
        list.add("Rose");
        list.add("Lion");
        list.add("Fire");
        list.add("Dragon");
        list.add("Math");
        list.add("Logic");
        sort(list);
      
        for (String s: list){
            System.out.println(s);
        }
      
   
    }
   
    public static void sort(List<String> list){
      
        assert list != null : "list cannot be null";
      
        quicksort(list, 0, list.size()-1);
      
    }
   
    public static void quicksort(List<String> list, int startIndex, int endIndex){
      
        if (startIndex < 0 || endIndex >= list.size())
            return;
      
        if (endIndex <= startIndex)
            return;
      
        String value = list.get(endIndex);
      
        int partition = partition(list, value, startIndex, endIndex -1);
      
        if (list.get(partition).compareTo(value) < 0)
            ++partition;
      
        if (partition != endIndex)
            swap(list, partition, endIndex);
      
        //sort right and left of partition
        quicksort(list, startIndex, partition -1);
        quicksort(list, partition + 1, endIndex);
      
    }
   
    private static int partition(List<String> list, String value,
            int leftIndex, int rightIndex) {
        int left = leftIndex;
        int right = rightIndex;
      
        while (left < right){
          
            if(list.get(left).compareTo(value)< 0){
                ++left;
                continue;
            }
   
            if(list.get(right).compareTo(value)>=0){
                --right;
                continue;
            }
          
            swap(list, left, right);
            ++left;
        }
   
        return left;
    }

    private static void swap(List<String> list, int left, int right){
        String obj = list.get(left);
        list.set(left,  list.get(right));
        list.set(right,  obj);
    }


Merge Sort
- not in place

Implementation:

- divide the list in half recursively until you get to one item arrays
- merge each array together two at a time
-- pointer at the beginning of each two array merging
-- compare elements and put the smaller into a new array
-- move pointer of array you took element from +1
-- repeat for all elements
-- repeat for all arrays

package com.radicalsoftware.sort;

import java.util.ArrayList;
import java.util.List;

public class MergeSort {


    public static void main(String[] args) {
      
        ArrayList<String> list = new ArrayList<String>();
      
        list.add("Zero");
        list.add("Alpha");
        list.add("Cat");
        list.add("Boy");
        list.add("Dog");
        list.add("Rose");
        list.add("Lion");
        list.add("Fire");
        list.add("Dragon");
        list.add("Math");
        list.add("Logic");
        list.add("Salmon");
   
        List<String> sortedList = sort(list);

        for (String s: sortedList){
            System.out.println(s);
        }
    }
   
    public static List<String> sort(List<String> list){
      
        assert list != null : "list cannot be null";
      
        return mergesort(list, 0, list.size()-1);
      
    }

    private static List<String> mergesort(List<String> list, int startIndex, 

        int endIndex) {

      
      
        int middleIndex = (startIndex + endIndex)/2;
      
        if (startIndex < endIndex){
          
            List<String> left = mergesort(list, startIndex, middleIndex);
            List<String> right = mergesort(list, middleIndex + 1, endIndex);
          
            return merge(left, right);
      
        }
      
        List<String> mergedList = new ArrayList<String>();
        mergedList.add(list.get(startIndex));      
        return mergedList;
      
      
      
    }

    private static List<String> merge(List<String> left, List<String> right) {
      

        List<String> mergedList = new ArrayList<String>();
      
        int rightIndex = 0;
        int leftIndex = 0;
      
        while(leftIndex < left.size() || rightIndex < right.size()){

            boolean rightHasMore = rightIndex < right.size();
            boolean leftHasMore = leftIndex < left.size();
          
            if (!rightHasMore || 

                (leftHasMore && 
                left.get(leftIndex).compareTo(right.get(rightIndex)) <= 0)){
                mergedList.add(left.get(leftIndex));
                leftIndex++;
            }else{
                if(rightHasMore){
                    mergedList.add(right.get(rightIndex));
                    rightIndex++;
                }
            }  
      
          
        }
          
        return mergedList;
    }
   

}


Heap Sort
- in place (uses less memory than mergesort which has to allocate additional storage)
- comparison based sort
- selection sort family
- slower than well implemented quick sort, but better worst case
- in place
- not stable

Implementation

An array viewed as a nearly complete binary tree (meaning a subsequent level is not started until the previous level is completely filled).

http://www.youtube.com/watch?v=B7hVxCmfPtM
 

Bucket, counting and radix sort:
http://www.youtube.com/watch?v=woTbZMli6z4

 Bucket Sort
- stable sort
- range of keys can't be too big
- linear time

Counting Sort 
- stable sort
- range of keys can't be too big
- linear time

Implementation:
- histogram
- indicies
- place

Radix sort
- stable
- only good when
- use counting sort trick on the least significant digit
- not in place
- linear time
- unlimited number of keys
- uses the stability of bucket sort and counting sort