Question: Design a linear-time certification algorithm to check whether an array pq[] is a min-oriented heap.

Answer:
public static boolean isMinHeap(Comparable[] pq, int n) {
    for (int k = 1; k <= n / 2; k++) {
        if (2 * k <= n && greater(pq[k], pq[2 * k])) {
            return false;
        }
        if (2 * k + 1 <= n && greater(pq[k], pq[2 * k + 1])) {
            return false;
        }
    }
    return true;
}

private static boolean greater(Comparable a, Comparable b) {
    return a.compareTo(b) > 0;
}

This is linear time because it checks each parent-child heap-order relation once. Equal keys are accepted, as they should be in a min-oriented heap.


Question: Prove that sink-based heap construction uses fewer than 2N compares and fewer than N exchanges.

Answer:
2.4.20

Sink-based heap construction uses fewer than 2N compares and fewer than N exchanges.

Proof: It suffices to prove that sink-based heap construction uses fewer than n exchanges because the number of compares is at most twice the number of exchanges.
For simplicity, assume that the binary heap is perfect (i.e., a binary tree in which every level is completely filled) and has height h.

                        4
           3                         3
      2           2             2            2
  1     1     1       1     1      1       1    1
 0 0   0 0   0 0     0 0   0 0    0 0     0 0  0 0

The values are the height of the subtrees rooted in each node.

We define the height of a node in a tree to be the height of the subtree rooted at that node. A key at height k can be exchanged with at most k keys beneath it when it is sunk down.
Since there are 2^h-k nodes at height k, the total number of exchanges is at most:
h + 2(h - 1) + 4(h - 2) + 8(h - 3) + ... + 2^h(0) = 2^(h+1) - h - 2
= N - h - 1 < N

The first equality is for a nonstandard sum, but it is straightforward to verify that the formula holds via mathematical induction. The second equality holds because a perfect binary tree of height h has 2^(h+1) - 1 nodes.

When the binary tree is not perfect, the result still holds by using the same analysis and considering the fact that the number of nodes at height k in a binary heap on n nodes is at most ceil(n / 2^(k+1)).

Alternate solution: Again, for simplicity, assume that the binary heap is perfect (i.e., a binary tree in which every level is completely filled). We define the height of a node in a tree to be the height of the subtree rooted at that node.

* First, observe that a binary heap on n nodes has n - 1 links (because each link is the parent of one node and every node has a parent link except the root).
* Sinking a node of height k requires at most k exchanges.
* We will charge k links to each node at height k, but not necessarily the links on the path taken when sinking the node. Instead, we charge the node the k links along the path from the node that goes left-right-right-right-...
* Note that no link is charged to more than one node. (In fact, there are two links not charged to any node: the right link from the root and the parent link from the bottom rightmost node).
* Thus, the total number of exchanges is at most n. Since there are at most 2 compares per exchange, the number of compares is at most 2n.

Reference: http://algs4.cs.princeton.edu/24pq/


Question: Multiway heaps. Considering the cost of compares only, and assuming that it takes t compares to find the largest of t items, find the value of t that minimizes the coefficient of N lg N in the compare count when a t-ary heap is used in heapsort. First, assume a straightforward generalization of sink(); then, assume that Floyd’s method can save one compare in the inner loop.

Answer:
In a t-ary heap, the height is log_t N = lg N / lg t. In the straightforward sink(), each level needs t - 1 compares to find the largest child and one more compare to decide whether to exchange with that child, for about t compares per level. The leading coefficient of N lg N is therefore proportional to

    t / lg t

This is minimized near t = e, so among integer arities the best choice is t = 3.

With Floyd's method, the item being sunk is moved down without comparing it against the largest child at each level; the downward pass only finds the largest child. The leading coefficient is therefore proportional to

    (t - 1) / lg t

This expression is minimized at t = 2 among integer arities. So the answers are: t = 3 for the straightforward generalization, and t = 2 when Floyd's method saves one compare in the inner loop.


Question: Priority queue with explicit links. Implement a priority queue using a heap-ordered binary tree, but use a triply linked structure instead of an array. You will need three links per node: two to traverse down the tree and one to traverse up the tree. Your implementation should guarantee logarithmic running time per operation, even if no maximum priority-queue size is known ahead of time.

Answer:
package chapter2.section4;

import edu.princeton.cs.algs4.StdOut;
import util.ArrayUtil;

/**
 * Created by Rene Argento on 23/03/17.
 */
@SuppressWarnings("unchecked")
public class Exercise24_PriorityQueueExplicitLinks<Key extends Comparable> {

    private class PQNode {
        PQNode parent;
        PQNode leftChild;
        PQNode rightChild;

        Key value;
    }

    private class PriorityQueueExplicitLinks {

        private PQNode priorityQueue;
        private int size = 0; // The first node will not be used to simplify index computation

        PriorityQueueExplicitLinks() {
            priorityQueue = new PQNode(); // 0 position is not used
        }

        public boolean isEmpty() {
            return size == 0;
        }

        public int size() {
            return size;
        }

        // O(lg N)
        public void insert(Key key) {
            size++;

            PQNode newNode = new PQNode();
            newNode.value = key;

            int parentIndex = size / 2; //The index of the parent of the new node
            int[] pathToParentNode = generatePathToNode(parentIndex);
            PQNode parentNode = getNode(pathToParentNode);

            if (parentNode.leftChild == null) {
                parentNode.leftChild = newNode;
            } else {
                parentNode.rightChild = newNode;
            }
            newNode.parent = parentNode;

            swim(newNode);
        }

        // O(lg N)
        public Key deleteMax() {
            if (size == 0) {
                throw new RuntimeException("Priority queue underflow");
            }

            Key max = priorityQueue.leftChild.value;
            int parentNodeIndex = size / 2; //The index of the parent of the last node

            // If we are deleting the root
            if (parentNodeIndex == 0) {
                priorityQueue.leftChild = null;
                size--;
                return max;
            }

            int[] pathToLastNodeParent = generatePathToNode(parentNodeIndex);
            PQNode lastNodeParent = getNode(pathToLastNodeParent);

            Key lastItemValue;

            if (lastNodeParent.rightChild != null) {
                lastItemValue = lastNodeParent.rightChild.value;
                lastNodeParent.rightChild.parent = null;
                lastNodeParent.rightChild = null;
            } else {
                lastItemValue = lastNodeParent.leftChild.value;
                lastNodeParent.leftChild.parent = null;
                lastNodeParent.leftChild = null;
            }

            priorityQueue.leftChild.value = lastItemValue;
            sink(priorityQueue.leftChild);

            size--;
            return max;
        }

        // O(lg N)
        private void swim(PQNode node) {

            while (node.parent.value != null && ArrayUtil.less(node.parent.value, node.value)) {
                // Swap node values
                Key temp = node.value;
                node.value = node.parent.value;
                node.parent.value = temp;

                node = node.parent;
            }
        }

        // O(lg N)
        private void sink(PQNode node) {
            boolean isTheLeftChildTheHighestItem;
            Key highestItemValue;

            // Repeat while the current node exists and has at least 1 child
            while (node != null && node.leftChild != null) {
                // Check which child is bigger
                if (node.rightChild != null) {
                    if (ArrayUtil.less(node.leftChild.value, node.rightChild.value)) {
                        isTheLeftChildTheHighestItem = false;
                        highestItemValue = node.rightChild.value;
                    } else {
                        isTheLeftChildTheHighestItem = true;
                        highestItemValue = node.leftChild.value;
                    }
                } else {
                    isTheLeftChildTheHighestItem = true;
                    highestItemValue = node.leftChild.value;
                }

                // Compare highest value child and parent
                if (ArrayUtil.less(node.value, highestItemValue)) {
                    Key temp = node.value;

                    if (isTheLeftChildTheHighestItem) {
                        node.value = node.leftChild.value;
                        node.leftChild.value = temp;

                        node = node.leftChild;
                    } else {
                        node.value = node.rightChild.value;
                        node.rightChild.value = temp;

                        node = node.rightChild;
                    }
                } else {
                    break;
                }
            }
        }

        // O(lg N)
        private int[] generatePathToNode(int nodeIndex) {
            int pathSize = (int) Math.ceil(Math.log10(nodeIndex) / Math.log10(2)) + 1;

            if (pathSize <= 0) {
                return new int[0];
            }

            int[] pathToNode = new int[pathSize];

            for (int i = pathSize - 1; i >= 0; i--) {
                pathToNode[i] = nodeIndex;

                nodeIndex /= 2;
            }

            return pathToNode;
        }

        // O(lg N)
        private PQNode getNode(int[] pathToNode) {
            int currentIndex = 1;
            PQNode currentNode = priorityQueue.leftChild;

            for (int i = 0; i < pathToNode.length && currentNode != null; i++) {
                if (pathToNode[i] == currentIndex * 2) {
                    currentNode = currentNode.leftChild;
                    currentIndex = currentIndex * 2;
                } else if (pathToNode[i] == currentIndex * 2 + 1) {
                    currentNode = currentNode.rightChild;
                    currentIndex = currentIndex * 2 + 1;
                }
            }

            if (currentNode == null) {
                return priorityQueue;
            }

            return currentNode;
        }
    }

    public static void main(String[] args) {
        Exercise24_PriorityQueueExplicitLinks<Integer>.PriorityQueueExplicitLinks priorityQueueExplicitLinks =
                new Exercise24_PriorityQueueExplicitLinks().new PriorityQueueExplicitLinks();

        StdOut.println("isEmpty: " + priorityQueueExplicitLinks.isEmpty() + " Expected: true");

        priorityQueueExplicitLinks.insert(10);
        priorityQueueExplicitLinks.insert(2);
        priorityQueueExplicitLinks.insert(7);
        priorityQueueExplicitLinks.insert(20);

        StdOut.println("Size: " + priorityQueueExplicitLinks.size() + " Expected: 4");
        StdOut.println("isEmpty: " + priorityQueueExplicitLinks.isEmpty() + " Expected: false");

        StdOut.println("Item removed: " + priorityQueueExplicitLinks.deleteMax());
        StdOut.println("Item removed: " + priorityQueueExplicitLinks.deleteMax());
        StdOut.println("Item removed: " + priorityQueueExplicitLinks.deleteMax());
        StdOut.println("Item removed: " + priorityQueueExplicitLinks.deleteMax());
    }
}


Question: Computational number theory. Write a program CubeSum.java that prints out all integers of the form a^3 + b^3 where a and b are integers between 0 and N in sorted order, without using excessive space. That is, instead of computing an array of the N^2 sums and sorting them, build a minimum-oriented priority queue, initially containing (0^3, 0, 0), (1^3, 1, 0), (2^3, 2, 0), . . . , (N^3, N, 0). Then, while the priority queue is nonempty, remove the smallest item(i^3 + j^3, i, j), print it, and then, if j < N, insert the item (i^3 + (j+1)^3, i, j+1). Use this program to find all distinct integers a, b, c, and d between 0 and 10^6 such that a^3 + b^3 = c^3 + d^3.

Answer:
Maintain one pending item for each possible `a`, where the item stores `(sum, a, b)` and `sum = a^3 + b^3`. Delete the minimum sum, print it, and then insert the next value for the same `a`.

```java
import edu.princeton.cs.algs4.MinPQ;
import edu.princeton.cs.algs4.StdOut;

public class CubeSum implements Comparable<CubeSum> {
    private final int a;
    private final int b;
    private final long sum;

    public CubeSum(int a, int b) {
        this.a = a;
        this.b = b;
        this.sum = (long) a * a * a + (long) b * b * b;
    }

    public int compareTo(CubeSum that) {
        return Long.compare(this.sum, that.sum);
    }

    public String toString() {
        return sum + " = " + a + "^3 + " + b + "^3";
    }

    public static void main(String[] args) {
        int n = Integer.parseInt(args[0]);
        MinPQ<CubeSum> pq = new MinPQ<>();
        for (int a = 0; a <= n; a++) pq.insert(new CubeSum(a, 0));

        while (!pq.isEmpty()) {
            CubeSum x = pq.delMin();
            StdOut.println(x);
            if (x.b < n) pq.insert(new CubeSum(x.a, x.b + 1));
        }
    }
}
```

This prints all `(N+1)^2` sums in sorted order using only `O(N)` priority-queue space.


Question: Heap without exchanges. Because the exch() primitive is used in the sink() and swim() operations, the items are loaded and stored twice as often as necessary. Give more efficient implementations that avoid this inefficiency, a la insertion sort (see Exercise 2.1.25).

Answer:
package chapter2.section4;

import edu.princeton.cs.algs4.StdOut;
import util.ArrayUtil;

/**
 * Created by Rene Argento on 25/03/17.
 */
@SuppressWarnings("unchecked")
public class Exercise26_HeapWithoutExchanges {

    private enum Orientation {
        MAX, MIN;
    }

    private class PriorityQueue<Key extends Comparable<Key>> {

        private Key[] priorityQueue;
        private int size = 0; // in priorityQueue[1..n] with pq[0] unused
        private Orientation orientation;

        PriorityQueue(int size, Orientation orientation) {
            priorityQueue = (Key[]) new Comparable[size + 1];
            this.orientation = orientation;
        }

        public boolean isEmpty() {
            return size == 0;
        }

        public int size() {
            return size;
        }

        public void insert(Key key) {
            if (size != priorityQueue.length - 1) {
                size++;

                priorityQueue[size] = key;
                swim(size);
            }
        }

        Key deleteTop() {
            if (size == 0) {
                throw new RuntimeException("Priority queue underflow");
            }

            size--;

            Key topElement = priorityQueue[1];
            ArrayUtil.exchange(priorityQueue, 1, size + 1);

            priorityQueue[size + 1] = null;
            sink(1);

            return topElement;
        }

        private void swim(int index) {
            Key aux = priorityQueue[index];

            boolean exchangeRequired = false;

            while (index / 2 >= 1) {
                if ((orientation == Orientation.MAX && ArrayUtil.less(priorityQueue[index / 2], aux))
                        || (orientation == Orientation.MIN && ArrayUtil.more(priorityQueue[index / 2], aux))) {
                    priorityQueue[index] = priorityQueue[index / 2];
                    exchangeRequired = true;
                } else {
                    break;
                }

                index = index / 2;
            }

            if (exchangeRequired) {
                priorityQueue[index] = aux;
            }
        }

        private void sink(int index) {

            Key aux = priorityQueue[index];

            while (index * 2 <= size) {
                int selectedChildIndex = index * 2;

                if (index * 2 + 1 <= size &&
                        (
                         (orientation == Orientation.MAX && ArrayUtil.less(priorityQueue[index * 2], priorityQueue[index * 2 + 1]))
                               || (orientation == Orientation.MIN && ArrayUtil.more(priorityQueue[index * 2], priorityQueue[index * 2 + 1]))
                        )
                        ) {
                    selectedChildIndex = index * 2 + 1;
                }

                if ((orientation == Orientation.MAX && ArrayUtil.more(priorityQueue[selectedChildIndex], aux))
                        || (orientation == Orientation.MIN && ArrayUtil.less(priorityQueue[selectedChildIndex], aux))) {
                    priorityQueue[index] = priorityQueue[selectedChildIndex];
                } else {
                    break;
                }

                index = selectedChildIndex;
            }

            // No need to check if an exchange is required.
            // The value of index is only updated when an exchange happens.
            priorityQueue[index] = aux;
        }
    }

    public static void main(String[] args) {
        Exercise26_HeapWithoutExchanges.PriorityQueue<Integer> priorityQueue =
                new Exercise26_HeapWithoutExchanges().new PriorityQueue(4, Orientation.MAX);

        priorityQueue.insert(10);
        priorityQueue.insert(2);
        priorityQueue.insert(7);
        priorityQueue.insert(20);

        StdOut.println("Size: " + priorityQueue.size());
        StdOut.println("isEmpty: " + priorityQueue.isEmpty());

        StdOut.println("Item removed: " + priorityQueue.deleteTop() + " Expected: 20");
        StdOut.println("Item removed: " + priorityQueue.deleteTop() + " Expected: 10");
        StdOut.println("Item removed: " + priorityQueue.deleteTop() + " Expected: 7");
        StdOut.println("Item removed: " + priorityQueue.deleteTop() + " Expected: 2");
    }
}


Question: Find the minimum. Add a min() method to MaxPQ. Your implementation should use constant time and constant extra space.

Answer:
package chapter2.section4;

import edu.princeton.cs.algs4.StdOut;
import util.ArrayUtil;

/**
 * Created by Rene Argento on 25/03/17.
 */
@SuppressWarnings("unchecked")
public class Exercise27_FindTheMinimum {

    private class PriorityQueue<Key extends Comparable<Key>> {

        private Key[] priorityQueue;
        private int size = 0; // in priorityQueue[1..n] with pq[0] unused

        private Key min;

        PriorityQueue(int size) {
            priorityQueue = (Key[]) new Comparable[size + 1];
        }

        public boolean isEmpty() {
            return size == 0;
        }

        public int size() {
            return size;
        }

        public void insert(Key key) {
            if (size != priorityQueue.length - 1) {
                size++;

                if (min == null || ArrayUtil.less(key, min)) {
                    min = key;
                }

                priorityQueue[size] = key;
                swim(size);
            }
        }

        Key deleteMax() {
            if (size == 0) {
                throw new RuntimeException("Priority queue underflow");
            }

            size--;

            Key max = priorityQueue[1];
            ArrayUtil.exchange(priorityQueue, 1, size + 1);

            if (size == 0) {
                min = null;
            }

            priorityQueue[size + 1] = null;
            sink(1);

            return max;
        }

        private void swim(int index) {
            while (index / 2 >= 1 && ArrayUtil.less(priorityQueue[index / 2], priorityQueue[index])) {
                ArrayUtil.exchange(priorityQueue, index / 2, index);

                index = index / 2;
            }
        }

        private void sink(int index) {
            while (index * 2 <= size) {
                int selectedChildIndex = index * 2;

                if (index * 2 + 1 <= size && ArrayUtil.less(priorityQueue[index * 2], priorityQueue[index * 2 + 1])) {
                    selectedChildIndex = index * 2 + 1;
                }

                if (ArrayUtil.more(priorityQueue[selectedChildIndex], priorityQueue[index])) {
                    ArrayUtil.exchange(priorityQueue, index, selectedChildIndex);
                } else {
                    break;
                }

                index = selectedChildIndex;
            }
        }

        public Key min() {
            return min;
        }
    }

    public static void main(String[] args) {
        Exercise27_FindTheMinimum.PriorityQueue<Integer> priorityQueue =
                new Exercise27_FindTheMinimum().new PriorityQueue(5);

        StdOut.println("Min: " + priorityQueue.min() + " Expected: null");

        priorityQueue.insert(10);

        StdOut.println("Min: " + priorityQueue.min() + " Expected: 10");

        priorityQueue.insert(2);

        StdOut.println("Min: " + priorityQueue.min() + " Expected: 2");

        priorityQueue.insert(7);

        StdOut.println("Min: " + priorityQueue.min() + " Expected: 2");

        priorityQueue.insert(20);
        priorityQueue.insert(1);

        StdOut.println("Min: " + priorityQueue.min() + " Expected: 1");

        StdOut.println("Item removed: " + priorityQueue.deleteMax());
        StdOut.println("Item removed: " + priorityQueue.deleteMax());
        StdOut.println("Item removed: " + priorityQueue.deleteMax());
        StdOut.println("Item removed: " + priorityQueue.deleteMax());

        StdOut.println("Min: " + priorityQueue.min() + " Expected: 1");

        StdOut.println("Item removed: " + priorityQueue.deleteMax());

        priorityQueue.insert(99);
        StdOut.println("Min: " + priorityQueue.min() + " Expected: 99");
    }
}


Question: Selection filter. Write a TopM client that reads points (x, y, z) from standard input, takes a value M from the command line, and prints the M points that are closest to the origin in Euclidean distance. Estimate the running time of your client for N = 10^8 and M = 10^4.

Answer:
// Exercise28_SelectionFilter.java
package chapter2.section4;

import edu.princeton.cs.algs4.StdIn;
import edu.princeton.cs.algs4.StdOut;
import edu.princeton.cs.algs4.StdRandom;
import edu.princeton.cs.algs4.Stopwatch;

import java.util.Stack;

/**
 * Created by Rene Argento on 25/03/17.
 */
public class Exercise28_SelectionFilter {

    private class Point implements Comparable<Point>{
        double x, y, z;

        Point(double x, double y, double z) {
            this.x = x;
            this.y = y;
            this.z = z;
        }

        @Override
        public int compareTo(Point other) {
            //Distance to 0,0 (origin)
            double euclideanDistance = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2) + Math.pow(z, 2));
            double otherPointEuclideanDistance = Math.sqrt(Math.pow(other.x, 2) + Math.pow(other.y, 2) +
                    Math.pow(other.z, 2));

            if (euclideanDistance < otherPointEuclideanDistance) {
                return -1;
            } else if (euclideanDistance > otherPointEuclideanDistance) {
                return 1;
            } else {
                return 0;
            }
        }

        @Override
        public String toString() {
            return "x = " + x + " y = " + y + " z = " + z;
        }
    }

    // Parameter example: 10000
    public static void main(String[] args) {

        int m = Integer.parseInt(args[0]);

        PriorityQueue<Point> priorityQueue = new PriorityQueue<>(m + 1, PriorityQueue.Orientation.MAX);

        while (StdIn.hasNextLine()) {
            String pointsLine = StdIn.readLine();

            String[] pointsString = pointsLine.split(" ");
            double x = Double.parseDouble(pointsString[0]);
            double y = Double.parseDouble(pointsString[1]);
            double z = Double.parseDouble(pointsString[2]);

            Point point = new Exercise28_SelectionFilter().new Point(x, y, z);
            priorityQueue.insert(point);

            if (priorityQueue.size() > m) {
                priorityQueue.deleteTop();
            }
        }

        Stack<Point> pointsStack = reversePointsOrder(priorityQueue);
        printPoints(pointsStack);

        //Estimate running time
        int initialArraySize = 100000; //10^5
        int numberOfExperiments = 3; //10^5, 10^6, 10^7
        doExperimentToEstimateRunningTime(initialArraySize, numberOfExperiments);
    }

    private static Stack<Point> reversePointsOrder(PriorityQueue<Point> priorityQueue) {
        Stack<Point> stack = new Stack<>();

        while (priorityQueue.size() > 0) {
            stack.push(priorityQueue.deleteTop());
        }

        return stack;
    }

    private static void printPoints(Stack<Point> pointsStack) {
        while (pointsStack.size() > 0) {
            StdOut.println(pointsStack.pop());
        }
    }

    private static void doExperimentToEstimateRunningTime(int arraySize, int numberOfExperiments) {
        int m = 10000; //10^4

        for (int i = 0; i < numberOfExperiments; i++) {

            PriorityQueue<Point> priorityQueue = new PriorityQueue<>(m + 1, PriorityQueue.Orientation.MAX);

            Point[] pointArray = generateRandomPointsArray(arraySize);

            Stopwatch timer = new Stopwatch();

            for (Point point : pointArray) {
                priorityQueue.insert(point);

                if (priorityQueue.size() > m) {
                    priorityQueue.deleteTop();
                }
            }

            Stack<Point> pointsStack = reversePointsOrder(priorityQueue);
            printPoints(pointsStack);

            double runningTime = timer.elapsedTime();
            StdOut.println("Running time for N = " + arraySize + " and M = " + m + ": " + runningTime);

            arraySize *= 10;
        }
    }

    private static Point[] generateRandomPointsArray(int length) {
        Point[] array = new Point[length];

        for (int i = 0; i < length; i++) {
            double x = StdRandom.uniform();
            double y = StdRandom.uniform();
            double z = StdRandom.uniform();

            Point point = new Exercise28_SelectionFilter().new Point(x, y, z);
            array[i] = point;
        }
        return array;
    }
}

Additional notes/results:
2.4.28 - Selection filter

Running time for N = 100000 and M = 10000: 0.053
Running time for N = 1000000 and M = 10000: 0.365
Running time for N = 10000000 and M = 10000: 3.362

N = 10^5 to N^6 increased the running time by 6.9 times.
N = 10^6 to N^7 increased the running time by 9.21 times.
Estimated increase from N^7 to N^8: 12.29 times the running time of N^7.

Estimated running time for N = 10^8 and M = 10^4 = 41.32 seconds

Sample input

10 20 10
1 2 3
90 91 92
10 11 10
3 3 4
-1 -4 0
20 10 13
100 102 999
1001 98 10

Sample output
x = 1.0 y = 2.0 z = 3.0
x = -1.0 y = -4.0 z = 0.0
x = 3.0 y = 3.0 z = 4.0
x = 10.0 y = 11.0 z = 10.0
x = 10.0 y = 20.0 z = 10.0


Question: Min/max priority queue. Design a data type that supports the following operations: insert, delete the maximum, and delete the minimum (all in logarithmic time); and find the maximum and find the minimum (both in constant time). Hint: Use two heaps.

Answer:
package chapter2.section4;

import edu.princeton.cs.algs4.StdOut;
import util.ArrayUtil;

/**
 * Created by Rene Argento on 25/03/17.
 */
// Based on: http://eranle.blogspot.com.br/2012/08/min-max-heap-java-implementation.html
// Thanks to YRFT (https://github.com/YRFT) for finding that the method deleteItem() also needs to call the swim() method:
// https://github.com/reneargento/algorithms-sedgewick-wayne/issues/181
@SuppressWarnings("unchecked")
public class Exercise29_MinMaxPriorityQueue  {

    private enum Orientation {
        MAX, MIN;
    }

    private class PQNode implements Comparable<PQNode>{
        Comparable key;
        int minHeapIndex;
        int maxHeapIndex;

        @Override
        public int compareTo(PQNode other) {
            return key.compareTo(other.key);
        }
    }

    private class MinMaxPriorityQueue<Key extends Comparable<Key>> {
        private PQNode[] minPriorityQueue;
        private PQNode[] maxPriorityQueue;
        private int size = 0;

        MinMaxPriorityQueue() {
            minPriorityQueue = new PQNode[2];
            maxPriorityQueue = new PQNode[2];
        }

        public boolean isEmpty() {
            return size == 0;
        }

        public int size() {
            return size;
        }

        public void insert(Key key) {
            if (size == minPriorityQueue.length - 1) {
                resize(minPriorityQueue.length * 2);
            }

            PQNode pqNode = new PQNode();
            pqNode.key = key;

            size++;

            insertOnMinHeap(pqNode);
            insertOnMaxHeap(pqNode);
        }

        private void insertOnMinHeap(PQNode pqNode) {
            minPriorityQueue[size] = pqNode;
            swim(minPriorityQueue, size, Orientation.MIN);
        }

        private void insertOnMaxHeap(PQNode pqNode) {
            maxPriorityQueue[size] = pqNode;
            swim(maxPriorityQueue, size, Orientation.MAX);
        }

        // O(1)
        public Comparable findMin() {
            if (size == 0) {
                return null;
            }

            return minPriorityQueue[1].key;
        }

        // O(1)
        public Comparable findMax() {
            if (size == 0) {
                return null;
            }

            return maxPriorityQueue[1].key;
        }

        // O(lg N)
        public Comparable deleteMax() {
            if (size == 0) {
                throw new RuntimeException("Priority queue underflow");
            }

            size--;

            PQNode max = maxPriorityQueue[1];

            deleteTopItem(maxPriorityQueue, Orientation.MAX);
            deleteItem(minPriorityQueue, Orientation.MIN, max.minHeapIndex);

            if (size == minPriorityQueue.length / 4) {
                resize(minPriorityQueue.length / 2);
            }

            return max.key;
        }

        // O(lg N)
        public Comparable deleteMin() {
            if (size == 0) {
                throw new RuntimeException("Priority queue underflow");
            }

            size--;

            PQNode min = minPriorityQueue[1];

            deleteTopItem(minPriorityQueue, Orientation.MIN);
            deleteItem(maxPriorityQueue, Orientation.MAX, min.maxHeapIndex);

            if (size == minPriorityQueue.length / 4) {
                resize(minPriorityQueue.length / 2);
            }

            return min.key;
        }

        private void deleteTopItem(PQNode[] priorityQueue, Orientation orientation) {
            deleteItem(priorityQueue, orientation, 1);
        }

        private void deleteItem(PQNode[] priorityQueue, Orientation orientation, int index) {
            ArrayUtil.exchange(priorityQueue, index, size + 1);
            priorityQueue[size + 1] = null;

            if (index == size + 1) {
                // We deleted the last value, so no need to sink
                return;
            }

            sink(priorityQueue, index, orientation);
            swim(priorityQueue, index, orientation);
        }

        private void swim(PQNode[] priorityQueue, int index, Orientation orientation) {
            while (index / 2 >= 1) {
                if ((orientation == Orientation.MAX && ArrayUtil.less(priorityQueue[index / 2], priorityQueue[index]))
                        || (orientation == Orientation.MIN && ArrayUtil.more(priorityQueue[index / 2], priorityQueue[index]))) {
                    ArrayUtil.exchange(priorityQueue, index / 2, index);

                    if (orientation == Orientation.MIN) {
                        priorityQueue[index].minHeapIndex = index;
                        priorityQueue[index / 2].minHeapIndex = index / 2;
                    } else {
                        priorityQueue[index].maxHeapIndex = index;
                        priorityQueue[index / 2].maxHeapIndex = index / 2;
                    }
                } else {
                    break;
                }

                index = index / 2;
            }

            // Even if there were no exchanges, we still need to update the index
            if (orientation == Orientation.MIN) {
                priorityQueue[index].minHeapIndex = index;
            } else {
                priorityQueue[index].maxHeapIndex = index;
            }
        }

        private void sink(PQNode[] priorityQueue, int index, Orientation orientation) {
            while (index * 2 <= size) {
                int selectedChildIndex = index * 2;

                if (index * 2 + 1 <= size &&
                        (
                         (orientation == Orientation.MAX && ArrayUtil.less(priorityQueue[index * 2], priorityQueue[index * 2 + 1]))
                              || (orientation == Orientation.MIN && ArrayUtil.more(priorityQueue[index * 2], priorityQueue[index * 2 + 1]))
                        )
                        ) {
                    selectedChildIndex = index * 2 + 1;
                }

                if ((orientation == Orientation.MAX && ArrayUtil.more(priorityQueue[selectedChildIndex], priorityQueue[index]))
                        || (orientation == Orientation.MIN && ArrayUtil.less(priorityQueue[selectedChildIndex], priorityQueue[index]))) {
                    ArrayUtil.exchange(priorityQueue, index, selectedChildIndex);

                    if (orientation == Orientation.MIN) {
                        priorityQueue[index].minHeapIndex = index;
                        priorityQueue[selectedChildIndex].minHeapIndex = selectedChildIndex;
                    } else {
                        priorityQueue[index].maxHeapIndex = index;
                        priorityQueue[selectedChildIndex].maxHeapIndex = selectedChildIndex;
                    }
                } else {
                    break;
                }

                index = selectedChildIndex;
            }

            // Even if there were no exchanges, we still need to update the index
            if (orientation == Orientation.MIN) {
                priorityQueue[index].minHeapIndex = index;
            } else {
                priorityQueue[index].maxHeapIndex = index;
            }
        }

        private void resize(int newSize) {
            // Min heap
            PQNode[] newMinPriorityQueue = new PQNode[newSize];
            System.arraycopy(minPriorityQueue, 1, newMinPriorityQueue, 1, size);
            minPriorityQueue = newMinPriorityQueue;

            // Max heap
            PQNode[] newMaxPriorityQueue = new PQNode[newSize];
            System.arraycopy(maxPriorityQueue, 1, newMaxPriorityQueue, 1, size);
            maxPriorityQueue = newMaxPriorityQueue;
        }
    }

    public static void main(String[] args) {
        Exercise29_MinMaxPriorityQueue.MinMaxPriorityQueue<Integer> minMaxPriorityQueue =
                new Exercise29_MinMaxPriorityQueue().new MinMaxPriorityQueue();

        minMaxPriorityQueue.insert(10);
        minMaxPriorityQueue.insert(2);
        minMaxPriorityQueue.insert(40);
        minMaxPriorityQueue.insert(1);

        StdOut.println("Delete Max: " + minMaxPriorityQueue.deleteMax() + " Expected: 40");
        StdOut.println("Delete Min: " + minMaxPriorityQueue.deleteMin() + " Expected: 1");

        StdOut.println("Find Max: " + minMaxPriorityQueue.findMax() + " Expected: 10");
        StdOut.println("Find Min: " + minMaxPriorityQueue.findMin() + " Expected: 2");

        minMaxPriorityQueue.insert(99);
        minMaxPriorityQueue.insert(-1);

        StdOut.println("Find Max: " + minMaxPriorityQueue.findMax() + " Expected: 99");
        StdOut.println("Find Min: " + minMaxPriorityQueue.findMin() + " Expected: -1");
    }
}


Question: Dynamic median-finding. Design a data type that supports insert in logarithmic time, find the median in constant time, and delete the median in logarithmic time. Hint: Use a min-heap and a max-heap.

Answer:
package chapter2.section4;

import edu.princeton.cs.algs4.StdOut;
import util.ArrayUtil;

/**
 * Created by Rene Argento on 26/03/17.
 */
public class Exercise30_DynamicMedianFinding {

    private class DynamicMedianFindingHeap<Key extends Comparable<Key>> {

        private PriorityQueueResize<Key> minPriorityQueue;
        private PriorityQueueResize<Key> maxPriorityQueue;

        private int size;

        DynamicMedianFindingHeap() {
            minPriorityQueue = new PriorityQueueResize<>(PriorityQueueResize.Orientation.MIN);
            maxPriorityQueue = new PriorityQueueResize<>(PriorityQueueResize.Orientation.MAX);
            size = 0;
        }

        //O(lg N)
        public void insert(Key key) {

            if (size == 0 || ArrayUtil.less(key, maxPriorityQueue.peek())) {
                maxPriorityQueue.insert(key);
            } else {
                minPriorityQueue.insert(key);
            }

            if (minPriorityQueue.size() > maxPriorityQueue.size() + 1) {
                Key keyToBeMoved = minPriorityQueue.deleteTop();
                maxPriorityQueue.insert(keyToBeMoved);
            } else if (maxPriorityQueue.size() > minPriorityQueue.size() + 1) {
                Key keyToBeMoved = maxPriorityQueue.deleteTop();
                minPriorityQueue.insert(keyToBeMoved);
            }

            size++;
        }

        //O(1)
        public Key findTheMedian() {
            Key median;

            if (minPriorityQueue.size() > maxPriorityQueue.size()) {
                median = minPriorityQueue.peek();
            } else {
                median = maxPriorityQueue.peek();
            }

            return median;
        }

        //O(lg N)
        public Key deleteMedian() {
            Key median;

            if (minPriorityQueue.size() > maxPriorityQueue.size()) {
                median = minPriorityQueue.deleteTop();
            } else {
                median = maxPriorityQueue.deleteTop();
            }

            size--;

            return median;
        }
    }

    public static void main(String[] args) {
        Exercise30_DynamicMedianFinding.DynamicMedianFindingHeap<Integer> dynamicMedianFindingHeap =
                new Exercise30_DynamicMedianFinding().new DynamicMedianFindingHeap<>();

        dynamicMedianFindingHeap.insert(1);
        dynamicMedianFindingHeap.insert(2);
        dynamicMedianFindingHeap.insert(3);
        dynamicMedianFindingHeap.insert(4);
        dynamicMedianFindingHeap.insert(5);
        dynamicMedianFindingHeap.insert(6);
        dynamicMedianFindingHeap.insert(7);

        StdOut.println("Median: " + dynamicMedianFindingHeap.findTheMedian() + " Expected: 4");
        StdOut.println("Delete Median: " + dynamicMedianFindingHeap.deleteMedian() + " Expected: 4");

        //When we have an even number of values, pick the left one
        StdOut.println("Median: " + dynamicMedianFindingHeap.findTheMedian() + " Expected: 3");

        dynamicMedianFindingHeap.deleteMedian();
        dynamicMedianFindingHeap.insert(99);
        dynamicMedianFindingHeap.insert(100);

        StdOut.println("Median: " + dynamicMedianFindingHeap.findTheMedian() + " Expected: 6");
    }

}


Question: Fast insert. Develop a compare-based implementation of the MinPQ API such that insert uses ~log log N compares and delete the minimum uses ~2 log N compares. Hint: Use binary search on parent pointers to find the ancestor in swim().

Answer:
package chapter2.section4;

import edu.princeton.cs.algs4.StdOut;
import util.ArrayUtil;

/**
 * Created by Rene Argento on 26/03/17.
 */
// Thanks to dragon-dreamer (https://github.com/dragon-dreamer) for suggesting an improved binarySearchToGetTargetAncestor() method.
// https://github.com/reneargento/algorithms-sedgewick-wayne/issues/114
@SuppressWarnings("unchecked")
public class Exercise31_FastInsert {

    private class PriorityQueueFastInsert<Key extends Comparable<Key>> {
        private Key[] priorityQueue;
        private int size = 0; // in priorityQueue[1..n] with pq[0] unused

        private int numberOfCompares;

        PriorityQueueFastInsert() {
            priorityQueue = (Key[]) new Comparable[2];
        }

        public boolean isEmpty() {
            return size == 0;
        }

        public int size() {
            return size;
        }

        public void insert(Key key) {
            numberOfCompares = 0;

            if (size == priorityQueue.length - 1) {
                resize(priorityQueue.length * 2);
            }
            size++;
            priorityQueue[size] = key;

            swim(size);

            double lgN = Math.log10(size) / Math.log10(2);
            int lgLgN = lgN != 0 ? (int) (Math.log10(lgN) / Math.log10(2)) : 0;

            StdOut.println("Number of compares: " + numberOfCompares + " Expected: ~" + lgLgN);
        }

        public Key deleteMin() {
            numberOfCompares = 0;

            if (size == 0) {
                throw new RuntimeException("Priority queue underflow");
            }
            size--;

            Key min = priorityQueue[1];

            ArrayUtil.exchange(priorityQueue, 1, size + 1);
            priorityQueue[size + 1] = null;

            sink(1);

            if (size == priorityQueue.length / 4) {
                resize(priorityQueue.length / 2);
            }

            int lgN = (int) (Math.log10(size) / Math.log10(2));
            StdOut.println("Number of compares: " + numberOfCompares + " Expected: ~" + (2 * lgN));

            return min;
        }

        private void swim(int index) {
            // No need to swim if we only have 1 element
            if (index == 1) {
                return;
            }

            int targetAncestor = binarySearchToGetTargetAncestor(index);

            while (index / 2 >= targetAncestor) {
                ArrayUtil.exchange(priorityQueue, index / 2, index);
                index = index / 2;
            }
        }

        private int binarySearchToGetTargetAncestor(int index) {
            int higherLevel = 0;
            int indexLevel = log2(index);
            int lowerLevel = indexLevel;

            while (lowerLevel != higherLevel) {
                int middleLevel = higherLevel + (lowerLevel - higherLevel) / 2;
                int parentIndex = index / (1 << (indexLevel - middleLevel));

                numberOfCompares++;
                if (ArrayUtil.more(priorityQueue[parentIndex], priorityQueue[index])) {
                    lowerLevel = middleLevel;
                } else {
                    higherLevel = middleLevel + 1;
                }
            }
            return index / (1 << (indexLevel - higherLevel));
        }

        private int log2(int value) {
            int result = 0;
            while ((value >>= 1) != 0) {
                result++;
            }
            return result;
        }

        private void sink(int index) {
            while (index * 2 <= size) {
                int selectedChildIndex = index * 2;

                numberOfCompares++;
                if (index * 2 + 1 <= size && ArrayUtil.more(priorityQueue[index * 2], priorityQueue[index * 2 + 1])) {
                    selectedChildIndex = index * 2 + 1;
                }

                numberOfCompares++;
                if (ArrayUtil.less(priorityQueue[selectedChildIndex], priorityQueue[index])) {
                    ArrayUtil.exchange(priorityQueue, index, selectedChildIndex);
                } else {
                    break;
                }

                index = selectedChildIndex;
            }
        }

        private void resize(int newSize) {
            Key[] newPriorityQueue = (Key[]) new Comparable[newSize];
            System.arraycopy(priorityQueue, 1, newPriorityQueue, 1, size);
            priorityQueue = newPriorityQueue;
        }
    }

    public static void main(String[] args) {
        Exercise31_FastInsert.PriorityQueueFastInsert<Integer> priorityQueueFastInsert =
                new Exercise31_FastInsert().new PriorityQueueFastInsert<>();

        // Insert many items and later insert smaller items to check the number of compares
        for (int i = 10; i <= 42; i++) {
            priorityQueueFastInsert.insert(i);
        }

        priorityQueueFastInsert.insert(2);
        priorityQueueFastInsert.insert(1);

        // Also test delete min
        StdOut.println("Delete Min: " + priorityQueueFastInsert.deleteMin() + " Expected: 1");
        StdOut.println("Delete Min: " + priorityQueueFastInsert.deleteMin() + " Expected: 2");
        StdOut.println("Delete Min: " + priorityQueueFastInsert.deleteMin() + " Expected: 10");
    }
}


Question: Index priority-queue implementation (additional operations). Add minIndex(), change(), and delete() to your implementation of Exercise 2.4.33.

Answer:
package chapter2.section4;

import edu.princeton.cs.algs4.StdOut;
import edu.princeton.cs.algs4.StdRandom;

import java.util.NoSuchElementException;

/**
 * Created by Rene Argento on 26/03/17.
 */
//Based on http://algs4.cs.princeton.edu/24pq/IndexMaxPQ.java.html
@SuppressWarnings("unchecked")
public class Exercise34_IndexPQAdditionalOps {

    private class IndexMinPQ<Key extends Comparable<Key>> {

        private Key[] keys;
        private int[] pq; // Holds the indices of the keys
        private int[] qp; // Inverse of pq -> qp[i] gives the position of i in pq[] (the index j such that pq[j] is i).
                          // qp[pq[i]] = pq[qp[i]] = i
        private int size = 0;

        @SuppressWarnings("unchecked")
        public IndexMinPQ(int size) {
            keys = (Key[]) new Comparable[size + 1];
            pq = new int[size + 1];
            qp = new int[size + 1];

            for (int i = 0; i < qp.length; i++) {
                qp[i] = -1;
            }
        }

        public boolean isEmpty() {
            return size == 0;
        }

        public int size() {
            return size;
        }

        public boolean contains(int index) {
            return qp[index] != -1;
        }

        //Return key associated with index
        public Key keyOf(int index) {
            if (!contains(index)) {
                throw new NoSuchElementException("Index is not in the priority queue");
            }

            return keys[index];
        }

        public void insert(int index, Key key) {
            if (contains(index)) {
                throw new IllegalArgumentException("Index is already in the priority queue");
            }

            if (size != keys.length - 1) {
                size++;

                keys[index] = key;
                pq[size] = index;
                qp[index] = size;

                swim(size);
            }
        }

        //Remove a minimal key and return its index
        public int deleteMin() {
            if (size == 0) {
                throw new NoSuchElementException("Priority queue underflow");
            }

            int minElementIndex = pq[1];
            exchange(1, size);
            size--;
            sink(1);

            keys[pq[size + 1]] = null;
            qp[pq[size + 1]] = -1;

            return minElementIndex;
        }

        public void delete(int i) {
            if (!contains(i)) {
                throw new NoSuchElementException("Index is not in the priority queue");
            }

            int index = qp[i];

            exchange(index, size);
            size--;

            swim(index);
            sink(index);

            keys[i] = null; //Same thing as keys[pq[size + 1]] = null
            qp[i] = -1;  //Same thing as qp[pq[size + 1]] = -1;
        }

        //Change the key associated with index to key argument
        public void change(int index, Key key) {
            if (!contains(index)) {
                throw new NoSuchElementException("Index is not in the priority queue");
            }

            keys[index] = key;

            swim(qp[index]);
            sink(qp[index]);
        }

        public Key minKey() {
            if (size == 0) {
                throw new NoSuchElementException("Priority queue underflow");
            }

            return keys[pq[1]];
        }

        public int minIndex() {
            if (size == 0) {
                throw new NoSuchElementException("Priority queue underflow");
            }

            return pq[1];
        }

        private void swim(int index) {
            while (index / 2 >= 1 && more(index / 2, index)) {
                exchange(index / 2, index);
                index = index / 2;
            }
        }

        private void sink(int index) {
            while (index * 2 <= size) {
                int selectedChildIndex = index * 2;

                if (index * 2 + 1 <= size && more(index * 2, index * 2 + 1)) {
                    selectedChildIndex = index * 2 + 1;
                }

                if (less(selectedChildIndex, index)) {
                    exchange(index, selectedChildIndex);
                } else {
                    break;
                }

                index = selectedChildIndex;
            }
        }

        private boolean less(int keyIndex1, int keyIndex2) {
            return keys[pq[keyIndex1]].compareTo(keys[pq[keyIndex2]]) < 0;
        }

        private boolean more(int keyIndex1, int keyIndex2) {
            return keys[pq[keyIndex1]].compareTo(keys[pq[keyIndex2]]) > 0;
        }

        private void exchange(int keyIndex1, int keyIndex2) {
            int temp = pq[keyIndex1];
            pq[keyIndex1] = pq[keyIndex2];
            pq[keyIndex2] = temp;

            qp[pq[keyIndex1]] = keyIndex1;
            qp[pq[keyIndex2]] = keyIndex2;
        }
    }

    public static void main(String[] args) {
        // Insert a bunch of strings
        String[] strings = { "it", "was", "the", "best", "of", "times", "it", "was", "the", "worst" };

        Exercise34_IndexPQAdditionalOps.IndexMinPQ<String> priorityQueue =
                new Exercise34_IndexPQAdditionalOps().new IndexMinPQ<>(strings.length);

        for (int i = 0; i < strings.length; i++) {
            priorityQueue.insert(i, strings[i]);
        }

        StdOut.println("Min index: " + priorityQueue.minIndex() + " Expected: 3");

        priorityQueue.changeKey(4, "changed");
        StdOut.println("Changed key: " + priorityQueue.keyOf(4) + " Expected: changed");

        // Delete and print each key
        StdOut.println("Keys:");

        while (!priorityQueue.isEmpty()) {
            String key = priorityQueue.minKey();
            int index = priorityQueue.deleteMin();
            StdOut.println(index + " " + key);
        }
        StdOut.println();

        // Reinsert the same strings
        for (int i = 0; i < strings.length; i++) {
            priorityQueue.insert(i, strings[i]);
        }

        // Delete and print them in random order
        int[] randomIndices = new int[strings.length];
        for (int i = 0; i < strings.length; i++) {
            randomIndices[i] = i;
        }

        StdRandom.shuffle(randomIndices);

        StdOut.println("Randomly deleting keys");
        for (int i = 0; i < randomIndices.length; i++) {
            String key = priorityQueue.keyOf(randomIndices[i]);
            priorityQueue.delete(randomIndices[i]);
            StdOut.println(randomIndices[i] + " " + key);
        }
    }
}


Question: Sampling from a discrete probability distribution. Write a class Sample with a constructor that takes an array p[] of double values as argument and supports the following two operations: random()—return an index i with probability p[i]/T (where T is the sum of the numbers in p[])—and change(i, v)—change the value of p[i] to v. Hint: Use a complete binary tree where each node has implied weight p[i]. Store in each node the cumulative weight of all the nodes in its subtree. To generate a random index, pick a random number between 0 and T and use the cumulative weights to determine which branch of the subtree to explore. When updating p[i], change all of the weights of the nodes on the path from the root to i. Avoid explicit pointers, as we do for heaps.

Answer:
package chapter2.section4;

import edu.princeton.cs.algs4.StdOut;
import edu.princeton.cs.algs4.StdRandom;

import java.util.HashMap;
import java.util.Map;

/**
 * Created by Rene Argento on 27/03/17.
 */
// Thanks to dragon-dreamer (https://github.com/dragon-dreamer) for fixing the random() method.
// https://github.com/reneargento/algorithms-sedgewick-wayne/issues/116
public class Sample {

    private class Node {
        double weight;
        double cumulativeWeight;

        public Node(double weight) {
            this.weight = weight;
        }
    }

    private Node[] nodes;
    private double sum = 0;

    public Sample(double[] probabilities) {
        nodes = new Node[probabilities.length + 1];

        for (int i = 1; i <= probabilities.length; i++) {
            double weight = probabilities[i - 1];
            nodes[i] = new Node(weight);
            sum += weight;
        }
        computeCumulativeWeights();
    }

    private void computeCumulativeWeights() {
        for (int i = nodes.length - 1; i >= 2; i--) {
            nodes[i / 2].cumulativeWeight += nodes[i].cumulativeWeight + nodes[i].weight;
        }
    }

    public int random() {
        double randomValue = StdRandom.uniform(0, sum);
        int index = 1;

        while (randomValue < nodes[index].cumulativeWeight) {
            index *= 2;
            double leftSubtreeWeight = nodes[index].cumulativeWeight + nodes[index].weight;

            if (randomValue >= leftSubtreeWeight) {
                randomValue -= leftSubtreeWeight;
                index++;
            }
        }
        return index - 1;
    }

    public void change(int index, double value) {
        index++;
        double difference = value - nodes[index].weight;
        nodes[index].weight = value;

        sum += difference;
        updateCumulativeWeights(index / 2, difference);
    }

    private void updateCumulativeWeights(int index, double difference) {
        while (index >= 1) {
            nodes[index].cumulativeWeight += difference;
            index /= 2;
        }
    }

    public static void main(String[] args) {
        double[] weights = { 5, 1, 3, 4, 2, 20 };
        Sample sampling = new Sample(weights);

        sampling.change(5, 5);

        Map<Integer, Integer> result = new HashMap<>();
        for (int i = 0; i < 20000; i++) {
            int index = sampling.random();
            result.put(index, result.getOrDefault(index, 0) + 1);
        }

        for (Map.Entry<Integer, Integer> entry : result.entrySet()) {
            StdOut.println("Key = " + entry.getKey() +
                    " (value = " + weights[entry.getKey()] +
                    ") count = " + entry.getValue());
        }
    }
}


Question:
Consider the following implementation of the compareTo() method for String. How does the third line help with efficiency?
public int compareTo(String that)
{
 if (this == that) return 0; // this line
 int n = Math.min(this.length(), that.length());
 for (int i = 0; i < n; i++)
 {
 if (this.charAt(i) < that.charAt(i)) return -1;
 else if (this.charAt(i) > that.charAt(i)) return +1;
 }
 return this.length() - that.length();
}

Answer:
2.5.1

The third line helps with efficiency by checking if the two objects being compared are the same (when both references point to the same object). This verification helps to avoid iterating through all characters on both Strings in the case that both objects are the same.


Question: Write a program that reads a list of words from standard input and prints all two-word compound words in the list. For example, if after, thought, and afterthought are in the list, then afterthought is a compound word.

Answer:
package chapter2.section5;

import edu.princeton.cs.algs4.StdOut;

import java.util.*;

/**
 * Created by Rene Argento on 09/04/17.
 */
// Thanks to Vivek Bhojawala (https://github.com/VBhojawala) for suggesting a simpler code to solve this exercise.
// https://github.com/reneargento/algorithms-sedgewick-wayne/issues/15
public class Exercise2 {

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        ArrayList<String> wordList = new ArrayList<>();

        /**
         * Testcase
         *
         begin
         end
         test
         afterthought
         Brazil
         thought
         after
         beginend
         abc
         */

        // Expected:
        // afterthought
        // beginend
        while (scanner.hasNext()) {
            wordList.add(scanner.next());
        }

        Exercise2 exercise2 = new Exercise2();
        List<String> compoundWords = exercise2.getCompoundWords(wordList);

        if (compoundWords.size() > 0) {
            StdOut.println("Compound words:");

            for (String compoundWord : compoundWords) {
                StdOut.println(compoundWord);
            }
        }
    }

    // O(n^2)
    private List<String> getCompoundWords(ArrayList<String> wordList) {
        Collections.sort(wordList);
        Set<String> wordsSet = new HashSet<>(wordList);

        List<String> compoundWords = new ArrayList<>();

        for (int i = 0; i < wordList.size(); i++) {
            for (int j = i + 1; j < wordList.size(); j++) {
                if (wordList.get(j).startsWith(wordList.get(i))) {

                    String restOfTheWord = wordList.get(j).substring(wordList.get(i).length());
                    if (wordsSet.contains(restOfTheWord)) {
                        compoundWords.add(wordList.get(j));
                    }
                } else {
                    break;
                }
            }
        }
        return compoundWords;
    }
}


Question:
Criticize the following implementation of a class intended to represent account balances. Why is compareTo() a flawed implementation of the Comparable interface?
public class Balance implements Comparable<Balance>
{
 ...
 private double amount;
 public int compareTo(Balance that)
 {
 if (this.amount < that.amount - 0.005) return -1;
 if (this.amount > that.amount + 0.005) return +1;
 return 0;
 }
 ...
}
Describe a way to fix this problem.

Answer:
2.5.3

The compareTo() is a flawed implementation of the Comparable interface because it violates the Comparable contract.
The contract says that if x.compareTo(y) == 0 then the sign of x.compareTo(z) must be equal to the sign of y.compareTo(z), for all z.
In the implementation described in the exercise, if x = 0.001, y = 0.005 and z = 0.007, the rule will be violated:
x.compareTo(y) will be equal to 0, x.compareTo(z) will have a negative sign and y.compareTo(z) will be equal to zero.

In other words, it is possible that x.compareTo(y) and y.compareTo(z) are both 0, but x.compareTo(z) is negative (or positive).

Another issue is that floating point numbers cannot be represented with exact precision in binary with java's double primitive data type.
Critical information such as account balances should be stored in BigDecimal objects instead.
This will also allow direct comparison between both values to work correctly:

public class Balance implements Comparable<Balance> {
    ...
    private BigDecimal amount;
    public int compareTo(Balance that) {
        return this.amount.compareTo(that.amount);
    }
    @Override
    public boolean equals(Object object) {
        if (object instanceof Balance) {
            return this.amount.compareTo(((Balance) object).amount) == 0;
        }
        return false;
    }
    ...
}

For the BigDecimal class we also have to override the equals method to guarantee that it is consistent with compareTo().
This ensures that values with the same amount but different scales (such as 1.0 and 1.00) are considered equivalent, adhering to the Comparable contract.

Thanks to dragon-dreamer (https://github.com/dragon-dreamer) for showing that using double values is not the best approach to represent the balance amount.
https://github.com/reneargento/algorithms-sedgewick-wayne/issues/117


Question: Implement a method String[] dedup(String[] a) that returns the objects in a[] in sorted order, with duplicates removed.

Answer:
package chapter2.section5;

import edu.princeton.cs.algs4.StdOut;

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

/**
 * Created by Rene Argento on 09/04/17.
 */
public class Exercise4 {

    public static void main(String[] args) {
        String[] input = {"Algorithms", "Sedgewick", "Wayne", "Argento", "Djikstra", "Wayne", "Argento", "Prim"};

        Exercise4 exercise4 = new Exercise4();
        String[] dedupStrings = exercise4.dedup(input);

        StdOut.println("Strings:");
        for (String string : dedupStrings) {
            StdOut.println(string);
        }

        StdOut.println();
        StdOut.println("Expected: \n" +
                "Algorithms\n" +
                "Argento\n" +
                "Djikstra\n" +
                "Prim\n" +
                "Sedgewick\n" +
                "Wayne");
    }

    private String[] dedup(String[] strings) {
        if (strings == null || strings.length == 0) {
            return new String[0];
        }

        Arrays.sort(strings);

        List<String> dedupStringList = new ArrayList<>();

        String currentString = strings[0];
        dedupStringList.add(strings[0]);

        for (int i = 1; i < strings.length; i++) {
            if (strings[i].equals(currentString)) {
                continue;
            } else {
                currentString = strings[i];
            }

            dedupStringList.add(strings[i]);
        }

        String[] dedupStringArray = new String[dedupStringList.size()];
        for (int i = 0; i < dedupStringArray.length; i++) {
            dedupStringArray[i] = dedupStringList.get(i);
        }

        return dedupStringArray;
    }
}


Question: Explain why selection sort is not stable.

Answer:
2.5.5

Selection sort is not stable because it exchanges nonadjacent elements.
On the example below, the first B gets swapped to the right of the second B.

i  min   0   1   2
0    2  B1  B2   A
1    1   A  B2  B1
2    2   A  B2  B1
         A  B2  B1


Question: Implement a recursive version of select().

Answer:
package chapter2.section5;

import edu.princeton.cs.algs4.StdOut;
import edu.princeton.cs.algs4.StdRandom;
import util.ArrayUtil;

/**
 * Created by Rene Argento on 09/04/17.
 */
// Thanks to ckwastra (https://github.com/ckwastra) for reporting a bug in recursiveSelect().
// https://github.com/reneargento/algorithms-sedgewick-wayne/issues/265
@SuppressWarnings("unchecked")
public class Exercise6 {

    public static void main(String[] args) {
        Comparable[] array = { 1, 9, 2, 8, 3, 7, 4, 6, 5, 0 };

        Exercise6 exercise6 = new Exercise6();
        Comparable<Integer> selectedItem1 = exercise6.recursiveSelect(array, 4);
        Comparable<Integer> selectedItem2 = exercise6.recursiveSelect(array, 1);
        Comparable<Integer> selectedItem3 = exercise6.recursiveSelect(array, 0);
        Comparable<Integer> selectedItem4 = exercise6.recursiveSelect(array, 9);

        StdOut.println("Element at index 4: " + selectedItem1 + " Expected: 4");
        StdOut.println("Element at index 1: " + selectedItem2 + " Expected: 1");
        StdOut.println("Element at index 0: " + selectedItem3 + " Expected: 0");
        StdOut.println("Element at index 9: " + selectedItem4 + " Expected: 9");
    }

    private Comparable<Integer> recursiveSelect(Comparable<Integer>[] array, int index) {
        if (index >= array.length) {
            throw new IllegalArgumentException("Index must be smaller than array size");
        }
        StdRandom.shuffle(array);
        return recursiveSelect(array, index, 0, array.length - 1);
    }

    private Comparable<Integer> recursiveSelect(Comparable<Integer>[] array, int index, int low, int high) {
        if (low == high) {
            return array[low];
        }

        int pivotIndex = partition(array, low, high);
        if (pivotIndex == index) {
            return array[index];
        } else {
            if (pivotIndex < index) {
                return recursiveSelect(array, index, pivotIndex + 1, high);
            } else {
                return recursiveSelect(array, index, low, pivotIndex - 1);
            }
        }
    }

    private int partition(Comparable<Integer>[] array, int low, int high) {
        Comparable<Integer> pivot = array[low];
        int i = low;
        int j = high + 1;

        while (true) {
            while (ArrayUtil.less(array[++i], pivot)) {
                if (i == high) {
                    break;
                }
            }

            while (ArrayUtil.more(array[--j], pivot)) {
                if (j == low) {
                    break;
                }
            }

            if (i >= j) {
                break;
            }
            ArrayUtil.exchange(array, i, j);
        }

        ArrayUtil.exchange(array, low, j);
        return j;
    }
}


Question: About how many compares are required, on the average, to find the smallest of N items using select()?

Answer:
2.5.7

On the average, to find the smallest of N items using select(), it is required 2N + 2 * lnN + (2N - 2) * ln(N / (N - 1)) compares.
This follows from proposition U in the book that says that the average number of compares to find the kth item in a shuffled array is ~2N + 2k * ln(N / k) + 2(N - k) * ln(N / (N - k)) and replacing k for 1.

Thanks to ckwastra (https://github.com/ckwastra) for suggesting a fix to the compare formula.
https://github.com/reneargento/algorithms-sedgewick-wayne/issues/266


Question: Write a program Frequency that reads strings from standard input and prints the number of times each string occurs, in descending order of frequency.

Answer:
package chapter2.section5;

import edu.princeton.cs.algs4.StdOut;

import java.util.*;

/**
 * Created by Rene Argento on 09/04/17.
 */
public class Exercise8 {

    private class StringFrequency implements Comparable<StringFrequency>{
        String string;
        int frequency;

        StringFrequency(String string, int frequency) {
            this.string = string;
            this.frequency = frequency;
        }

        @Override
        public int compareTo(StringFrequency that) {
            if (this.frequency > that.frequency) {
                return -1;
            } else if (this.frequency < that.frequency) {
                return 1;
            } else {
                return 0;
            }
        }
    }

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        List<String> stringList = new ArrayList<>();

        /**
         * Testcase
         *
         test
         test
         begin
         begin
         test
         begin
         abc
         abc
         test
         end
         */
        while (scanner.hasNext()) {
            stringList.add(scanner.next());
        }

        Exercise8 exercise8 = new Exercise8();
        StringFrequency[] frequencies = exercise8.frequency2(stringList);
        for (StringFrequency frequency : frequencies) {
            StdOut.println(frequency.string + " " + frequency.frequency + " occurrence(s)");
        }

        StdOut.println();
        StdOut.println("Expected: \n" +
                "test 4 occurrence(s)\n" +
                "begin 3 occurrence(s)\n" +
                "abc 2 occurrence(s)\n" +
                "end 1 occurrence(s)");
    }

    private StringFrequency[] frequency(List<String> strings) {
        Map<String, Integer> occurrenciesMap = new HashMap<>();

        for (String string : strings) {
            int frequency = 0;

            if (occurrenciesMap.containsKey(string)) {
                frequency = occurrenciesMap.get(string);
            }

            frequency++;
            occurrenciesMap.put(string, frequency);
        }

        StringFrequency[] stringFrequencies = new StringFrequency[occurrenciesMap.size()];
        int stringFrequenciesIndex = 0;

        for (String key : occurrenciesMap.keySet()) {
            int frequency = occurrenciesMap.get(key);

            StringFrequency stringFrequency = new StringFrequency(key, frequency);
            stringFrequencies[stringFrequenciesIndex++] = stringFrequency;
        }

        Arrays.sort(stringFrequencies);
        return stringFrequencies;
    }

    //Optimized for space - no need to use a HashMap
    //Based on http://algs4.cs.princeton.edu/25applications/Frequency.java.html
    private StringFrequency[] frequency2(List<String> strings) {

        StringFrequency[] stringFrequencies = new StringFrequency[strings.size()];
        int stringFrequenciesIndex = 0;

        Collections.sort(strings);
        String currentString = strings.get(0);
        int frequency = 1;

        for (int i = 1; i < strings.size(); i++) {
            if (!currentString.equals(strings.get(i))) {
                stringFrequencies[stringFrequenciesIndex++] = new StringFrequency(currentString, frequency);
                currentString = strings.get(i);
                frequency = 1;
            } else {
                frequency++;
            }
        }

        stringFrequencies[stringFrequenciesIndex++] = new StringFrequency(currentString, frequency);

        Arrays.sort(stringFrequencies, 0, stringFrequenciesIndex);
        StringFrequency[] stringFrequenciesOutput = new StringFrequency[stringFrequenciesIndex];
        System.arraycopy(stringFrequencies, 0, stringFrequenciesOutput, 0, stringFrequenciesIndex);

        return stringFrequenciesOutput;
    }
}


Question:
Develop a data type that allows you to write a client that can sort stock-volume records such as the following by volume:

```text
1-Oct-28   3500000
2-Oct-28   3850000
3-Oct-28   4060000
```

Each input line contains a date and a volume; the natural order of the data type should be by volume.

Answer:
package chapter2.section5;

import edu.princeton.cs.algs4.StdOut;

import java.util.Arrays;

/**
 * Created by Rene Argento on 09/04/17.
 */
public class Exercise9 {

    class VolumesPerDay implements Comparable<VolumesPerDay>{
        private String date;
        private long volume;

        VolumesPerDay(String date, long volume) {
            this.date = date;
            this.volume = volume;
        }

        @Override
        public int compareTo(VolumesPerDay that) {
            if (this.volume < that.volume) {
                return -1;
            } else if (this.volume > that.volume) {
                return 1;
            } else {
                return 0;
            }
        }

        @Override
        public String toString() {
            return date + "   " + volume;
        }
    }

    public static void main(String[] args) {
        Exercise9 exercise9 = new Exercise9();
        VolumesPerDay volumesPerDay1 = exercise9.new VolumesPerDay("1-Oct-28", 2775559936L);
        VolumesPerDay volumesPerDay2 = exercise9.new VolumesPerDay("2-Oct-28", 500);
        VolumesPerDay volumesPerDay3 = exercise9.new VolumesPerDay("3-Oct-28", 1000);

        VolumesPerDay[] volumesPerDays = new VolumesPerDay[3];
        volumesPerDays[0] = volumesPerDay1;
        volumesPerDays[1] = volumesPerDay2;
        volumesPerDays[2] = volumesPerDay3;

        Arrays.sort(volumesPerDays);

        for (VolumesPerDay volumesPerDay : volumesPerDays) {
            StdOut.println(volumesPerDay);
        }

        StdOut.println("\nExpected:\n" +
                "2-Oct-28   500\n" +
                "3-Oct-28   1000\n" +
                "1-Oct-28   2775559936");
    }
}


Question: Create a data type Version that represents a software version number, such as 115.1.1, 115.10.1, 115.10.2. Implement the Comparable interface so that 115.1.1 is less than 115.10.1, and so forth.

Answer:
package chapter2.section5;

import edu.princeton.cs.algs4.StdOut;

/**
 * Created by Rene Argento on 09/04/17.
 */
public class Exercise10 {

    class Version implements Comparable<Version>{

        private String version;

        Version(String version) {
            String[] versionSplit = version.split("\\.");
            if (versionSplit.length < 3) {
                throw new IllegalArgumentException("Incorrect version format. A version requires 3 parts such as 115.10.1");
            }

            this.version = version;
        }

        @Override
        public int compareTo(Version that) {
            String[] splitVersion = version.split("\\.");
            String[] otherSplitVersion = that.version.split("\\.");

            if (Integer.parseInt(splitVersion[0]) < Integer.parseInt(otherSplitVersion[0])) {
                return -1;
            } else if (Integer.parseInt(splitVersion[0]) > Integer.parseInt(otherSplitVersion[0])) {
                return 1;
            } else if (Integer.parseInt(splitVersion[1]) < Integer.parseInt(otherSplitVersion[1])) {
                return -1;
            } else if (Integer.parseInt(splitVersion[1]) > Integer.parseInt(otherSplitVersion[1])) {
                return 1;
            } else if (Integer.parseInt(splitVersion[2]) < Integer.parseInt(otherSplitVersion[2])) {
                return -1;
            } else if (Integer.parseInt(splitVersion[2]) > Integer.parseInt(otherSplitVersion[2])) {
                return 1;
            } else {
                return 0;
            }
        }

        @Override
        public String toString() {
            return version;
        }
    }

    public static void main(String[] args) {
        Exercise10 exercise10 = new Exercise10();
        Version version1 = exercise10.new Version("115.1.1");
        Version version2 = exercise10.new Version("115.10.1");
        Version version3 = exercise10.new Version("115.10.2");

        if (version1.compareTo(version2) < 0) {
            StdOut.println(version1 + " is less than " + version2 + " - Correct!");
        } else {
            StdOut.println(version1 + " is more than " + version2 + " - Wrong!");
        }
        StdOut.println("Expected: Correct!\n");

        if (version2.compareTo(version3) < 0) {
            StdOut.println(version2 + " is less than " + version3 + " - Correct!");
        } else {
            StdOut.println(version2 + " is more than " + version3 + " - Wrong!");
        }
        StdOut.println("Expected: Correct!");
    }

}


Question: Load balancing. Write a program LPT.java that takes an integer M as a command-line argument, reads job names and processing times from standard input and prints a schedule assigning the jobs to M processors that approximately minimizes the time when the last job completes using the longest processing time first rule, as described on page 349.

Answer:
package chapter2.section5;

import edu.princeton.cs.algs4.StdIn;
import edu.princeton.cs.algs4.StdOut;

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

/**
 * Created by Rene Argento on 10/04/17.
 */
//The resulting solution is guaranteed to be within 33% of the best possible (actually 4/3 - 1/(3N)).
public class Exercise13_LoadBalancing {

    private class Job implements Comparable<Job> {

        private String name;
        private int processingTime;

        Job(String name, int processingTime) {
            this.name = name;
            this.processingTime = processingTime;
        }

        @Override
        //Longest processing time first rule
        public int compareTo(Job that) {
            if (this.processingTime > that.processingTime) {
                return -1;
            } else if (this.processingTime < that.processingTime) {
                return 1;
            } else {
                return 0;
            }
        }

        @Override
        public String toString() {
            return name + "   " + processingTime;
        }
    }

    private class Processor implements Comparable<Processor> {
        private String name;
        private List<Job> jobsAssigned;
        private int sumOfJobsAssignedProcessingTime;

        Processor(String name) {
            this.name = name;
            this.sumOfJobsAssignedProcessingTime = 0;
            jobsAssigned = new ArrayList<>();
        }

        void assignJob(Job job) {
            jobsAssigned.add(job);
        }

        @Override
        public int compareTo(Processor that) {
            if (this.sumOfJobsAssignedProcessingTime < that.sumOfJobsAssignedProcessingTime) {
                return -1;
            } else if (this.sumOfJobsAssignedProcessingTime > that.sumOfJobsAssignedProcessingTime) {
                return 1;
            } else {
                return 0;
            }
        }

        @Override
        public String toString() {
            return name;
        }
    }

    public static void main(String[] args) {
        int numberOfProcessors = Integer.parseInt(args[0]); //testcase: numberOfProcessors = 3

        new Exercise13_LoadBalancing().lpt(numberOfProcessors);
    }

    private void lpt(int numberOfProcessors) {

        /**
         * Test case
         *
         * JobA 100
         * JobB 1
         * JobC 999
         * JobD 1000
         * JobE 0
         * JobF 999999999
         */
        String[] input = StdIn.readAllLines();
        Job[] jobs = new Job[input.length];
        int jobsIndex = 0;

        for (String jobString : input) {
            String[] jobDescription = jobString.split(" ");
            String name = jobDescription[0];
            int processingTime = Integer.parseInt(jobDescription[1]);

            jobs[jobsIndex++] = new Job(name, processingTime);
        }

        Arrays.sort(jobs);

        PriorityQueue<Processor> heap = new PriorityQueue<>();
        for (int i = 0; i < numberOfProcessors; i++) {
            Processor processor = new Processor("Processor " + i);
            heap.add(processor);
        }

        loadBalanceAndPrintSchedule(jobs, heap);

        StdOut.println();
        StdOut.println("Expected:");
        StdOut.println("JobF assigned to Processor 0\n" +
                "JobD assigned to Processor 2\n" +
                "JobC assigned to Processor 1\n" +
                "JobA assigned to Processor 1\n" +
                "JobB assigned to Processor 2\n" +
                "JobE assigned to Processor 2");
    }

    private void loadBalanceAndPrintSchedule(Job[] jobs, PriorityQueue<Processor> heap) {
        for (int i = 0; i < jobs.length; i++) {
            Processor nextProcessorAvailable = heap.remove();
            // Assign job to the next available processor
            nextProcessorAvailable.assignJob(jobs[i]);
            nextProcessorAvailable.sumOfJobsAssignedProcessingTime += jobs[i].processingTime;

            StdOut.println(jobs[i].name + " assigned to " + nextProcessorAvailable.name);

            heap.add(nextProcessorAvailable);
        }
    }
}


Question: Sort by reverse domain. Write a data type Domain that represents domain names, including an appropriate compareTo() method where the natural order is in order of the reverse domain name. For example, the reverse domain of cs.princeton.edu is edu.princeton.cs. This is useful for web log analysis. Hint: Use s.split("\\.") to split the string s into tokens, delimited by dots. Write a client that reads domain names from standard input and prints the reverse domains in sorted order.

Answer:
package chapter2.section5;

import edu.princeton.cs.algs4.StdIn;
import edu.princeton.cs.algs4.StdOut;

import java.util.Arrays;

/**
 * Created by Rene Argento on 10/04/17.
 */
public class Exercise14_SortByReverseDomain {

    private class Domain implements Comparable<Domain>{

        String domainName;
        String reverseDomainName;

        Domain(String domainName) {
            this.domainName = domainName;

            String[] reverseDomain = domainName.split("\\.");
            StringBuilder reverseDomainConcatenation = new StringBuilder();
            for (int i = reverseDomain.length - 1; i >= 0; i--) {
                reverseDomainConcatenation.append(reverseDomain[i]);

                if (i != 0) {
                    reverseDomainConcatenation.append(".");
                }
            }
            this.reverseDomainName = reverseDomainConcatenation.toString();
        }

        @Override
        public int compareTo(Domain that) {
            return this.reverseDomainName.compareTo(that.reverseDomainName);
        }
    }

    public static void main(String[] args) {
        Exercise14_SortByReverseDomain sortByReverseDomain = new Exercise14_SortByReverseDomain();

        /**
         * Test case
         *
         * cs.princeton.edu
         * www.google.com
         * www.rene.argento
         * www.somewebsite.gov
         * cs.harvard.edu
         */
        String[] domainNames = StdIn.readAllLines();
        Domain[] domains = new Domain[domainNames.length];

        for (int i = 0; i < domainNames.length; i++) {
            Domain domain = sortByReverseDomain.new Domain(domainNames[i]);
            domains[i] = domain;
        }

        Arrays.sort(domains);

        for (Domain domain : domains) {
            StdOut.println(domain.reverseDomainName);
        }

        StdOut.println();
        StdOut.println("Expected:");
        StdOut.println("argento.rene.www\n" +
                "com.google.www\n" +
                "edu.harvard.cs\n" +
                "edu.princeton.cs\n" +
                "gov.somewebsite.www");
    }
}


Question: Spam campaign. To initiate an illegal spam campaign, you have a list of email addresses from various domains (the part of the email address that follows the @ symbol). To better forge the return addresses, you want to send the email from another user at the same domain. For example, you might want to forge an email from wayne@princeton.edu to rs@princeton.edu. How would you process the email list to make this an efficient task?

Answer:
2.5.15 - Spam campaign

I would sort the email list by the reverse domain.
Then I would choose emails among the same domain to serve as sender and receiver and repeat this process for each domain.


Question: Idle time. Suppose that a parallel machine processes N jobs. Write a program that, given the list of job start and finish times, finds the largest interval where the machine is idle and the largest interval where the machine is not idle.

Answer:
package chapter2.section5;

import edu.princeton.cs.algs4.StdOut;

import java.util.Arrays;

/**
 * Created by Rene Argento on 12/04/17.
 */
public class Exercise20_IdleTime {

    private class Job implements Comparable<Job> {

        private int startTime;
        private int endTime;

        Job(int startTime, int endTime) {
            this.startTime = startTime;
            this.endTime = endTime;
        }

        @Override
        public int compareTo(Job that) {
            if (this.startTime < that.startTime) {
                return -1;
            } else if (this.startTime > that.startTime) {
                return 1;
            } else {
                if (this.endTime < that.endTime) {
                    return -1;
                } else if (this.endTime > that.endTime) {
                    return 1;
                }
            }

            return 0;
        }
    }

    public static void main(String[] args) {

        Exercise20_IdleTime idleTime = new Exercise20_IdleTime();

        Job job1 = idleTime.new Job(5, 6);
        Job job2 = idleTime.new Job(12, 14);
        Job job3 = idleTime.new Job(1, 3);
        Job job4 = idleTime.new Job(9, 12);
        Job job5 = idleTime.new Job(15, 16);

        Job[] jobs = {job1, job2, job3, job4, job5};

        int[] intervals = idleTime.getAsynchronousJobsIntervals(jobs);

        StdOut.println("Largest idle interval: " + intervals[0] + " to " + intervals[1]);
        StdOut.println("Largest non-idle interval: " + intervals[2] + " to " + intervals[3]);

        StdOut.println("\nExpected");
        StdOut.println("Largest idle interval: 6 to 9");
        StdOut.println("Largest non-idle interval: 9 to 14");

        Job job6 = idleTime.new Job(1, 10);
        Job job7 = idleTime.new Job(6, 10);
        Job job8 = idleTime.new Job(2, 4);
        Job job9 = idleTime.new Job(5, 12);
        Job job10 = idleTime.new Job(15, 17);
        Job job11 = idleTime.new Job(16, 20);

        Job[] jobs2 = {job6, job7, job8, job9, job10, job11};

        int[] intervals2 = idleTime.getAsynchronousJobsIntervals(jobs2);

        StdOut.println("\nLargest idle interval: " + intervals2[0] + " to " + intervals2[1]);
        StdOut.println("Largest non-idle interval: " + intervals2[2] + " to " + intervals2[3]);

        StdOut.println("\nExpected");
        StdOut.println("Largest idle interval: 12 to 15");
        StdOut.println("Largest non-idle interval: 1 to 12");
    }

    private int[] getAsynchronousJobsIntervals(Job[] jobs) {

        if (jobs == null || jobs.length == 0) {
            return new int[]{0, 0, 0, 0};
        }

        int currentIntervalStartIndex = 0;

        int largestIdleTimeStartIndex = 0;
        int largestIdleTimeEndIndex = 0;
        int largestBusyTimeStartIndex = 0;
        int largestBusyTimeEndIndex = 0;

        Arrays.sort(jobs);

        int currentMaxEndTime = jobs[0].endTime;
        int currentMaxEndTimeIndex = 0;

        int maxIdleTime = jobs[0].startTime;

        for (int i = 0; i < jobs.length; i++) {

            if (i != 0 && jobs[i].startTime > currentMaxEndTime) {

                //A new interval is beginning
                if (jobs[i].startTime - currentMaxEndTime > maxIdleTime) {
                    largestIdleTimeEndIndex = i;
                    largestIdleTimeStartIndex = currentMaxEndTimeIndex;

                    maxIdleTime = jobs[largestIdleTimeEndIndex].startTime - jobs[largestIdleTimeStartIndex].endTime;
                }

                currentIntervalStartIndex = i;
            }

            if (jobs[i].endTime - jobs[currentIntervalStartIndex].startTime >
                    jobs[largestBusyTimeEndIndex].endTime - jobs[largestBusyTimeStartIndex].startTime) {
                largestBusyTimeStartIndex = currentIntervalStartIndex;
                largestBusyTimeEndIndex = i;
            }

            if (jobs[i].endTime > currentMaxEndTime) {
                currentMaxEndTime = jobs[i].endTime;
                currentMaxEndTimeIndex = i;
            }
        }

        int largestIdleTimeStart = jobs[largestIdleTimeStartIndex].endTime;
        int largestIdleTimeEnd = jobs[largestIdleTimeEndIndex].startTime;

        // Edge case - when the largest idle interval is from time 0 to the beginning of the first job
        if (largestIdleTimeStart > largestIdleTimeEnd) {
            largestIdleTimeStart = 0;
        }

        int largestBusyTimeStart = jobs[largestBusyTimeStartIndex].startTime;
        int largestBusyTimeEnd = jobs[largestBusyTimeEndIndex].endTime;

        return new int[]{largestIdleTimeStart, largestIdleTimeEnd, largestBusyTimeStart, largestBusyTimeEnd};
    }

    // Only used for synchronous jobs
    private int[] getSynchronousJobsIntervals(Job[] jobs) {

        if (jobs == null || jobs.length == 0) {
            return new int[]{0, 0, 0, 0};
        }

        int currentIntervalStartIndex = 0;

        int largestIdleTimeStartIndex = 0;
        int largestIdleTimeEndIndex = 0;
        int largestBusyTimeStartIndex = 0;
        int largestBusyTimeEndIndex = 0;

        Arrays.sort(jobs);

        int maxIdleTime = jobs[0].startTime;

        for (int i = 0; i < jobs.length; i++) {

            if (i != 0 && jobs[i].startTime > jobs[i - 1].endTime) {

                // A new interval is beginning
                if (jobs[i].startTime - jobs[i - 1].endTime > maxIdleTime) {
                    largestIdleTimeEndIndex = i;
                    largestIdleTimeStartIndex = i-1;

                    maxIdleTime = jobs[largestIdleTimeEndIndex].startTime - jobs[largestIdleTimeStartIndex].endTime;
                }

                currentIntervalStartIndex = i;
            }

            if (jobs[i].endTime - jobs[currentIntervalStartIndex].startTime >
                    jobs[largestBusyTimeEndIndex].endTime - jobs[largestBusyTimeStartIndex].startTime) {
                largestBusyTimeStartIndex = currentIntervalStartIndex;
                largestBusyTimeEndIndex = i;
            }
        }

        int largestIdleTimeStart = jobs[largestIdleTimeStartIndex].endTime;
        int largestIdleTimeEnd = jobs[largestIdleTimeEndIndex].startTime;

        // Edge case - when the largest idle interval is from time 0 to the beginning of the first job
        if (largestIdleTimeStart > largestIdleTimeEnd) {
            largestIdleTimeStart = 0;
        }

        int largestBusyTimeStart = jobs[largestBusyTimeStartIndex].startTime;
        int largestBusyTimeEnd = jobs[largestBusyTimeEndIndex].endTime;

        return new int[]{largestIdleTimeStart, largestIdleTimeEnd, largestBusyTimeStart, largestBusyTimeEnd};
    }
}


Question: Sampling for selection. Investigate the idea of using sampling to improve selection. Hint: Using the median may not always be helpful.

Answer:
2.5.23 - Sampling for selection

Sampling improves selection in cases where the element searched is one of the smallest or highest values in the array.
For example, when searching for the 2nd smallest element in an array of size 10^9, selecting one of the smallest elements as the pivot in the initial steps will yield better performance than always choosing the median.
This can discard 75%+ of the array, improving selection performance.


Question: Stable priority queue. Develop a stable priority-queue implementation (which returns duplicate keys in the same order in which they were inserted).

Answer:
package chapter2.section5;

import edu.princeton.cs.algs4.StdOut;

/**
 * Created by Rene Argento on 15/04/17.
 */
//Based on http://algs4.cs.princeton.edu/25applications/StableMinPQ.java.html
@SuppressWarnings("unchecked")
public class Exercise24_StablePriorityQueue {

    public enum Orientation {
        MAX, MIN;
    }

    private class PriorityQueueStable<Key extends Comparable<Key>> {

        private Key[] priorityQueue;
        private int size = 0; // in priorityQueue[1..n] with pq[0] unused
        private Orientation orientation;

        private long[] timestamp;
        private int currentTimestamp = 0;

        PriorityQueueStable(Orientation orientation) {
            priorityQueue = (Key[]) new Comparable[2];
            timestamp = new long[2];
            this.orientation = orientation;
        }

        public boolean isEmpty() {
            return size == 0;
        }

        public int size() {
            return size;
        }

        public Key peek() {
            return priorityQueue[1];
        }

        public void insert(Key key) {

            if (size == priorityQueue.length - 1) {
                resize(priorityQueue.length * 2);
            }

            size++;
            priorityQueue[size] = key;
            timestamp[size] = ++currentTimestamp;

            swim(size);
        }

        public Key deleteTop() {

            if (size == 0) {
                throw new RuntimeException("Priority queue underflow");
            }

            size--;

            Key top = priorityQueue[1];

            exchange(1, size + 1);
            priorityQueue[size + 1] = null;
            timestamp[size + 1] = 0;

            sink(1);

            if (size == priorityQueue.length / 4) {
                resize(priorityQueue.length / 2);
            }

            return top;
        }

        private void swim(int index) {
            while (index / 2 >= 1) {
                if ((orientation == Orientation.MAX && less(index / 2, index))
                        || (orientation == Orientation.MIN && more(index / 2, index))) {
                    exchange(index / 2, index);
                } else {
                    break;
                }

                index = index / 2;
            }
        }

        private void sink(int index) {
            while (index * 2 <= size) {
                int selectedChildIndex = index * 2;

                if (index * 2 + 1 <= size &&
                        ((orientation == Orientation.MAX && less(index * 2, index * 2 + 1))
                                || (orientation == Orientation.MIN && more(index * 2, index * 2 + 1)))) {
                    selectedChildIndex = index * 2 + 1;
                }

                if ((orientation == Orientation.MAX && more(selectedChildIndex, index))
                        || (orientation == Orientation.MIN && less(selectedChildIndex, index))) {
                    exchange(index, selectedChildIndex);
                } else {
                    break;
                }

                index = selectedChildIndex;
            }
        }

        private void resize(int newSize) {
            Key[] newPriorityQueue = (Key[]) new Comparable[newSize];
            System.arraycopy(priorityQueue, 1, newPriorityQueue, 1, size);
            priorityQueue = newPriorityQueue;

            long[] newTimestamp = new long[newSize];
            System.arraycopy(timestamp, 1, newTimestamp, 1, size);
            timestamp = newTimestamp;
        }

        private boolean less(int key1Index, int key2Index) {
            int compare = priorityQueue[key1Index].compareTo(priorityQueue[key2Index]);

            if (compare < 0) {
                return true;
            } else if (compare > 0) {
                return false;
            } else {
                return timestamp[key1Index] < timestamp[key2Index];
            }
        }

        private boolean more(int key1Index, int key2Index) {
            int compare = priorityQueue[key1Index].compareTo(priorityQueue[key2Index]);

            if (compare > 0) {
                return true;
            } else if (compare < 0) {
                return false;
            } else {
                return timestamp[key1Index] > timestamp[key2Index];
            }
        }

        private void exchange(int key1Index, int key2Index) {
            Key tempKey = priorityQueue[key1Index];
            priorityQueue[key1Index] = priorityQueue[key2Index];
            priorityQueue[key2Index] = tempKey;

            long tempTimestamp = timestamp[key1Index];
            timestamp[key1Index] = timestamp[key2Index];
            timestamp[key2Index] = tempTimestamp;
        }
    }

    private class Tuple implements Comparable<Tuple> {

        private String value;
        private int id;

        Tuple(String value, int id) {
            this.value = value;
            this.id = id;
        }

        @Override
        public int compareTo(Tuple that) {
            return this.value.compareTo(that.value);
        }

        @Override
        public String toString() {
            return value + " " + id;
        }
    }

    public static void main(String[] args) {
        Exercise24_StablePriorityQueue stablePriorityQueue = new Exercise24_StablePriorityQueue();
        PriorityQueueStable<Tuple> priorityQueueStable = stablePriorityQueue.new PriorityQueueStable<>(Orientation.MIN);

        // Insert a bunch of strings
        String text = "it was the best of times it was the worst of times it was the "
                + "age of wisdom it was the age of foolishness it was the epoch "
                + "belief it was the epoch of incredulity it was the season of light "
                + "it was the season of darkness it was the spring of hope it was the "
                + "winter of despair";

        String[] strings = text.split(" ");

        for (int i = 0; i < strings.length; i++) {
            priorityQueueStable.insert(stablePriorityQueue.new Tuple(strings[i], i));
        }

        // Delete and print each key
        while (!priorityQueueStable.isEmpty()) {
            StdOut.println(priorityQueueStable.deleteTop());
        }
    }
}