Question:
Give the order of growth (as a function of N) of the running times of each of the following code fragments:
a. int sum = 0;
 for (int n = N; n > 0; n /= 2)
 for(int i = 0; i < n; i++)
 sum++;
b. int sum = 0;
 for (int i = 1; i < N; i *= 2)
 for (int j = 0; j < i; j++)
 sum++;
c. int sum = 0;
 for (int i = 1; i < N; i *= 2)
 for (int j = 0; j < N; j++)
 sum++;

Answer:
1.4.6

a. O(N)
b. O(N)
c. O(N log(N))

Thanks to Vivek Bhojawala (https://github.com/VBhojawala) for mentioning the correct answer to the question in letter a.
https://github.com/reneargento/algorithms-sedgewick-wayne/issues/9


Question: Analyze ThreeSum under a cost model that counts arithmetic operations (and comparisons) involving the input numbers.

Answer:
1.4.7

With a cost model that counts arithmetic operations (and comparisons) involving input numbers, the main "if" from
ThreeSum involves 4 operations instead of 1.

if (a[i] + a[j] + a[k] == 0)

One operation for the if check; one operation to sum a[i] and a[j]; one operation to sum the result with a[k]
and one operation to compare the result with 0.

Therefore the main "if" has a frequency of 4 (N^3/6 - N^2/2 + N/3) instead of (N^3/6 - N^2/2 + N/3)

And the grand total becomes:
t1(4N^3/6 - 4N^2/2 + 4N/3) + t2(N^2/2 - N/2) + t3N + t4

Tilde approximation: ~(4t1/6)N^3
Order of growth: N^3


Question: Write a program to determine the number pairs of values in an input file that are equal. If your first try is quadratic, think again and use Arrays.sort() to develop a linearithmic solution.

Answer:
package chapter1.section4;

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

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

/**
 * Created by Rene Argento on 9/28/16.
 */
// Thanks to Vivek Bhojawala (https://github.com/VBhojawala) for mentioning that the solution should use
// Arrays.sort(), as requested in the exercise. https://github.com/reneargento/algorithms-sedgewick-wayne/issues/6

//Quantity of numbers : Quantity of equal pairs
//        2                      1
//        3                      3
//        4                      6
//        5                      10
// The quantity of equal pairs is equal to:
// (n - 1) * n / 2
public class Exercise8 {

    public static void main(String[] args) {
        In in = new In(args[0]);
        int[] values = in.readAllInts();
        StdOut.println(countNumberOfPairs(values));

        // Tests
        int[] values1 = {1, 2, 4, 1, 2, 1, 2, 4, 5, 1, 2, 4, 5, 1, 2 ,5, 6, 7, 7, 8, 2, 1, 2, 4, 5};
        StdOut.println("Equal pairs 1: " + countNumberOfPairs(values1) + " Expected: 49");

        int[] values2 = {1, 1, 1};
        StdOut.println("Equal pairs 2: " + countNumberOfPairs(values2) + " Expected: 3");
    }

    // O(n lg n) solution
    private static int countNumberOfPairs(int[] values) {
        Arrays.sort(values);

        int count = 0;
        int currentFrequency = 1;

        for (int i = 1; i < values.length; i++) {
            if (values[i] == values[i - 1]) {
                currentFrequency++;
            } else {
                if (currentFrequency > 1) {
                    count += (currentFrequency - 1) * currentFrequency / 2;
                    currentFrequency = 1;
                }
            }
        }

        if (currentFrequency > 1) {
            count += (currentFrequency - 1) * currentFrequency / 2;
        }
        return count;
    }

    // O(n) solution
    private static int countNumberOfPairs2(int[] values) {
        Map<Integer, Integer> valuesMap = new HashMap<>();
        int equalNumbersCount = 0;

        for (int i = 0; i < values.length; i++) {
            int count = 0;
            if (valuesMap.containsKey(values[i])) {
                count = valuesMap.get(values[i]);
            }
            count++;
            valuesMap.put(values[i], count);
        }

        for (int numberKey : valuesMap.keySet()) {
            if (valuesMap.get(numberKey) > 1) {
                int n = valuesMap.get(numberKey);
                equalNumbersCount += (n - 1) * n / 2;
            }
        }
        return equalNumbersCount;
    }
}


Question: Modify binary search so that it always returns the element with the smallest index that matches the search element (and still guarantees logarithmic running time).

Answer:
package chapter1.section4;

import edu.princeton.cs.algs4.StdOut;

/**
 * Created by Rene Argento on 9/28/16.
 */
// Thanks to Vivek Bhojawala (https://github.com/VBhojawala) for fixing a bug and suggesting improvements
// in the binarySearch() method at https://github.com/reneargento/algorithms-sedgewick-wayne/issues/6
// Thanks to ajfg93 (https://github.com/ajfg93) for suggesting an iterative solution for the problem at
// https://github.com/reneargento/algorithms-sedgewick-wayne/issues/27
// Thanks to emergencyd (https://github.com/emergencyd) for suggesting an improvement on the iterative solution.
// https://github.com/reneargento/algorithms-sedgewick-wayne/issues/124
// For another (similar) iterative solution to this problem, check
// https://github.com/reneargento/algorithms-sedgewick-wayne/issues/196 - Thanks to nedas-dev (https://github.com/nedas-dev)
public class Exercise10 {

    public static void main(String[] args) {
        int[] testArray1 = {3, 4, 4, 5, 6, 10, 15, 20, 20, 20, 20, 21};
        int elementToSearch1 = 4;
        int elementToSearch2 = 20;
        int elementToSearch3 = -5;

        StdOut.println("Binary search: " + binarySearch(testArray1, elementToSearch1, 0, testArray1.length - 1) +
                " Expected: 1");
        StdOut.println("Binary search: " + binarySearch(testArray1, elementToSearch2, 0, testArray1.length - 1) +
                " Expected: 7");
        StdOut.println("Binary search: " + binarySearch(testArray1, elementToSearch3, 0, testArray1.length - 1) +
                " Expected: -1");

        int[] testArray2 = {4, 4, 4, 4, 4, 4, 15, 20, 20, 20, 20, 21};
        int elementToSearch4 = 4;

        StdOut.println("Binary search: " + binarySearch(testArray2, elementToSearch4, 0, testArray2.length - 1) +
                " Expected: 0");
    }

    private static int binarySearch(int[] array, int element, int low, int high) {
        if (low > high) {
            return -1;
        }

        int middle = low + (high - low) / 2;

        if (array[middle] < element) {
            return binarySearch(array, element, middle + 1, high);
        } else if (array[middle] > element) {
            return binarySearch(array, element, low, middle - 1);
        } else {
            int possibleSmallestIndex = binarySearch(array, element, low, middle - 1);

            if (possibleSmallestIndex == -1) {
                return middle;
            } else {
                return possibleSmallestIndex;
            }
        }
    }

    private static int binarySearchIterative(int[] array, int element, int low, int high) {
        while (low <= high) {
            int middle = low + (high - low) / 2;

            if (array[middle] < element) {
                low = middle + 1;
            } else if (array[middle] > element
                    || (middle > 0 && array[middle - 1] == element)) {
                high = middle - 1;
            } else {
                return middle;
            }
        }
        return -1;
    }
}


Question: Write a program that, given two sorted arrays of N int values, prints all elements that appear in both arrays, in sorted order. The running time of your program should be proportional to N in the worst case.

Answer:
package chapter1.section4;

import edu.princeton.cs.algs4.StdOut;

/**
 * Created by Rene Argento on 9/29/16.
 */
// Thanks to ektasingh151 (https://github.com/ektasingh151) for suggesting a simpler solution:
// https://github.com/reneargento/algorithms-sedgewick-wayne/pull/149
public class Exercise12 {

    public static void main(String[] args) {
        int[] array1 = { 0, 1, 2, 2, 5, 6, 6, 8, 25, 25 };
        int[] array2 = { -2, 0, 1, 2, 2, 2, 3, 4, 5, 10, 20, 25, 25 };

        StdOut.print("Elements: ");
        printElementsThatAppearInBothArrays(array1, array2);
        StdOut.println("\nExpected: 0 1 2 5 25");
    }

    private static void printElementsThatAppearInBothArrays(int[] array1, int[] array2) {
        int array1Index = 0;
        int array2Index = 0;
        Integer recentMatchedValue = null;

        while (array1Index < array1.length && array2Index < array2.length) {
            if (array1[array1Index] < array2[array2Index]) {
                array1Index++;
            } else if (array2[array2Index] < array1[array1Index]) {
                array2Index++;
            } else {
                if (recentMatchedValue == null || recentMatchedValue != array1[array1Index]) {
                    StdOut.print(array1[array1Index] + " ");
                    recentMatchedValue = array1[array1Index];
                }
                array1Index++;
                array2Index++;
            }
        }
    }
}


Question: Closest pair (in one dimension). Write a program that, given an array a[] of N double values, finds a closest pair: two values whose difference is no greater than the difference of any other pair (in absolute value). The running time of your program should be linearithmic in the worst case.

Answer:
package chapter1.section4;

import edu.princeton.cs.algs4.StdOut;

import java.util.Arrays;

/**
 * Created by Rene Argento on 9/30/16.
 */
public class Exercise16_ClosestPair {

    public static void main(String[] args) {
        double[] array1 = {-5.2, 9.4, 20, -10, 21.1, 40, 50, -20};
        double[] array2 = {-4, -3, 0, 10, 20};
        double[] array3 = {-10, -3, 0, 2, 4, 20};

        double[] closestPair1 = closestPair(array1);
        double[] closestPair2 = closestPair(array2);
        double[] closestPair3 = closestPair(array3);

        StdOut.println("Closest pair: " + closestPair1[0] + " " + closestPair1[1] + " Expected: 20.0 21.1");
        StdOut.println("Closest pair: " + closestPair2[0] + " " + closestPair2[1] + " Expected: -4.0 -3.0");
        StdOut.println("Closest pair: " + closestPair3[0] + " " + closestPair3[1] + " Expected: 0.0 2.0");
    }

    private static double[] closestPair(double[] array) {
        double[] closestPair = new double[2];

        double currentMinimumDifference = Double.MAX_VALUE;

        Arrays.sort(array);

        for (int i = 0; i < array.length - 1; i++) {
            if (Math.abs(array[i] - array[i + 1]) < currentMinimumDifference) {
                currentMinimumDifference = Math.abs(array[i] - array[i + 1]);

                closestPair[0] = array[i];
                closestPair[1] = array[i + 1];
            }
        }
        return closestPair;
    }
}


Question: Farthest pair (in one dimension). Write a program that, given an array a[] of N double values, finds a farthest pair: two values whose difference is no smaller than the difference of any other pair (in absolute value). The running time of your program should be linear in the worst case.

Answer:
package chapter1.section4;

import edu.princeton.cs.algs4.StdOut;

/**
 * Created by Rene Argento on 9/30/16.
 */
public class Exercise17_FarthestPair {

    public static void main(String[] args) {
        double[] array1 = {-5.2, 9.4, 20, -10, 21.1, 40, 50, -20};
        double[] array2 = {-4, -3, 0, 10, 20};
        double[] array3 = {-10, -3, 0, 2, 4, 20};

        double[] farthestPair1 = farthestPair(array1);
        double[] farthestPair2 = farthestPair(array2);
        double[] farthestPair3 = farthestPair(array3);

        StdOut.println("Farthest pair: " + farthestPair1[0] + " " + farthestPair1[1] + " Expected: -20.0 50.0");
        StdOut.println("Farthest pair: " + farthestPair2[0] + " " + farthestPair2[1] + " Expected: -4.0 20.0");
        StdOut.println("Farthest pair: " + farthestPair3[0] + " " + farthestPair3[1] + " Expected: -10.0 20.0");
    }

    private static double[] farthestPair(double[] array) {
        double[] farthestPair = new double[2];

        if (array.length == 0) {
            throw new RuntimeException("Array cannot be null");
        }

        double currentMin = array[0];
        double currentMax = array[0];

        farthestPair[0] = array[0];
        farthestPair[1] = array[0];

        for (int i = 1; i < array.length; i++) {
            if (array[i] < currentMin) {
                currentMin = array[i];
                farthestPair[0] = array[i];
            }

            if (array[i] > currentMax) {
                currentMax = array[i];
                farthestPair[1] = array[i];
            }
        }

        return farthestPair;
    }
}


Question: Local minimum of an array. Write a program that, given an array a[] of N distinct integers, finds a local minimum: an index i such that a[i] is smaller than its neighbors (a[i-1] > a[i] < a[i+1]). Your program should use ~2lg N compares in the worst case.

Answer:
public static int localMinimumIndex(int[] a) {
    if (a == null || a.length == 0) return -1;
    if (a.length == 1 || a[0] < a[1]) return 0;
    int n = a.length;
    if (a[n - 1] < a[n - 2]) return n - 1;

    int lo = 1;
    int hi = n - 2;
    while (lo <= hi) {
        int mid = lo + (hi - lo) / 2;
        if (a[mid] < a[mid - 1] && a[mid] < a[mid + 1]) {
            return mid;
        }
        if (a[mid - 1] < a[mid]) {
            hi = mid - 1;
        } else {
            lo = mid + 1;
        }
    }
    return -1;
}

At each step the search moves toward a smaller neighbor, so a local minimum must exist in the chosen half. The worst-case number of compares is proportional to lg N.


Question: Local minimum of a matrix. Given an N-by-N array a[] of N^2 distinct integers, design an algorithm that runs in time proportional to N to find a local minimum: a pair of indices i and j such that a[i][j] < a[i+1][j], a[i][j] < a[i][j+1], a[i][j] < a[i-1][j], and a[i][j] < a[i][j-1]. The running time of your program should be proportional to N in the worst case.

Answer:
Use the standard divide-and-conquer algorithm: inspect the middle row and middle column, find the minimum item on that cross, and either return it if it is smaller than its four neighbors or recurse into the quadrant containing a smaller neighbor.

```java
public static int[] localMinimum(int[][] a) {
    return localMinimum(a, 0, a.length - 1, 0, a.length - 1);
}

private static int[] localMinimum(int[][] a, int rowLo, int rowHi, int colLo, int colHi) {
    int rowMid = rowLo + (rowHi - rowLo) / 2;
    int colMid = colLo + (colHi - colLo) / 2;
    int bestRow = rowMid;
    int bestCol = colMid;

    for (int c = colLo; c <= colHi; c++) {
        if (a[rowMid][c] < a[bestRow][bestCol]) {
            bestRow = rowMid;
            bestCol = c;
        }
    }
    for (int r = rowLo; r <= rowHi; r++) {
        if (a[r][colMid] < a[bestRow][bestCol]) {
            bestRow = r;
            bestCol = colMid;
        }
    }

    int[][] neighbors = {{bestRow - 1, bestCol}, {bestRow + 1, bestCol}, {bestRow, bestCol - 1}, {bestRow, bestCol + 1}};
    for (int[] nb : neighbors) {
        int r = nb[0], c = nb[1];
        if (r >= rowLo && r <= rowHi && c >= colLo && c <= colHi && a[r][c] < a[bestRow][bestCol]) {
            if (r < rowMid && c < colMid) return localMinimum(a, rowLo, rowMid - 1, colLo, colMid - 1);
            if (r < rowMid) return localMinimum(a, rowLo, rowMid - 1, colMid + 1, colHi);
            if (c < colMid) return localMinimum(a, rowMid + 1, rowHi, colLo, colMid - 1);
            return localMinimum(a, rowMid + 1, rowHi, colMid + 1, colHi);
        }
    }
    return new int[] {bestRow, bestCol};
}
```

The work per level is proportional to the current side length, giving N + N/2 + N/4 + ... = O(N).


Question: Throwing two eggs from a building. Consider the previous question, but now suppose you only have two eggs, and your cost model is the number of throws. Devise a strategy to determine F such that the number of throws is at most 2√N, then find a way to reduce the cost to ~c√F. This is analogous to a situation where search hits (egg intact) are much cheaper than misses (egg broken).

Answer:
package chapter1.section4;

import edu.princeton.cs.algs4.StdOut;

/**
 * Created by Rene Argento on 23/10/16.
 */
public class Exercise25_Throwing2Eggs {

    public static void main(String[] args) {
        int[] array = {0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
        1, 1, 1, 1, 1, 1, 1, 1, 1};

        int[] array2 = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
                1, 1, 1, 1, 1, 1, 1, 1, 1};

        int[] array3 = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                0, 0, 0, 0, 0, 0, 0, 0, 1};

        Exercise25_Throwing2Eggs exercise25_throwing2Eggs = new Exercise25_Throwing2Eggs();

        // findFloorIn2SqrtN

        int floor = exercise25_throwing2Eggs.findFloorIn2SqrtN(array);
        StdOut.println("Floor: " + floor + " Expected: 7");

        int floor2 = exercise25_throwing2Eggs.findFloorIn2SqrtN(array2);
        StdOut.println("Floor: " + floor2 + " Expected: 0");

        int floor3 = exercise25_throwing2Eggs.findFloorIn2SqrtN(array3);
        StdOut.println("Floor: " + floor3 + " Expected: 40");

        // findFloorInCSqrtF

        int floor4 = exercise25_throwing2Eggs.findFloorInCSqrtF(array);
        StdOut.println("Floor: " + floor4 + " Expected: 7");

        int floor5 = exercise25_throwing2Eggs.findFloorInCSqrtF(array2);
        StdOut.println("Floor: " + floor5 + " Expected: 0");

        int floor6 = exercise25_throwing2Eggs.findFloorInCSqrtF(array3);
        StdOut.println("Floor: " + floor6 + " Expected: 40");
    }

    private int findFloorIn2SqrtN(int[] array) {
        int low = 0;
        int high = array.length - 1;

        return findFloorIn2SqrtN(array, low, high, 0);
    }

    /*
    Solution to Part 1: To achieve 2 * sqrt(N), drop eggs at floors
    sqrt(N), 2 * sqrt(N), 3 * sqrt(N), ..., sqrt(N) * sqrt(N).
    (For simplicity, we assume here that sqrt(N) is an integer.)
    Let assume that the egg broke at level k * sqrt(N).
    With the second egg you should then perform a linear search
    in the interval (k-1) * sqrt(N) to k * sqrt(N).
    In total you will be able to find the floor F in at most 2 * sqrt(N) trials.
     */
    private int findFloorIn2SqrtN(int[] array, int low, int high, int searchLevel) {
        int key = 1;

        if (low <= high) {
            int sqrt = (int) Math.sqrt(array.length - 1);

            int separator = sqrt * searchLevel;

            if (separator >= array.length) {
                separator = array.length - 1;
            }

            StdOut.println("Debug - current index: " + separator);

            if (key > array[separator]) {
                return findFloorIn2SqrtN(array, separator + 1, high, ++searchLevel);
            } else {
                // We broke 1 out of 2 eggs, now we do a linear search starting from a floor in which we know that the egg
                // does not break

                if (searchLevel != 0) {
                    searchLevel = searchLevel - 1;
                }

                int lastFloorThatDidNotBreak = sqrt * searchLevel;

                for (int i = lastFloorThatDidNotBreak; i <= separator; i++) {
                    StdOut.println("Debug - current index: " + i);

                    if (array[i] == 1) {
                        // 2 out of 2 eggs broken, but we now have the floor number
                        return i;
                    }
                }
            }
        }

        return -1;
    }

    private int findFloorInCSqrtF(int[] array) {
        int low = 0;
        int high = array.length - 1;

        return findFloorInCSqrtF(array, low, high, 0, 0);
    }

    // Hint from website: 1 + 2 + 3 + ... k ~ 1/2 k^2.
    private int findFloorInCSqrtF(int[] array, int low, int high, int searchElement, int increment) {
        int key = 1;

        if (low <= high) {

            searchElement = searchElement + increment;

            if (searchElement >= array.length) {
                searchElement = array.length - 1;
            }

            StdOut.println("Debug - current index: " + searchElement);

            if (key > array[searchElement]) {
                return findFloorInCSqrtF(array, searchElement + 1, high, searchElement, ++increment);
            } else {
                // We broke 1 out of 2 eggs, now we do a linear search starting from a floor in which we know that the egg
                // does not break

                searchElement = searchElement - increment;
                int lastFloorThatDidNotBreak = searchElement;

                for (int i = lastFloorThatDidNotBreak; i <= searchElement + increment; i++) {
                    StdOut.println("Debug - current index: " + i);

                    if (array[i] == 1) {
                        // 2 out of 2 eggs broken, but we now have the floor number
                        return i;
                    }
                }
            }
        }
        return -1;
    }
}


Question: Queue with two stacks. Implement a queue with two stacks so that each queue operation takes a constant amortized number of stack operations. Hint: If you push elements onto a stack and then pop them all, they appear in reverse order. If you repeat this process, they’re now back in order.

Answer:
package chapter1.section4;

import edu.princeton.cs.algs4.StdOut;

import java.util.Stack;

/**
 * Created by Rene Argento on 20/11/16.
 */
public class Exercise27_QueueWith2Stacks<Item> {
    private Stack<Item> tailStack;
    private Stack<Item> headStack;

    public Exercise27_QueueWith2Stacks() {
        tailStack = new Stack<>();
        headStack = new Stack<>();
    }

    // O(1)
    public int size() {
        return headStack.size() + tailStack.size();
    }

    // O(1)
    public boolean isEmpty() {
        return headStack.isEmpty() && tailStack.isEmpty();
    }

    // O(1)
    public void enqueue(Item item) {
        tailStack.push(item);
    }

    // Amortized O(1)
    public Item dequeue() {
        if (headStack.isEmpty()) {
            moveAllItemsFromTailToHead();
        }
        return headStack.pop();
    }

    private void moveAllItemsFromTailToHead() {
        while (!tailStack.isEmpty()) {
            headStack.push(tailStack.pop());
        }
    }

    public static void main(String[] args) {
        Exercise27_QueueWith2Stacks<String> exercise27_queueWith2Stacks = new Exercise27_QueueWith2Stacks<>();

        StdOut.println("IsEmpty: " + exercise27_queueWith2Stacks.isEmpty() + " Expected: true");
        StdOut.println("Size: " + exercise27_queueWith2Stacks.size() + " Expected: 0");

        exercise27_queueWith2Stacks.enqueue("A");
        exercise27_queueWith2Stacks.enqueue("B");
        StdOut.println(exercise27_queueWith2Stacks.dequeue());
        StdOut.println(exercise27_queueWith2Stacks.dequeue());

        exercise27_queueWith2Stacks.enqueue("C");
        exercise27_queueWith2Stacks.enqueue("D");
        exercise27_queueWith2Stacks.enqueue("E");
        exercise27_queueWith2Stacks.enqueue("F");

        StdOut.println("Size: " + exercise27_queueWith2Stacks.size() + " Expected: 4");

        StdOut.println(exercise27_queueWith2Stacks.dequeue());
        StdOut.println(exercise27_queueWith2Stacks.dequeue());

        StdOut.println("Expected output from dequeue(): A B C D");

        StdOut.println("IsEmpty: " + exercise27_queueWith2Stacks.isEmpty() + " Expected: false");
        StdOut.println("Size: " + exercise27_queueWith2Stacks.size() + " Expected: 2");
    }
}


Question: Hot or cold. Your goal is to guess a secret integer between 1 and N. You repeatedly guess integers between 1 and N. After each guess you learn if your guess equals the secret integer (and the game stops). Otherwise, you learn if the guess is hotter (closer to) or colder (farther from) the secret number than your previous guess. Design an algorithm that finds the secret number in at most ~2 lg N guesses. Then design an algorithm that finds the secret number in at most ~ 1 lg N guesses.

Answer:
// Exercise34_HotOrCold1LgN.java
package chapter1.section4;

import edu.princeton.cs.algs4.StdOut;

/**
 * Created by Rene Argento on 23/11/16.
 */
//IOI 2010 task
//Based on http://stackoverflow.com/questions/25558951/hot-and-cold-binary-search-game
    // Worst case is O(lg n) + 6 when we start in an "end quarter"
    // or O(lg n) + 4 when we start in a "middle quarter"
public class Exercise34_HotOrCold1LgN {

    private int hotOrCold(int n, int target) {
        return firstGuesses(n, target, 1, n);
    }

    private int firstGuesses(int number, int target, int low, int high) {

        //Check if it is in the first half
        int firstGuessIndex = number / 2;

        if (firstGuessIndex == target) {
            StdOut.println("Found it!");
            return firstGuessIndex;
        }

        //Check if it is in the second half
        int secondGuessIndex = (number / 2) + 1;
        if (secondGuessIndex == target) {
            StdOut.println("Found it!");
            return secondGuessIndex;
        } else {
            boolean isItHotter = isItHotter(firstGuessIndex, secondGuessIndex, target);

            if (isItHotter) {
                //Secret is in the second half, so the next guess will be "high"
                return initialSearch(target, secondGuessIndex, false, secondGuessIndex, high);
            } else {
                //Secret is in the first half, so the next guess will be "low"
                return initialSearch(target, secondGuessIndex, true, low, firstGuessIndex);
            }
        }
    }

    //This is just in case we fall into one of the 2 "end quarters" and need to go to one of the 2 "middle quarters"
    private int initialSearch(int target, int lastGuess, boolean isNextGuessInLeftEnd, int low, int high) {

        if (low > high) {
            return -1;
        }

        int nextGuess;
        //Check new guess
        if (isNextGuessInLeftEnd) {
            nextGuess = low;
        } else {
            nextGuess = high;
        }
        boolean isItHotter = isItHotter(lastGuess, nextGuess, target);

        int middle = low + (high - low) / 2;

        if (nextGuess == target) {
            return nextGuess;
        } else if (isItHotter) {
            //We are in one of the 2 end quarters

            int middleOfMiddle;

            if (isNextGuessInLeftEnd) {
                middleOfMiddle = low + (middle - low) / 2;
            } else {
                middleOfMiddle = middle + (high - middle) / 2;
            }

            //Guess middleOfMiddle
            isItHotter(nextGuess, middleOfMiddle, target);
            if (middleOfMiddle == target) {
                return middleOfMiddle;
            }

            //Guess middleOfMiddle + 1
            isItHotter = isItHotter(middleOfMiddle, middleOfMiddle + 1, target);
            if (middleOfMiddle + 1 == target) {
                return middleOfMiddle + 1;
            }

            if (isItHotter) {
                //Secret is in the second half
                return search(target, middleOfMiddle + 1, middleOfMiddle + 1, high);
            } else {
                //Secret is in the first half
                return search(target, middleOfMiddle + 1, low, middleOfMiddle);
            }
        } else {
            //We are in one of the 2 middle quarters
            if (isNextGuessInLeftEnd) {
                return search(target, nextGuess, middle + 1, high);
            } else {
                return search(target, nextGuess, low, middle);
            }
        }
    }

    //We are in a "middle quarter"
    //Considering [a, b] as the interval we know the secret integer exists within,
    // take c to be the last number we guessed.
    // We want to determine the position of the secret with respect
    // to the mid point (a + b) / 2, so we have a new number d to guess at to know the
    // secret relative position to (a + b) / 2.
    // How do we know such number d?
    // By solving the equation (c + d) / 2 = (a + b) / 2, which yields d = a + b - c.
    // Guessing at that d, we shrink the range [a, b] appropriately based on
    // the answer(colder or hotter) and then we repeat the process.
    private int search(int target, int lastGuess, int low, int high) {

        if (low == high) {
            if (low == target) {
                //Found it!
                return low;
            } else {
                return -1;
            }
        }

        if (low > high) {
            return -1;
        }

        // a = low
        // b = high
        // c = lastGuess
        // d = nextGuess

        int nextGuess = low + high - lastGuess;

        boolean isItHotter = isItHotter(lastGuess, nextGuess, target);

        int middle = low + (high - low) / 2;

        if (nextGuess == target) {
            return nextGuess;
        } else if (isItHotter) {
            if (nextGuess >= high) {
                return search(target, nextGuess, middle + 1, high);
            } else {
                return search(target, nextGuess, low, middle);
            }
        } else {
            if (nextGuess >= high) {
                return search(target, nextGuess, low, middle);
            } else {
                return search(target, nextGuess, middle + 1, high);
            }
        }
    }

    private boolean isItHotter(int lastGuess, int currentGuess, int secret) {

        if (currentGuess == secret) {
            StdOut.println("Found it!");
            return true;
        }

        if (Math.abs(secret - currentGuess) < Math.abs(secret - lastGuess)) {
            StdOut.println("Hotter - Last guess: " + lastGuess + " Current guess: " + currentGuess);
            return true;
        } else {
            StdOut.println("Colder - Last guess: " + lastGuess + " Current guess: " + currentGuess);
            return false;
        }
    }

    public static void main(String[] args) {
        Exercise34_HotOrCold1LgN hotOrCold = new Exercise34_HotOrCold1LgN();
        StdOut.println("Hot or Cold: " + hotOrCold.hotOrCold(10, 3) + " Expected: 3");
        StdOut.println("Hot or Cold: " + hotOrCold.hotOrCold(20, 12) + " Expected: 12");
        StdOut.println("Hot or Cold: " + hotOrCold.hotOrCold(10, 11) + " Expected: -1");
    }

}

// Exercise34_HotOrCold2LgN.java
package chapter1.section4;

import edu.princeton.cs.algs4.StdOut;

/**
 * Created by Rene Argento on 23/11/16.
 */
public class Exercise34_HotOrCold2LgN {

    private int hotOrCold(int number, int target) {
        //Check if it is in the first half
        int firstGuessIndex = number / 2;

        if (firstGuessIndex == target) {
            StdOut.println("Found it!");
            return firstGuessIndex;
        }

        //Check if it is in the second half
        int secondGuessIndex = firstGuessIndex + 1;
        if (secondGuessIndex == target) {
            StdOut.println("Found it!");
            return secondGuessIndex;
        } else {
            boolean isItHotter = isItHotter(firstGuessIndex, secondGuessIndex, target);

            if (isItHotter) {
                return binarySearch(target, secondGuessIndex, secondGuessIndex, number);
            } else {
                return binarySearch(target, secondGuessIndex, 1, firstGuessIndex);
            }
        }
    }

    //2 * O(lg n)
    private int binarySearch(int target, int lastGuess, int low, int high) {
        if (low == high) {
            if (low == target) {
                //Found it!
                return low;
            } else {
                return -1;
            }
        }

        if (low > high) {
            return -1;
        }

        int middle = low + (high - low) / 2;

        // Guess middle
        boolean isItHotterFirstHalf = isItHotter(lastGuess, middle, target);
        if (isItHotterFirstHalf && middle == target) {
            return middle;
        }

        // Guess middle + 1
        boolean isItHotterSecondHalf = isItHotter(middle, middle + 1, target);

        if (middle + 1 == target) {
            return middle + 1;
        } else if (isItHotterSecondHalf) {
            return binarySearch(target, middle + 1, middle + 2, high);
        } else {
            return binarySearch(target, middle + 1, low, middle);
        }
    }

    private boolean isItHotter(int lastGuess, int currentGuess, int secret) {

        if (currentGuess == secret) {
            StdOut.println("Found it!");
            return true;
        }

        if (Math.abs(secret - currentGuess) < Math.abs(secret - lastGuess)) {
            StdOut.println("Hotter - Last guess: " + lastGuess + " Current guess: " + currentGuess);
            return true;
        } else {
            StdOut.println("Colder - Last guess: " + lastGuess + " Current guess: " + currentGuess);
            return false;
        }
    }

    public static void main(String[] args) {
        Exercise34_HotOrCold2LgN hotOrCold = new Exercise34_HotOrCold2LgN();
        StdOut.println("Hot or Cold: " + hotOrCold.hotOrCold(10, 3) + " Expected: 3");
        StdOut.println("Hot or Cold: " + hotOrCold.hotOrCold(20, 12) + " Expected: 12");
        StdOut.println("Hot or Cold: " + hotOrCold.hotOrCold(10, 11) + " Expected: -1");
    }

}


Question:
Time costs for pushdown stacks. Justify the entries in the table below, which shows typical time costs for various pushdown stack implementations, using a cost model that counts both data references (references to data pushed onto the stack, either an array reference or a reference to an object’s instance variable) and objects created.
| data structure   | item type | cost to push N int values | |
|------------------|-----------|---------------------------|----------------|
|                  |           | data references           | objects created |
| linked list      | int       | 2N                        | N              |
|                  | Integer   | 3N                        | 2N             |
| resizing array   | int       | ~5N                       | lg N           |
|                  | Integer   | ~5N                       | ~N             |

Answer:
1.4.35 - Time costs for pushdown stacks

** Linked list
  * int
     2N data references - N references of the nodes for the enclosing class and N references for the next Node (including a reference to the first node in the stack)
     N objects created - the N nodes created
  * Integer
     3N data references - N references of the nodes for the enclosing class, N references for the next Node (including a reference to the first node in the stack) and N references for the Integer object
     2N objects created - N nodes created, each containing an Integer

** Resizing array
  * int
     ~5N data references - each of the array entries has a reference to an int value. Due to resizing, the array can be 25% to 100% full, meaning the total number of references can go up to ~4N. Also, the stack has a reference to the array.
     lgN objects created - every time the array is resized, a new array object is created. When the array is resized (due to pushes), it doubles its size. Therefore, at capacity 1, 2, 4, 8, 16, etc, a new object will be created. Hence, lgN objects.
  * Integer
     ~5N data references - each of the array entries has a reference to an int value. Due to resizing, the array can be 25% to 100% full, meaning the total number of references can go up to ~4N. Also, the stack has a reference to the array.
     ~N objects created - the N Integer objects created plus lg N objects created due to the resize operations. Therefore, ~N.


Question:
Naive 3-sum implementation. Run experiments to evaluate the following implementation of the inner loop of ThreeSum:
for (int i = 0; i < N; i++)
 for (int j = 0; j < N; j++)
 for (int k = 0; k < N; k++)
 if (i < j && j < k)
 if (a[i] + a[j] + a[k] == 0)
 cnt++;
Do so by developing a version of DoublingTest that computes the ratio of the running times of this program and ThreeSum.

Answer:
// Exercise38_Naive3Sum.java
package chapter1.section4;

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

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

/**
 * Created by Rene Argento on 26/11/16.
 */
public class Exercise38_Naive3Sum {

    private static double timeTrial(int n, boolean useEvenNaiverImplementation) {
        //Time ThreeSum.count() for n random 6-digit ints.
        int max = 1000000;
        int[] values = new int[n];

        for (int i = 0; i < n; i++) {
            values[i] = StdRandom.uniform(-max, max);
        }

        Stopwatch timer = new Stopwatch();

        if (useEvenNaiverImplementation) {
            evenNaiverThreeSumCount(values);
        } else {
            threeSumCount(values);
        }

        return timer.elapsedTime();
    }

    private static int threeSumCount(int[] values) {
        // Count triples that sum to 0
        int n = values.length;
        int count = 0;

        for (int i = 0; i < n; i++) {
            for (int j = i + 1; j < n; j++) {
                for (int k = j + 1; k < n; k++) {
                    if (values[i] + values[j] + values[k] == 0) {
                        count++;
                    }
                }
            }
        }
        return count;
    }

    private static int evenNaiverThreeSumCount(int[] values) {
        //Count triples that sum to 0
        int n = values.length;
        int count = 0;

        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                for (int k = 0; k < n; k++) {
                    if (i < j && j < k) {
                        if (values[i] + values[j] + values[k] == 0) {
                            count++;
                        }
                    }
                }
            }
        }
        return count;
    }

    public static void main(String[] args) {
        Map<Integer, Double> timesOfNaiveThreeSum = new HashMap<>();
        Map<Integer, Double> timesOfEvenNaiverThreeSum = new HashMap<>();

        int maxTrials = 4000;

        StdOut.println("Number of items and ratio");

        // Compute running times of naive 3-Sum
        for (int n = 250; n <= maxTrials; n += n) {
            double time = timeTrial(n, false);
            timesOfNaiveThreeSum.put(n, time);
        }

        // Compute running times of even naiver 3-Sum
        for (int n = 250; n <= maxTrials; n += n) {
            double time = timeTrial(n, true);
            timesOfEvenNaiverThreeSum.put(n, time);
        }

        for (int n = 250; n <= maxTrials; n += n) {
            double timeOfNaiveThreeSum = timesOfNaiveThreeSum.get(n);
            double timeOfEvenNaiverThreeSum = timesOfEvenNaiverThreeSum.get(n);

            double ratio = timeOfEvenNaiverThreeSum / timeOfNaiveThreeSum;

            StdOut.printf("%7d %5.1f\n", n, ratio);
        }
    }
}

Additional notes/results:
1.4.38 - Naive 3-sum implementation

Number of items and ratio (naiver implementation / naive implementation)
    250   3.6
    500   5.8
   1000  31.3
   2000  30.6
   4000  31.3

The naive implementation takes about 30 times less time than the naiver implementation.


Question: Show the contents of the id[] array and the number of times the array is accessed for each input pair when you use quick-find for the sequence 9-0 3-4 5-8 7-2 2-1 5-7 0-3 4-2.

Answer:
1.5.1
        0 1 2 3 4 5 6 7 8 9
array = 0 1 2 3 4 5 6 7 8 9

9-0
        0 1 2 3 4 5 6 7 8 9
array = 0 1 2 3 4 5 6 7 8 0
Array accesses: 13 (2 in 2x find() + 10 for checking parents + 1 for updating parents)

3-4
        0 1 2 3 4 5 6 7 8 9
array = 0 1 2 4 4 5 6 7 8 0
Array accesses: 13 (2 in 2x find() + 10 for checking parents + 1 for updating parents)

5-8
        0 1 2 3 4 5 6 7 8 9
array = 0 1 2 4 4 8 6 7 8 0
Array accesses: 13 (2 in 2x find() + 10 for checking parents + 1 for updating parents)

7-2
        0 1 2 3 4 5 6 7 8 9
array = 0 1 2 4 4 8 6 2 8 0
Array accesses: 13 (2 in 2x find() + 10 for checking parents + 1 for updating parents)

2-1
        0 1 2 3 4 5 6 7 8 9
array = 0 1 1 4 4 8 6 1 8 0
Array accesses: 14 (2 in 2x find() + 10 for checking parents + 2 for updating parents)

5-7
        0 1 2 3 4 5 6 7 8 9
array = 0 1 1 4 4 1 6 1 1 0
Array accesses: 14 (2 in 2x find() + 10 for checking parents + 2 for updating parents)

0-3
        0 1 2 3 4 5 6 7 8 9
array = 4 1 1 4 4 1 6 1 1 4
Array accesses: 14 (2 in 2x find() + 10 for checking parents + 2 for updating parents)

4-2
        0 1 2 3 4 5 6 7 8 9
array = 1 1 1 1 1 1 6 1 1 1
Array accesses: 16 (2 in 2x find() + 10 for checking parents + 4 for updating parents)


Question: Do Exercise 1.5.1, but use quick-union (page 224). In addition, draw the forest of trees represented by the id[] array after each input pair is processed.

Answer:
1.5.2
        0 1 2 3 4 5 6 7 8 9
array = 0 1 2 3 4 5 6 7 8 9

9-0
        0 1 2 3 4 5 6 7 8 9
array = 0 1 2 3 4 5 6 7 8 0
Array accesses: 3 (1 for find(9), 1 for find(0) and 1 for updating parent)

Forest:
0 1 2 3 4 5 6 7 8
9

3-4
        0 1 2 3 4 5 6 7 8 9
array = 0 1 2 4 4 5 6 7 8 0
Array accesses: 3 (1 for find(3), 1 for find(4) and 1 for updating parent)

Forest:
0 1 2 4 5 6 7 8
9     3

5-8
        0 1 2 3 4 5 6 7 8 9
array = 0 1 2 4 4 8 6 7 8 0
Array accesses: 3 (1 for find(5), 1 for find(8) and 1 for updating parent)

Forest:
0 1 2 4 6 7 8
9     3     5

7-2
        0 1 2 3 4 5 6 7 8 9
array = 0 1 2 4 4 8 6 2 8 0
Array accesses: 3 (1 for find(7), 1 for find(2) and 1 for updating parent)

Forest:
0 1 2 4 6 8
9   7 3   5

2-1
        0 1 2 3 4 5 6 7 8 9
array = 0 1 1 4 4 8 6 2 8 0
Array accesses: 3 (1 for find(2), 1 for find(1) and 1 for updating parent)

Forest:
0 1 4 6 8
9 2 3   5
  7

5-7
        0 1 2 3 4 5 6 7 8 9
array = 0 1 1 4 4 8 6 2 1 0
Array accesses: 9 (3 for find(5), 5 for find(7) and 1 for updating parent)

Forest:
0   1   4 6
9  2 8  3
   7 5

0-3
        0 1 2 3 4 5 6 7 8 9
array = 4 1 1 4 4 8 6 2 1 0
Array accesses: 5 (1 for find(0), 3 for find(3) and 1 for updating parent)

Forest:
 1    4   6
2 8  3 0
7 5    9

4-2
        0 1 2 3 4 5 6 7 8 9
array = 4 1 1 4 1 8 6 2 1 0
Array accesses: 5 (1 for find(4), 3 for find(2) and 1 for updating parent)

Forest:
  1      6
2 8  4
7 5 3 0
      9

Thanks to kaustubhb (https://github.com/kaustubhb) for fixing a bug in the array values.
https://github.com/reneargento/algorithms-sedgewick-wayne/issues/4
Thanks to emergencyd (https://github.com/emergencyd) for fixing some of the array access counts.
https://github.com/reneargento/algorithms-sedgewick-wayne/issues/135


Question: Show the contents of the sz[] and id[] arrays and the number of array accesses for each input pair corresponding to the weighted quick-union examples in the text (both the reference input and the worst-case input).

Answer:
1.5.4

Reference input

Beginning

id[]
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9

sz[]
0 1 2 3 4 5 6 7 8 9
1 1 1 1 1 1 1 1 1 1

4-3

id[]
0 1 2 3 4 5 6 7 8 9
0 1 2 4 4 5 6 7 8 9

sz[]
0 1 2 3 4 5 6 7 8 9
1 1 1 1 2 1 1 1 1 1

Array accesses: 3 (1 for find(4), 1 for find(3) and 1 for updating parent)

3-8

id[]
0 1 2 3 4 5 6 7 8 9
0 1 2 4 4 5 6 7 4 9

sz[]
0 1 2 3 4 5 6 7 8 9
1 1 1 1 3 1 1 1 1 1

Array accesses: 5 (3 for find(3), 1 for find(8) and 1 for updating parent)

6-5

id[]
0 1 2 3 4 5 6 7 8 9
0 1 2 4 4 6 6 7 4 9

sz[]
0 1 2 3 4 5 6 7 8 9
1 1 1 1 3 1 2 1 1 1

Array accesses: 3 (1 for find(6), 1 for find(5) and 1 for updating parent)

9-4

id[]
0 1 2 3 4 5 6 7 8 9
0 1 2 4 4 6 6 7 4 4

sz[]
0 1 2 3 4 5 6 7 8 9
1 1 1 1 4 1 2 1 1 1

Array accesses: 3 (1 for find(9), 1 for find(4) and 1 for updating parent)

2-1

id[]
0 1 2 3 4 5 6 7 8 9
0 2 2 4 4 6 6 7 4 4

sz[]
0 1 2 3 4 5 6 7 8 9
1 1 2 1 4 1 2 1 1 1

Array accesses: 3 (1 for find(2), 1 for find(1) and 1 for updating parent)

8-9

id[]
0 1 2 3 4 5 6 7 8 9
0 2 2 4 4 6 6 7 4 4

sz[]
0 1 2 3 4 5 6 7 8 9
1 1 2 1 4 1 2 1 1 1

Array accesses: 6 (3 for find(8) and 3 for find(9))

5-0

id[]
0 1 2 3 4 5 6 7 8 9
6 2 2 4 4 6 6 7 4 4

sz[]
0 1 2 3 4 5 6 7 8 9
1 1 2 1 4 1 3 1 1 1

Array accesses: 5 (3 for find(5), 1 for find(0) and 1 for updating parent)

7-2

id[]
0 1 2 3 4 5 6 7 8 9
6 2 2 4 4 6 6 2 4 4

sz[]
0 1 2 3 4 5 6 7 8 9
1 1 3 1 4 1 3 1 1 1

Array accesses: 3 (1 for find(7), 1 for find(2) and 1 for updating parent)

6-1

id[]
0 1 2 3 4 5 6 7 8 9
6 2 6 4 4 6 6 2 4 4

sz[]
0 1 2 3 4 5 6 7 8 9
1 1 3 1 4 1 6 1 1 1

Array accesses: 5 (1 for find(6), 3 for find(1) and 1 for updating parent)

1-0

id[]
0 1 2 3 4 5 6 7 8 9
6 2 6 4 4 6 6 2 4 4

sz[]
0 1 2 3 4 5 6 7 8 9
1 1 3 1 4 1 6 1 1 1

Array accesses: 8 (5 for find(1) and 3 for find(0))

6-7

id[]
0 1 2 3 4 5 6 7 8 9
6 2 6 4 4 6 6 2 4 4

sz[]
0 1 2 3 4 5 6 7 8 9
1 1 3 1 4 1 6 1 1 1

Array accesses: 6 (1 for find(6) and 5 for find(7))

Worst-case input

Beginning

id[]
0 1 2 3 4 5 6 7
0 1 2 3 4 5 6 7

sz[]
0 1 2 3 4 5 6 7
1 1 1 1 1 1 1 1

0-1

id[]
0 1 2 3 4 5 6 7
0 0 2 3 4 5 6 7

sz[]
0 1 2 3 4 5 6 7
2 1 1 1 1 1 1 1

Array accesses: 3 (1 for find(0), 1 for find(1) and 1 for updating parent)

2-3

id[]
0 1 2 3 4 5 6 7
0 0 2 2 4 5 6 7

sz[]
0 1 2 3 4 5 6 7
2 1 2 1 1 1 1 1

Array accesses: 3 (1 for find(2), 1 for find(3) and 1 for updating parent)

4-5

id[]
0 1 2 3 4 5 6 7
0 0 2 2 4 4 6 7

sz[]
0 1 2 3 4 5 6 7
2 1 2 1 2 1 1 1

Array accesses: 3 (1 for find(4), 1 for find(5) and 1 for updating parent)

6-7

id[]
0 1 2 3 4 5 6 7
0 0 2 2 4 4 6 6

sz[]
0 1 2 3 4 5 6 7
2 1 2 1 2 1 2 1

Array accesses: 3 (1 for find(6), 1 for find(7) and 1 for updating parent)

0-2

id[]
0 1 2 3 4 5 6 7
0 0 0 2 4 4 6 6

sz[]
0 1 2 3 4 5 6 7
4 1 2 1 2 1 2 1

Array accesses: 3 (1 for find(0), 1 for find(2) and 1 for updating parent)

4-6

id[]
0 1 2 3 4 5 6 7
0 0 0 2 4 4 4 6

sz[]
0 1 2 3 4 5 6 7
4 1 2 1 4 1 2 1

Array accesses: 3 (1 for find(4), 1 for find(6) and 1 for updating parent)

0-4

id[]
0 1 2 3 4 5 6 7
0 0 0 2 0 4 4 6

sz[]
0 1 2 3 4 5 6 7
8 1 2 1 4 1 2 1

Array accesses: 3 (1 for find(0), 1 for find(4) and 1 for updating parent)

Thanks to sergiovasquez122 (https://github.com/sergiovasquez122) for noticing that there were extra array fields in the worst case:
https://github.com/reneargento/algorithms-sedgewick-wayne/issues/168
Thanks to xzy2022 (https://github.com/xzy2022) for reporting an issue with the array access count in the find() method:
https://github.com/reneargento/algorithms-sedgewick-wayne/issues/307


Question: Repeat Exercise 1.5.5 for weighted quick-union.

Answer:
1.5.6

10^9 sites
10^6 input pairs
Computer can execute 10^9 instructions per second
Each iteration of the inner for loop requires 10 instructions

The object initialization makes ~10^10 instructions (1 for initializing the count variable, 1 for creating the sites array, 10^10 due to the for loop iterations over the sites, 10^9 for initializing each site, 1 for creating the size array, 10^9 for initializing each size)
Each find operation makes on the worst case lg(10^9) instructions * 10 due to the loop iterations over the sites ~ 300 instructions
Each union operation makes on the worst case ~650 instructions (300 for each find(), 1 for comparing sites of the components being united, 1 for comparing sizes, 1 for updating one of the sizes, 1 for increasing one of the sizes and 1 for reducing the count of components)

For 10^6 input pairs, the total number of instructions required (I) is:
I = M x IU

Where M is the number of input pairs and IU is the number of instructions in the union operation.

I = 10^6 x 650
I = 650,000,000

Seconds needed = 65 x 10^7 / 10^9
Seconds needed = 65 x 10^-2
Seconds needed = 0.65

0.65 seconds = 0.01 minutes = 0.0001 hours = 0.0000041 days

Thanks to QiotoF (https://github.com/QiotoF) for noting that the for loop instructions were not being considered.
https://github.com/reneargento/algorithms-sedgewick-wayne/issues/65


Question: Develop classes QuickUnionUF and QuickFindUF that implement quick-union and quick-find, respectively.

Answer:
```java
public class QuickUnionUF {
    private final int[] parent;
    private int count;

    public QuickUnionUF(int n) {
        count = n;
        parent = new int[n];
        for (int i = 0; i < n; i++) parent[i] = i;
    }

    public int count() { return count; }

    public int find(int p) {
        while (p != parent[p]) p = parent[p];
        return p;
    }

    public boolean connected(int p, int q) { return find(p) == find(q); }

    public void union(int p, int q) {
        int pRoot = find(p);
        int qRoot = find(q);
        if (pRoot == qRoot) return;
        parent[pRoot] = qRoot;
        count--;
    }
}

public class QuickFindUF {
    private final int[] id;
    private int count;

    public QuickFindUF(int n) {
        count = n;
        id = new int[n];
        for (int i = 0; i < n; i++) id[i] = i;
    }

    public int count() { return count; }
    public int find(int p) { return id[p]; }
    public boolean connected(int p, int q) { return id[p] == id[q]; }

    public void union(int p, int q) {
        int pID = id[p];
        int qID = id[q];
        if (pID == qID) return;
        for (int i = 0; i < id.length; i++) {
            if (id[i] == pID) id[i] = qID;
        }
        count--;
    }
}
```


Question:
Give a counterexample that shows why this intuitive implementation of union() for quick-find is not correct:
public void union(int p, int q)
{
 if (connected(p, q)) return;
 // Rename p’s component to q’s name.
 for (int i = 0; i < id.length; i++)
 if (id[i] == id[p]) id[i] = id[q];
 count--;
}

Answer:
1.5.8

In the loop, id[p] will eventually be set to id[q], losing the reference to the original parent.
This will make the next elements with id[i] == id[p] to not have their values updated.

Counterexample:

id[p] = 2
id[q] = 4

Array
0 1 2 2 4 2 6

0 != 2, is not updated
1 != 2, is not updated
2 == 2, is updated to 4 (after this, all comparisons are incorrectly made with 4 instead of 2)
2 != 4 is not updated (and should have been updated)
4 == 4 is updated to 4 (again)
2 != 4 is not updated (and should have been updated)
6 != 4 is not updated


Question: In the weighted quick-union algorithm, suppose that we set id[find(p)] to q instead of to id[find(q)]. Would the resulting algorithm be correct?

Answer:
1.5.10

Yes, but that would increase the maximum possible height of the trees to N, which would decrease find()'s
worst case performance from lg (N) to N and, consequently, decrease union()'s worst case performance to N.


Question: Quick-union with path compression. Modify quick-union (page 224) to include path compression, by adding a loop to union() that links every site on the paths from p and q to the roots of their trees to the root of the new tree. Give a sequence of input pairs that causes this method to produce a path of length 4. Note: The amortized cost per operation for this algorithm is known to be logarithmic.

Answer:
public void union(int p, int q) {
    int pRoot = root(p);
    int qRoot = root(q);
    if (pRoot == qRoot) {
        return;
    }

    id[pRoot] = qRoot;

    while (p != qRoot) {
        int next = id[p];
        id[p] = qRoot;
        p = next;
    }
    while (q != qRoot) {
        int next = id[q];
        id[q] = qRoot;
        q = next;
    }
    count--;
}

private int root(int p) {
    while (p != id[p]) {
        p = id[p];
    }
    return p;
}

One sequence that can still create a path of length 4 before compression on that path is: 0-1, 2-3, 4-5, 6-7, 6-4, 4-2, 4-0.


Question: Amortized costs plots. Instrument your implementations from Exercise 1.5.7 to make amortized costs plots like those in the text.

Answer:
// Exercise16_AmortizedCostsPlotsQF.java
package chapter1.section5;

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

import javax.swing.*;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by Rene Argento on 08/12/16.
 */
public class Exercise16_AmortizedCostsPlotsQF {

    private class QuickFind {

        int[] id;
        int count;

        // Used for plotting amortized costs
        int operation;
        int currentCost;
        int totalCost;
        List<Integer> total;

        public QuickFind(int size) {
            id = new int[size];
            count = size;

            operation = 1;
            currentCost = 0;
            totalCost = 0;
            total = new ArrayList<>();

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

        public int count() {
            return count;
        }

        // O(1)
        public int find(int site) {
            currentCost++;
            return id[site];
        }

        // O(1)
        public boolean connected(int site1, int site2) {
            boolean isConnected = find(site1) == find(site2);

            if (isConnected) {
                updateCostAnalysis();
            }
            return isConnected;
        }

        // O(n)
        public void union(int site1, int site2) {
            int leaderId1 = find(site1);
            int leaderId2 = find(site2);

            if (leaderId1 == leaderId2) {
                return;
            }

            for (int i = 0; i < id.length; i++) {
                currentCost++; // 1 access for every site

                if (id[i] == leaderId1) {
                    id[i] = leaderId2;

                    currentCost++; // 1 access for every component merged
                }
            }

            count--;
            updateCostAnalysis();
        }

        private void updateCostAnalysis() {
            totalCost += currentCost;

            total.add(totalCost / operation);

            currentCost = 0;
            operation++;
        }
    }

    public static void main(String[] args) {
        int numberOfSites = 100;

        Exercise16_AmortizedCostsPlotsQF amortizedCostsPlots = new Exercise16_AmortizedCostsPlotsQF();
        QuickFind quickFind = amortizedCostsPlots.new QuickFind(numberOfSites);

        for (int i = 0; i < 150; i++) {
            int randomSite1 = StdRandom.uniform(numberOfSites);
            int randomSite2 = StdRandom.uniform(numberOfSites);

            if (quickFind.connected(randomSite1, randomSite2)) {
                continue;
            }

            quickFind.union(randomSite1, randomSite2);
        }

        StdOut.println("Components: " + quickFind.count);

        amortizedCostsPlots.draw(quickFind);
    }

    private void draw(QuickFind quickFind) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                GraphPanel graphPanel = new GraphPanel("QuickFind", "Amortized Cost Plot",
                        "Number of Connections", "Number of Array Accesses", quickFind.total);
                graphPanel.createAndShowGui();
            }
        });
    }
}

// Exercise16_AmortizedCostsPlotsQU.java
package chapter1.section5;

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

import javax.swing.*;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by Rene Argento on 08/12/16.
 */
public class Exercise16_AmortizedCostsPlotsQU {

    private class QuickUnion {
        int[] id;
        int count;

        // Used for plotting amortized costs
        int operation;
        int currentCost;
        int totalCost;
        List<Integer> total;

        public QuickUnion(int size) {
            id = new int[size];
            count = size;

            operation = 1;
            currentCost = 0;
            totalCost = 0;
            total = new ArrayList<>();

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

        public int count() {
            return count;
        }

        // O(1)
        public int find(int site) {
            currentCost++;

            while (site != id[site]) {
                currentCost++;

                site = id[site];
            }

            return site;
        }

        // O(1)
        public boolean connected(int site1, int site2) {
            boolean isConnected = find(site1) == find(site2);

            if (isConnected) {
                updateCostAnalysis();
            }

            return isConnected;
        }

        // O(n)
        public void union(int site1, int site2) {
            int leaderId1 = find(site1);
            int leaderId2 = find(site2);

            if (leaderId1 == leaderId2) {
                return;
            }

            id[leaderId1] = leaderId2;

            count--;

            currentCost++;
            updateCostAnalysis();
        }

        private void updateCostAnalysis() {
            totalCost += currentCost;

            total.add(totalCost / operation);

            currentCost = 0;
            operation++;
        }
    }

    public static void main(String[] args) {
        int numberOfSites = 100;

        Exercise16_AmortizedCostsPlotsQU amortizedCostsPlots = new Exercise16_AmortizedCostsPlotsQU();
        QuickUnion quickUnion = amortizedCostsPlots.new QuickUnion(numberOfSites);

        for (int i = 0; i < 150; i++) {
            int randomSite1 = StdRandom.uniform(numberOfSites);
            int randomSite2 = StdRandom.uniform(numberOfSites);

            if (quickUnion.connected(randomSite1, randomSite2)) {
                continue;
            }

            quickUnion.union(randomSite1, randomSite2);
        }

        StdOut.println("Components: " + quickUnion.count);

        amortizedCostsPlots.draw(quickUnion);
    }

    private void draw(QuickUnion quickUnion) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                GraphPanel graphPanel = new GraphPanel("QuickUnion", "Amortized Cost Plot",
                        "Number of Connections", "Number of Array Accesses", quickUnion.total);
                graphPanel.createAndShowGui();
            }
        });
    }
}


Question: Random connections. Develop a UF client ErdosRenyi that takes an integer value N from the command line, generates random pairs of integers between 0 and N-1, calling connected() to determine if they are connected and then union() if not (as in our development client), looping until all sites are connected, and printing the number of connections generated. Package your program as a static method count() that takes N as argument and returns the number of connections and a main() that takes N from the command line, calls count(), and prints the returned value.

Answer:
```java
import edu.princeton.cs.algs4.StdOut;
import edu.princeton.cs.algs4.StdRandom;
import edu.princeton.cs.algs4.WeightedQuickUnionUF;

public class ErdosRenyi {
    public static int count(int n) {
        WeightedQuickUnionUF uf = new WeightedQuickUnionUF(n);
        int connections = 0;
        while (uf.count() > 1) {
            int p = StdRandom.uniformInt(n);
            int q = StdRandom.uniformInt(n);
            connections++;
            if (!uf.connected(p, q)) uf.union(p, q);
        }
        return connections;
    }

    public static void main(String[] args) {
        int n = Integer.parseInt(args[0]);
        StdOut.println(count(n));
    }
}
```


Question:
Random grid generator. Write a program RandomGrid that takes an int value N from the command line, generates all the connections in an N-by-N grid, puts them in random order, randomly orients them (so that p q and q p are equally likely to occur), and prints the result to standard output. To randomly order the connections, use a RandomBag (see Exercise 1.3.34 on page 167). To encapsulate p and q in a single object, use the Connection nested class shown below. Package your program as two static methods: generate(), which takes N as argument and returns an array of connections, and main(), which takes N from the command line, calls generate(), and iterates through the returned array to print the connections.
private class Connection
{
 int p;
 int q;
 public Connection(int p, int q)
 { this.p = p; this.q = q; }
}

Answer:
package chapter1.section5;

import chapter1.section3.Exercise34_RandomBag;
import edu.princeton.cs.algs4.StdOut;
import edu.princeton.cs.algs4.StdRandom;

public class RandomGrid {
    public static class Connection {
        int p;
        int q;
        Connection(int p, int q) {
            this.p = p;
            this.q = q;
        }
    }

    public static Connection[] generate(int n) {
        Exercise34_RandomBag<Connection> bag = new Exercise34_RandomBag<>();
        for (int row = 0; row < n; row++) {
            for (int col = 0; col < n; col++) {
                int site = row * n + col;
                if (col + 1 < n) addRandomOrientation(bag, site, site + 1);
                if (row + 1 < n) addRandomOrientation(bag, site, site + n);
            }
        }

        Connection[] result = new Connection[bag.size()];
        int i = 0;
        for (Connection c : bag) result[i++] = c;
        return result;
    }

    private static void addRandomOrientation(Exercise34_RandomBag<Connection> bag, int p, int q) {
        if (StdRandom.bernoulli()) bag.add(new Connection(p, q));
        else bag.add(new Connection(q, p));
    }

    public static void main(String[] args) {
        int n = Integer.parseInt(args[0]);
        for (Connection c : generate(n)) {
            StdOut.println(c.p + " " + c.q);
        }
    }
}


Question: Animation. Write a RandomGrid client (see Exercise 1.5.18) that uses UnionFind as in our development client to check connectivity and uses StdDraw to draw the connections as they are processed.

Answer:
Use `RandomGrid.generate(N)` to produce all grid connections in random order, draw each connection as it is processed, and stop when the union-find structure reports one component.

```java
public static void main(String[] args) {
    int n = Integer.parseInt(args[0]);
    int sites = n * n;
    WeightedQuickUnionUF uf = new WeightedQuickUnionUF(sites);
    StdDraw.setXscale(-1, n);
    StdDraw.setYscale(-1, n);

    for (RandomGrid.Connection c : RandomGrid.generate(n)) {
        int p = c.p;
        int q = c.q;
        drawConnection(p, q, n);
        if (!uf.connected(p, q)) uf.union(p, q);
        if (uf.count() == 1) break;
    }
}
```

The key correction is that `N` comes from the command line and every randomized grid connection is considered, not just a hard-coded 4-by-4 prefix.


Question: Doubling test for random grids. Develop a performance-testing client that takes an int value T from the command line and performs T trials of the following experiment: Use your client from Exercise 1.5.18 to generate the connections in an N-by-N square grid, randomly oriented and in random order, then use UnionFind to determine connectivity as in our development client, looping until all sites are connected. For each N, print the value of N, the average number of connections processed, and the ratio of the running time to the previous. Use your program to validate the hypotheses in the text that the running times for quick-find and quick-union are quadratic and weighted quick-union is near-linear. Note: As N doubles, the number of sites in the grid increases by a factor of 4, so expect a doubling factor of 16 for quadratic and 4 for linear.

Answer:
For each grid size `N`, run exactly `T` independent trials, average the number of connections processed and the elapsed time, then double `N`.

```java
for (int n = 8; true; n += n) {
    Stopwatch timer = new Stopwatch();
    long totalConnections = 0;
    for (int t = 0; t < T; t++) {
        WeightedQuickUnionUF uf = new WeightedQuickUnionUF(n * n);
        int processed = 0;
        for (RandomGrid.Connection c : RandomGrid.generate(n)) {
            processed++;
            if (!uf.connected(c.p, c.q)) uf.union(c.p, c.q);
            if (uf.count() == 1) break;
        }
        totalConnections += processed;
    }
    double time = timer.elapsedTime();
    StdOut.printf("%8d %12.2f %10.3f\n", n, (double) totalConnections / T, time);
}
```

`T` is the number of trials for each `N`; it is not the number of different grid sizes to test.


Question: Show, in the style of the example trace with Algorithm 2.1, how selection sort sorts the array E A S Y Q U E S T I O N.

Answer:
2.1.1
		a[]
i  min  0 1 2 3 4 5 6 7 8 9 10 11
	E A S Y Q U E S T I  O  N
0    1  E A S Y Q U E S T I  O  N
1    1  A E S Y Q U E S T I  O  N
2    6  A E S Y Q U E S T I  O  N
3    9  A E E Y Q U S S T I  O  N
4   11  A E E I Q U S S T Y  O  N
5   10  A E E I N U S S T Y  O  Q
6   11  A E E I N O S S T Y  U  Q
7    7  A E E I N O Q S T Y  U  S
8   11  A E E I N O Q S T Y  U  S
9   11  A E E I N O Q S S Y  U  T
10  10  A E E I N O Q S S T  U  Y
11  11  A E E I N O Q S S T  U  Y
	A E E I N O Q S S T  U  Y


Question: What is the maximum number of exchanges involving any particular element during selection sort? What is the average number of exchanges involving an element?

Answer:
2.1.2
The maximum number of exchanges involving any particular item during selection sort is N - 1.
This happens when the first item has the highest value in the unsorted array and the other values are sorted.
For example, in the array 4 1 2 3, the element 4 will be swapped N - 1 times.
The average number of exchanges involving an item is exactly 2, because there are exactly N exchanges and N items (and each exchange involves two items).

Reference: https://algs4.cs.princeton.edu/21elementary/


Question: Give an example of an array of N items that maximizes the number of times the test a[j] < a[min] succeeds (and, therefore, min gets updated) during the operation of selection sort (Algorithm 2.1).

Answer:
2.1.3
Array: H G F E D C B A

Thanks to QiotoF (https://github.com/QiotoF) for mentioning a better array solution for this exercise
https://github.com/reneargento/algorithms-sedgewick-wayne/issues/76


Question: Show, in the style of the example trace with Algorithm 2.2, how insertion sort sorts the array E A S Y Q U E S T I O N.

Answer:
2.1.4
		a[]
i    j  0 1 2 3 4 5 6 7 8 9 10 11
	E A S Y Q U E S T I  O  N
0    0  E A S Y Q U E S T I  O  N
1    0  A E S Y Q U E S T I  O  N
2    2  A E S Y Q U E S T I  O  N
3    3  A E S Y Q U E S T I  O  N
4    2  A E Q S Y U E S T I  O  N
5    4  A E Q S U Y E S T I  O  N
6    2  A E E Q S U Y S T I  O  N
7    5  A E E Q S S U Y T I  O  N
8    6  A E E Q S S T U Y I  O  N
9    3  A E E I Q S S T U Y  O  N
10   4  A E E I O Q S S T U  Y  N
11   4  A E E I N O Q S S T  U  Y
	A E E I N O Q S S T  U  Y


Question: For each of the two conditions in the inner for loop in insertion sort (Algorithm 2.2), describe an array of N items where that condition is always false when the loop terminates.

Answer:
2.1.5

Condition 1: j > 0 -> When the array is reverse ordered
Z Q K D C B A

Condition 2: less(a[j], a[j - 1]) -> When the array is ordered
A B C D E F G


Question: Which method runs faster for an array with all keys identical, selection sort or insertion sort?

Answer:
2.1.6

Insertion sort because it will only make one comparison with the previous element (per element) and won't exchange any elements,
running in linear time. Selection sort will exchange each element with itself and will run in quadratic time.

Thanks to glucu (https://github.com/glucu) for correcting the number of exchanges done in selection sort.


Question: Which method runs faster for an array in reverse order, selection sort or insertion sort?

Answer:
2.1.7

Selection sort because even though both selection sort and insertion sort will run in quadratic time, selection sort will
only make N exchanges, while insertion sort will make N * N / 2 exchanges.

Thanks to LudekCizinsky (https://github.com/LudekCizinsky), rg9a27 (https://github.com/rg9a27) and
BOTbkcd (https://github.com/BOTbkcd) for correcting the number of exchanges in selection sort.
https://github.com/reneargento/algorithms-sedgewick-wayne/issues/194 and
https://github.com/reneargento/algorithms-sedgewick-wayne/issues/211


Question: Suppose that we use insertion sort on a randomly ordered array where elements have only one of three values. Is the running time linear, quadratic, or something in between?

Answer:
2.1.8

Quadratic. Insertion sort's running time is linear when the array is already sorted or all elements are equal.
With three possible values the running time quadratic.


Question: Show, in the style of the example trace with Algorithm 2.3, how shellsort sorts the array E A S Y S H E L L S O R T Q U E S T I O N.

Answer:
2.1.9
				a[]
 h   i   j  0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
	    E A S Y S H E L L S  O  R  T  Q  U  E  S  T  I  O  N
13  13  13  E A S Y S H E L L S  O  R  T  Q  U  E  S  T  I  O  N
13  14  14  E A S Y S H E L L S  O  R  T  Q  U  E  S  T  I  O  N
13  15   2  E A E Y S H E L L S  O  R  T  Q  U  S  S  T  I  O  N
13  16   3  E A E S S H E L L S  O  R  T  Q  U  S  Y  T  I  O  N
13  17  17  E A E S S H E L L S  O  R  T  Q  U  S  Y  T  I  O  N
13  18  18  E A E S S H E L L S  O  R  T  Q  U  S  Y  T  I  O  N
13  19  19  E A E S S H E L L S  O  R  T  Q  U  S  Y  T  I  O  N
13  20  20  E A E S S H E L L S  O  R  T  Q  U  S  Y  T  I  O  N
	    E A E S S H E L L S  O  R  T  Q  U  S  Y  T  I  O  N
 4   4   4  E A E S S H E L L S  O  R  T  Q  U  S  Y  T  I  O  N
 4   5   5  E A E S S H E L L S  O  R  T  Q  U  S  Y  T  I  O  N
 4   6   6  E A E S S H E L L S  O  R  T  Q  U  S  Y  T  I  O  N
 4   7   3  E A E L S H E S L S  O  R  T  Q  U  S  Y  T  I  O  N
 4   8   4  E A E L L H E S S S  O  R  T  Q  U  S  Y  T  I  O  N
 4   9   9  E A E L L H E S S S  O  R  T  Q  U  S  Y  T  I  O  N
 4  10  10  E A E L L H E S S S  O  R  T  Q  U  S  Y  T  I  O  N
 4  11   7  E A E L L H E R S S  O  S  T  Q  U  S  Y  T  I  O  N
 4  12  12  E A E L L H E R S S  O  S  T  Q  U  S  Y  T  I  O  N
 4  13   9  E A E L L H E R S Q  O  S  T  S  U  S  Y  T  I  O  N
 4  14  14  E A E L L H E R S Q  O  S  T  S  U  S  Y  T  I  O  N
 4  15  15  E A E L L H E R S Q  O  S  T  S  U  S  Y  T  I  O  N
 4  16  16  E A E L L H E R S Q  O  S  T  S  U  S  Y  T  I  O  N
 4  17  17  E A E L L H E R S Q  O  S  T  S  U  S  Y  T  I  O  N
 4  18  10  E A E L L H E R S Q  I  S  T  S  O  S  Y  T  U  O  N
 4  19   7  E A E L L H E O S Q  I  R  T  S  O  S  Y  T  U  S  N
 4  20   8  E A E L L H E O N Q  I  R  S  S  O  S  T  T  U  S  Y
	    E A E L L H E O N Q  I  R  S  S  O  S  T  T  U  S  Y
 1   1   0  A E E L L H E O N Q  I  R  S  S  O  S  T  T  U  S  Y
 1   2   2  A E E L L H E O N Q  I  R  S  S  O  S  T  T  U  S  Y
 1   3   3  A E E L L H E O N Q  I  R  S  S  O  S  T  T  U  S  Y
 1   4   4  A E E L L H E O N Q  I  R  S  S  O  S  T  T  U  S  Y
 1   5   3  A E E H L L E O N Q  I  R  S  S  O  S  T  T  U  S  Y
 1   6   3  A E E E H L L O N Q  I  R  S  S  O  S  T  T  U  S  Y
 1   7   7  A E E E H L L O N Q  I  R  S  S  O  S  T  T  U  S  Y
 1   8   7  A E E E H L L N O Q  I  R  S  S  O  S  T  T  U  S  Y
 1   9   9  A E E E H L L N O Q  I  R  S  S  O  S  T  T  U  S  Y
 1  10   5  A E E E H I L L N O  Q  R  S  S  O  S  T  T  U  S  Y
 1  11  11  A E E E H I L L N O  Q  R  S  S  O  S  T  T  U  S  Y
 1  12  12  A E E E H I L L N O  Q  R  S  S  O  S  T  T  U  S  Y
 1  13  13  A E E E H I L L N O  Q  R  S  S  O  S  T  T  U  S  Y
 1  14  10  A E E E H I L L N O  O  Q  R  S  S  S  T  T  U  S  Y
 1  15  15  A E E E H I L L N O  O  Q  R  S  S  S  T  T  U  S  Y
 1  16  16  A E E E H I L L N O  O  Q  R  S  S  S  T  T  U  S  Y
 1  17  17  A E E E H I L L N O  O  Q  R  S  S  S  T  T  U  S  Y
 1  18  18  A E E E H I L L N O  O  Q  R  S  S  S  T  T  U  S  Y
 1  19  16  A E E E H I L L N O  O  Q  R  S  S  S  S  T  T  U  Y
 1  20  20  A E E E H I L L N O  O  Q  R  S  S  S  S  T  T  U  Y
	    A E E E H I L L N O  O  Q  R  S  S  S  S  T  T  U  Y


Question: Why not use selection sort for h-sorting in shellsort?

Answer:
2.1.10
Insertion sort is faster than selection sort for h-sorting because as "h" decreases, the array becomes partially sorted.
Insertion sort makes less comparisons in partially sorted arrays than selection sort.
Also, when h-sorting, eventually h will have an increment value of 1.
Using selection sort with an increment value of 1 would be the same as using the standard selection sort algorithm from the beginning.
This would make the steps with the previous increments to be unnecessary work.

Thanks to jaeheonshim (https://github.com/jaeheonshim) for adding an extra reason not to use selection sort.
https://github.com/reneargento/algorithms-sedgewick-wayne/issues/254


Question: Deck sort. Explain how you would put a deck of cards in order by suit (in the order spades, hearts, clubs, diamonds) and by rank within each suit, with the restriction that the cards must be laid out face down in a row, and the only allowed operations are to check the values of two cards and to exchange two cards (keeping them face down).

Answer:
2.1.13 - Deck sort
I would use selection sort, comparing the cards first by suit, and if they have the same suit, by rank.
As we are dealing with physical objects it makes sense to minimize the number of swaps.
With selection sort it may be needed to look at more cards than insertion sort (twice as many in the average case),
but swaps will be required at most 52 times versus at most 676 times.

Thanks to zefrawg (https://github.com/zefrawg) and nedas-dev (https://github.com/nedas-dev) for improving this exercise:
https://github.com/reneargento/algorithms-sedgewick-wayne/issues/198


Question: Dequeue sort. Explain how you would sort a deck of cards, with the restriction that the only allowed operations are to look at the values of the top two cards, to exchange the top two cards, and to move the top card to the bottom of the deck.

Answer:
2.1.14 - Dequeue sort
I would use a variation of bubble sort.

1- I would compare both top cards and, if the top card were bigger than the second card, I would swap them.
2- I would mark the top card, so I could know it was the first card (in this iteration) sent to the bottom of the deck.
3- I would send the top card to the bottom of the deck.
4- I would repeat steps 1 and 3 until the marked card becomes the second card in the deck
5- I would send the top card to the bottom of the deck (and the marked card is now at the top, signaling that a iteration is over)
6- If there were no swaps in this iteration, the deck is sorted. Otherwise, repeat steps 1 to 6.

Nice explanation here:
http://stackoverflow.com/questions/38061140/sort-a-deck-of-cards


Question: Expensive exchange. A clerk at a shipping company is charged with the task of rearranging a number of large crates in order of the time they are to be shipped out. Thus, the cost of compares is very low (just look at the labels) relative to the cost of exchanges (move the crates). The warehouse is nearly full—there is extra space sufficient to hold any one of the crates, but not two. What sorting method should the clerk use?

Answer:
2.1.15 - Expensive exchange

The clerk should use selection sort. Since the cost of compares is low, the N^2 complexity won't be a problem.
And it guarantees a cost of at most N exchanges.


Question: Shellsort best case. What is the best case for shellsort? Justify your answer.

Answer:
2.1.20 - Shellsort best case

Just like insertion sort, the best case for shellsort is when the array is already ordered. This causes every element to
be compared only once in each iteration.
The time complexity in this case is O(n log n).

Good explanation: https://www.toptal.com/developers/sorting-algorithms/shell-sort
http://www.codingeek.com/algorithms/shell-sort-algorithm-explanation-implementation-and-complexity/


Question: Give a trace, in the style of the trace given at the beginning of this section, showing how the keys A E Q S U Y E I N O S T are merged with the abstract in-place merge() method.

Answer:
2.2.1
                             a[]                                           aux[]
       k   0  1  2  3  4  5  6  7  8  9  10  11     i  j    0  1  2  3  4  5  6  7  8  9  10  11
input      A  E  Q  S  U  Y  E  I  N  O   S   T             -  -  -  -  -  -  -  -  -  -   -   -
copy       A  E  Q  S  U  Y  E  I  N  O   S   T             A  E  Q  S  U  Y  E  I  N  O   S   T
                                                    0  6
       0   A                                        1  6    A  E  Q  S  U  Y  E  I  N  O   S   T
       1   A  E                                     2  6       E  Q  S  U  Y  E  I  N  O   S   T
       2   A  E  E                                  2  7          Q  S  U  Y  E  I  N  O   S   T
       3   A  E  E  I                               2  8          Q  S  U  Y     I  N  O   S   T
       4   A  E  E  I  N                            2  9          Q  S  U  Y        N  O   S   T
       5   A  E  E  I  N  O                         2 10          Q  S  U  Y           O   S   T
       6   A  E  E  I  N  O  Q                      3 10          Q  S  U  Y               S   T
       7   A  E  E  I  N  O  Q  S                   4 10             S  U  Y               S   T
       8   A  E  E  I  N  O  Q  S  S                4 11                U  Y               S   T
       9   A  E  E  I  N  O  Q  S  S  T             4 12                U  Y                   T
      10   A  E  E  I  N  O  Q  S  S  T   U         5 12                U  Y
      11   A  E  E  I  N  O  Q  S  S  T   U   Y     6 12                   Y
merged result
           A  E  E  I  N  O  Q  S  S  T   U   Y


Question: Give traces, in the style of the trace given with Algorithm 2.4, showing how the keys E A S Y Q U E S T I O N are sorted with top-down mergesort.

Answer:
2.2.2
                                              a[]
                          0  1  2  3  4  5  6  7  8  9  10  11
                          E  A  S  Y  Q  U  E  S  T  I   O   N
       merge(a, 0, 0, 1)  A  E  S  Y  Q  U  E  S  T  I   O   N
     merge(a, 0, 1, 2)    A  E  S  Y  Q  U  E  S  T  I   O   N
       merge(a, 3, 3, 4)  A  E  S  Q  Y  U  E  S  T  I   O   N
     merge(a, 3, 4, 5)    A  E  S  Q  U  Y  E  S  T  I   O   N
   merge(a, 0, 2, 5)      A  E  Q  S  U  Y  E  S  T  I   O   N
       merge(a, 6, 6, 7)  A  E  Q  S  U  Y  E  S  T  I   O   N
     merge(a, 6, 7, 8)    A  E  Q  S  U  Y  E  S  T  I   O   N
       merge(a, 9, 9, 10) A  E  Q  S  U  Y  E  S  T  I   O   N
     merge(a, 9, 10, 11)  A  E  Q  S  U  Y  E  S  T  I   N   O
   merge(a, 6, 8, 11)     A  E  Q  S  U  Y  E  I  N  O   S   T
 merge(a, 0, 5, 11)       A  E  E  I  N  O  Q  S  S  T   U   Y
                          A  E  E  I  N  O  Q  S  S  T   U   Y


Question: Answer Exercise 2.2.2 for bottom-up mergesort.

Answer:
2.2.3

                                              a[]
                             0  1  2  3  4  5  6  7  8  9  10  11
       sz = 1                E  A  S  Y  Q  U  E  S  T  I   O   N
       merge(a, 0, 0, 1)     A  E  S  Y  Q  U  E  S  T  I   O   N
       merge(a, 2, 2, 3)     A  E  S  Y  Q  U  E  S  T  I   O   N
       merge(a, 4, 4, 5)     A  E  S  Y  Q  U  E  S  T  I   O   N
       merge(a, 6, 6, 7)     A  E  S  Y  Q  U  E  S  T  I   O   N
       merge(a, 8, 8, 9)     A  E  S  Y  Q  U  E  S  I  T   O   N
       merge(a, 10, 10, 11)  A  E  S  Y  Q  U  E  S  I  T   N   O
     sz = 2
     merge(a, 0, 1, 3)       A  E  S  Y  Q  U  E  S  I  T   N   O
     merge(a, 4, 5, 7)       A  E  S  Y  E  Q  S  U  I  T   N   O
     merge(a, 8, 9, 11)      A  E  S  Y  E  Q  S  U  I  N   O   T
   sz = 4
   merge(a, 0, 3, 7)         A  E  E  Q  S  S  U  Y  I  N   O   T
sz = 8
merge(a, 0, 7, 11)           A  E  E  I  N  O  Q  S  S  T   U   Y
                             A  E  E  I  N  O  Q  S  S  T   U   Y


Question: Does the abstract in-place merge produce proper output if and only if the two input subarrays are in sorted order? Prove your answer, or provide a counterexample.

Answer:
2.2.4

Yes. The merge phase uses two pointers that move comparing both subarray values.
Once it finds that one value is smaller than the other, it selects this value for the output without checking the other elements.
If one or more of the input subarrays are not sorted then some values would be considered in the wrong position during this comparison, leading to an incorrect output.


Question: Give the sequence of subarray sizes in the merges performed by both the top-down and the bottom-up mergesort algorithms, for N = 39.

Answer:
2.2.5

Top-down mergesort:
2, 3, 2, 5, 2, 3, 2, 5, 10, 2, 3, 2, 5, 2, 3, 2, 5, 10, 20, 2, 3, 2, 5, 2, 3, 2, 5, 10, 2, 3, 2, 5, 2, 2, 4, 9, 19, 39.

Bottom-up mergesort:
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 8, 8, 8, 8, 7, 16, 16, 32, 39.