Insertion Sort

Insertion sort is a very simple sorting algorithm. It runs in O(N^2) time, which is not great as most good sorting algorithms run in O(n * lg(n)) time.

It does turn out to be a very useful though in practice because:
– It is quite efficient on small lists.
– It is stable (won’t change the relative position of elements that are the same sort value).
– It is very efficient on mostly sorted lists.
– It requires no additional memory.

The algorithm is very simple if you think about it this way:
– Iterate “i” from index 1 (0 based) to length – 1.
– In a nested loop, move from “i” backward, swapping the element from “i” downward until it is in its proper place.

import java.util.Arrays;

public class InsertionSort {
    public static void sort(int[] a) {
        int i = 1;
        while (i <a> 0 &amp;&amp; a[j-1] &gt; a[j]) {
                int tmp = a[j];
                a[j] = a[j-1];
                a[j-1] = tmp;
                j--;
            }
            ++i;
        }
    }

    public static void main(String[] args) {
        int a[] = new int[] {7, 2, 9, 1, 6, 3, 4, 5, 8};
        System.out.println(Arrays.toString(a));
        sort(a);
        System.out.println(Arrays.toString(a));
    }
}

Output

[7, 2, 9, 1, 6, 3, 4, 5, 8]
[1, 2, 3, 4, 5, 6, 7, 8, 9]

Visualization

Insertion_sort

Image taken from https://en.wikipedia.org/wiki/Insertion_sort#/media/File:Insertion_sort.gif.

Functional Permutations

 

What is a Permutation?

A permutation is a distinct combination of the same set of digits. For example, a permutation of “abc” is “bca”. A common computer problem is finding all possible permutations of a given string of characters. The string “abcd” would have 24 possible permutations to print out, for example.

Calculating Permutation Counts Directly

The number of permutations for a given set of characters is the factorial of the count. For example, “abcd” has 4 characters, so 4! = 4 * 3 * 2 * 1 = 24 permutations.

How Should We Generate Permutations?

You can generate permutations in a number of ways… but I think the easiest one is to use a functional paradigm. This is very similar to the approach we used in the Locker Number No Repeats problem here: https://coding-stream-of-consciousness.com/2018/09/19/locker-number-w-o-repeats/.

The approach is:
– Make a recursive helper method that takes in just the target string of characters.
– Have it call a private recursive method with 2 parameters; a string representing the currently selected characters (will be empty) and a string representing the remaining/unused characters (will be set to the input string) and return the count of the found permutations.
– Make the private recursive method taking the 2 parameters noted earlier.
– If no characters are left in the unused character list, we have a solution; print it. Also, return 1 to count that solution.
– Otherwise, cycle through the remaining characters; ror each, recursively call the function with (1) The current string + the current remaining character and (2) all remaining characters except the current one we are processing.
– Track and return the sum of solutions from all the recursive calls.

Java Implementation

public class FunctionalPermutation {

  //Recursive helper function - gets recursion started.
  public static int permute(String s) {
    if (s == null) return 0;
    return permute("", s);
  }

  private static int permute(String c, String r) {

    //If no letters remain in r, we have a solution.  Print
    //it and return 1 to count it.
    if (r.length() == 0) {
      System.out.println(c);
      return 1;
    }

    //Recurse once for each remaining letter
    //in r, adding each to the current string.
    //Pass all letters but the added one into
    //the recursive call as remaining letters.
    //Also, sum up the number of solutions.
    int sum = 0;
    for (int i = 0; i < r.length(); ++i) {
      sum += permute(c + r.charAt(i), 
        r.substring(0, i) + r.substring(i + 1));
    }
    return sum;
  }

  public static void main(String[] args) {
    System.out.println("Permutation Count: " + permute("abcd"));
  }
}

Output

dbca
dcab
dcba
Permutation Count: 24

Locker Number W/O Repeats

A common interview problem for programming is to generate all of the possible locker numbers without repeats.

This is a little silly in the physical world… but the basic idea is:
– You have the numbers 0-9 (so, 10 numbers).
– You can choose any of the 10 for digit 1.
– You can choose any of the remaining 9 for digit 2.
– And the pattern continues.

So, this is a combinations problem… how many ways can you choose N numbers out of 10 numbers?

Calculating it Directly

  • Basically, its a limited factorial.
  • If N = 4, we have 10 options for #1, 9 for #2, 8 for #3 and 7 for #4 = 10 * 9 * 8 * 7 = 5,040 combinations.

Listing and Counting Them in Java

This problem can be expressed very easily and directly in a functional manner. In this case, that means that we continually build up our solutions immutably, as opposed to, say, trying to keep a character array that we modify as we go.

public class LockerNumberNoRepeats {
    public static void main(String[] args) {
        System.out.println("Total combinations = " +
            permutations(4));
    }

    public static int permutations(int targetLength) {
        return permutations("", "0123456789", targetLength);
    }

    private static int permutations(String c, String r,
            int targetLength) {

        //Print the result if we've reached a full locker
        //combo of the target length.  Also, return 1 so we
        //can sum up all found combinations.
        if (c.length() == targetLength) {
            System.out.println(c);
            return 1;
        }

        //Use sum to count all the permutations we see.  Also
        //cycle through all remaining letters (r) and add each
        //to our current string (c).  Then recursively call this
        //function to continue.
        int sum = 0;
        for (int i = 0; i < r.length(); ++i) {
            sum += permutations(c + r.charAt(i),
                r.substring(0,i) + r.substring(i + 1),
                targetLength);
        }
        return sum;
    }
}

Output

9874
9875
9876
Total combinations = 5040

Interleaving Iterator

Creating an interleaving iterator is a popular interview question in the Java world.

What is an Interleaving Iterator?

An interleaving iterator is an iterator of iterators. An iterator is a class that implements the following methods (others exist but are not mandatory).

boolean hasNext();
T next();

Iterators are designed to “iterate” (cycle through) a sequence of elements of unknown length. You don’t know how many elements there are; you just know that hasNext() returns true so more elements exist (or it returns false and you’re done with the iterator).

So, an iterator of iterators is an iterator that iterates over iterators which iterate over sequences of elements of another type.

Iterators can even be potentially infinite; they may never end!

Solution Details

There are quite a few different solutions for this… but the easiest one I’ve seen leverages a queue. The basic approach is:

  • Create a member variable in the class that is a Queue.
  • On construction, populate the queue with all of the provided iterators.
  • Create a utility method (getNext() in the solution below).
    • This method is designed to get the next element if it hasn’t already been found.
    • It stores it in a member variable of the class so that we know if it has already been found.
    • The methods next() and hasNext() both defer to this method to test if there is a next element and/or retrieve the next element.
    • This is important as when using the iterator, callers will call hasNext() to see if there is a next element (which must clearly get the next element to know if it exists). We want to return that already-found element when next is called instead of accidentally skipping ahead to yet-another element.
    • Also, if I (badly) made an iterator of iterators that I knew I put 10 elements in overall, I should technically be able to just call next() 10 times without calling hasNext(), even though it is bad practice.
    • IMPORTANT – the method must basically consist of:
    • while next element isn’t found and some iterators remain in the queue:
      • Take next iterator.
      • See if it has more elements.
      • If it does, take the next element and put it back in the queue if it has more.
      • If it doesn’t have more elements, add it back to the queue.
  • The next() method just gets the next element using the utility method and returns it, or throws a NoSuchElementException if there wasn’t one.
  • The hasNext() method just returns whether or not the utility method says there are more elements.

Implementation (Java)

import java.util.*;

public class InterleavingIterator<T> implements Iterator<T> {

    private Queue<Iterator<T>> iteratorQueue;
    private T next;

    public InterleavingIterator(Iterator<Iterator<T>> masterIterator) {
        if (masterIterator == null) {
            throw new IllegalArgumentException("Iterator cannot be null.");
        }
        iteratorQueue = new ArrayDeque<>();
        masterIterator.forEachRemaining(iteratorQueue::add);
    }

    private T getNext() {
        while (next == null && !iteratorQueue.isEmpty()) {
            Iterator<T> iterator = iteratorQueue.poll();
            if (iterator.hasNext()) {
                next = iterator.next();
                if (iterator.hasNext()) {
                    iteratorQueue.add(iterator);
                }
                return next;
            }
        }
        return next;
    }

    @Override
    public boolean hasNext() {
        return getNext() != null;
    }

    @Override
    public T next() {
        T n = getNext();
        next = null;
        if (n == null) throw new NoSuchElementException();
        return n;
    }

    //Test it out!
    public static void main(String[] args) {

        //Create 4 lists with a range of test cases.
        List<String> a = Arrays.asList("a", "d", "f");
        List<String> b = Arrays.asList("b", "e", "g", "h");
        List<String> c = Collections.singletonList("c");
        List<String> d = Collections.emptyList();

        Iterator<Iterator<String>> masterIterator = Arrays.asList(
                a.iterator(),
                b.iterator(),
                c.iterator(),
                d.iterator()).iterator();

        InterleavingIterator<String> interleaving =
                new InterleavingIterator<>(masterIterator);

        while (interleaving.hasNext()) {
            System.out.println(interleaving.next());
        }
    }
}

Output

a
b
c
d
e
f
g
h

The Heap Data Structure

What is a Heap?

A heap is a specialized data structure that is designed to always yield the smallest element in O(lg(n)) time (assuming it is a min-heap; a max-heap would do the opposite.

It is logically represented as a balanced binary tree with all the elements shifted as left as possible. Practically it is represented as an array because it is actually very easy to map a left-shifted binary tree into an array. The left child of any node is index 2 * N + 1 in the array and the right child of any node is index 2 * N + 2 of the array.

The O(lg(n)) retrieval time of a min or max element makes heaps ideal to be used as a priority queue.

How Does it Work?

The key to it providing this performance is that all elements are stored in such way that every element is guaranteed to be less than either of its child elements (so, logically this means the root is the smallest element). If it was a max-heap the opposite would be true.

It accomplishes this in a pretty simple way. Let’s say we’re using a min-heap:

  • The smallest element will be at the root of the tree.
  • So, to get the next smallest element, we just:
    • Take the root of the tree.
    • Move the last element in the tree (the rightmost leaf node)
      up to where the root element was.
    • Repeatedly switch this node with any nodes below it that are
      less than it (this is called “percolating down”.
    • Taking the root takes O(1) time, and percolating down at most
      takes the height of the tree, which for a binary tree is O(lg(n)).
      So, this is how we get O(lg(n)) time for taking each element.
  • If we want to insert new elements, we basically do the opposite:
    • Add the new element at the last position of the tree.
    • Repeatedly switch it upwards if it is less than its parent.
    • This is called “percolating up” and also is O(log(n)) time since
      it is also dependent on the height of the tree.

Creating a Heap

Surprisingly, a heap can actually be constructed in linear time O(n) from an existing array of objects.

This is a little time consuming to prove, so please refer to the method here: https://www.geeksforgeeks.org/time-complexity-of-building-a-heap/.

Implementation

Here is a full sample implementation of a heap (minus the construction of a heap from an existing array; in this case we just build it up using add and remove).

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.NoSuchElementException;

public class MinHeap<T extends Comparable> {

    private List heap;

    public MinHeap() {
        heap = new ArrayList();
    }

    //Add to end, percolate up to correct position.
    public void add(T value) {
        heap.add(value);

        for (int index = heap.size() - 1; index > 0; index = index / 2) {
            int parent = index / 2;
            if (heap.get(index).compareTo(heap.get(parent)) < 0) {
                T temp = heap.get(index);
                heap.set(index, heap.get(parent));
                heap.set(parent, temp);
            }
        }
    }

    private static int getLeft(int i) { return i * 2 + 1; }
    private static int getRight(int i) { return i * 2 + 2; }

    //Take from top, move last element to root, percolate down.
    public T remove() {
        if (heap.size() == 0) throw new NoSuchElementException();
        T result = heap.get(0);

        //Remove the last element.  If this makes the heap empty,
        //then just return the result.
        T last = heap.remove(heap.size() - 1);
        if (heap.size() == 0) return result;

        //If we got here, the heap still has elements, so add the last
        //element to the root (0) and percolate down.  Percolating down
        //means swapping the root with its children recursively until
        //it is smaller than both of its children.
        heap.set(0, last);
        int i = 0;
        while (i = getRight(i)) {
                T leftChild  = heap.get(getLeft(i));
                T rightChild = heap.get(getRight(i));
                if (leftChild.compareTo(rightChild) > 0) {
                    minChildIndex = getRight(i);
                }
            }
            if (heap.get(i).compareTo(heap.get(minChildIndex)) > 0) {
                T temp = heap.get(i);
                heap.set(i, heap.get(minChildIndex));
                heap.set(minChildIndex, temp);
                i = minChildIndex;
            }
            else break;
        }

        return result;
    }

    public boolean isEmpty() { return heap.isEmpty(); }

    public static void main(String[] args) {
        MinHeap h = new MinHeap();
        Arrays.asList(7, 6, 2, 9, 8, 17, 46, 12, 13, 1, 7)
            .forEach(h::add);
        while(!h.isEmpty()) { System.out.print(h.remove() + " "); }
        System.out.println();

        Arrays.asList(7, 6, 2, 9, 8, 17, 46, 12, 13, 1, 7, 51)
            .forEach(h::add);
        while(!h.isEmpty()) { System.out.print(h.remove() + " "); }

        System.out.println();
        h.add(1);
        while(!h.isEmpty()) { System.out.print(h.remove() + " "); }
    }
}

Continue reading