Question: All-pairs shortest path on a line. Given a weighted line graph (undirected connected graph, all vertices of degree 2, except two endpoints which have degree 1), devise an algorithm that preprocesses the graph in linear time and can return the distance of the shortest path between any two vertices in constant time.

Answer:
package chapter4.section4;

import chapter1.section3.Queue;
import chapter4.section3.Edge;
import chapter4.section3.EdgeWeightedGraph;
import edu.princeton.cs.algs4.StdOut;

/**
 * Created by Rene Argento on 09/12/17.
 */
public class Exercise31_AllPairsShortestPathsOnALine {

    public class AllPairsShortestPathsOnALine {

        private double[] distanceFromSource;

        AllPairsShortestPathsOnALine(EdgeWeightedGraph edgeWeightedGraph) {

            boolean isLineGraph = true;
            int numberOfVerticesWithDegree1 = 0;
            int sourceVertex = -1;

            // Find one of the two sources
            for (int vertex = 0; vertex < edgeWeightedGraph.vertices(); vertex++) {
                int outdegree = 0;

                for (Edge edge : edgeWeightedGraph.adjacent(vertex)) {
                    if (edge.weight() < 0) {
                        throw new IllegalArgumentException("Edge weights cannot be negative");
                    }

                    outdegree++;
                }

                if (outdegree == 1) {
                    if (sourceVertex == -1) {
                        sourceVertex = vertex;
                    }

                    numberOfVerticesWithDegree1++;
                } else if (outdegree == 0 || outdegree > 2) {
                    isLineGraph = false;
                    break;
                }
            }

            if (numberOfVerticesWithDegree1 != 2) {
                isLineGraph = false;
            }

            if (!isLineGraph) {
                throw new IllegalArgumentException("Graph is not a line graph");
            }

            distanceFromSource = new double[edgeWeightedGraph.vertices()];
            boolean[] visited = new boolean[edgeWeightedGraph.vertices()];

            for (int vertex = 0; vertex < distanceFromSource.length; vertex++) {
                distanceFromSource[vertex] = Double.POSITIVE_INFINITY;
            }

            // Do a breadth-first-search to compute the distances from the source in O(V + E)
            Queue<Integer> queue = new Queue<>();
            queue.enqueue(sourceVertex);
            visited[sourceVertex] = true;

            distanceFromSource[sourceVertex] = 0;

            while (!queue.isEmpty()) {
                int currentVertex = queue.dequeue();

                for (Edge edge : edgeWeightedGraph.adjacent(currentVertex)) {
                    int neighbor = edge.other(currentVertex);

                    if (!visited[neighbor]) {
                        distanceFromSource[neighbor] = distanceFromSource[currentVertex] + edge.weight();
                        queue.enqueue(neighbor);
                        visited[neighbor] = true;
                    }
                }
            }
        }

        public double dist(int source, int target) {
            return Math.abs(distanceFromSource[source] - distanceFromSource[target]);
        }
    }

    public static void main(String[] args) {
        EdgeWeightedGraph edgeWeightedGraph = new EdgeWeightedGraph(5);
        edgeWeightedGraph.addEdge(new Edge(0, 1, 2));
        edgeWeightedGraph.addEdge(new Edge(1, 2, 3));
        edgeWeightedGraph.addEdge(new Edge(2, 3, 4));
        edgeWeightedGraph.addEdge(new Edge(3, 4, 1));

        AllPairsShortestPathsOnALine allPairsShortestPathsOnALine =
                new Exercise31_AllPairsShortestPathsOnALine().new AllPairsShortestPathsOnALine(edgeWeightedGraph);

        double[][] expectedDistances = {
                {0, 2, 5, 9, 10},
                {2, 0, 3, 7, 8},
                {5, 3, 0, 4, 5},
                {9, 7, 4, 0, 1},
                {10, 8, 5, 1, 0}
        };

        for (int source = 0; source < edgeWeightedGraph.vertices(); source++) {
            for (int target = 0; target < edgeWeightedGraph.vertices(); target++) {
                StdOut.println("Distance from " + source + " to " + target + ": " +
                        allPairsShortestPathsOnALine.dist(source, target)
                        + " Expected: " + expectedDistances[source][target]);
            }
        }
    }
}


Question: Shortest path in a grid. Given an N-by-N matrix of positive integers, find the shortest path from the (0, 0) entry to the (N-1, N-1) entry, where the length of the path is the sum of the integers in the path. Repeat the problem but assume you can only move right and down.

Answer:
package chapter4.section4;

import edu.princeton.cs.algs4.StdOut;

/**
 * Created by Rene Argento on 10/12/17.
 */
public class Exercise33_ShortestPathInAGrid {

    public Iterable<DirectedEdge> shortestPathInGridAll4Directions(double[][] matrix) {
        EdgeWeightedDigraph edgeWeightedDigraph = new EdgeWeightedDigraph(matrix.length * matrix[0].length);

        for (int row = 0; row < matrix.length; row++) {
            for (int column = 0; column < matrix[0].length; column++) {

                int currentCellIndex = getCellIndex(matrix, row, column);

                int[] neighborRows = {-1, 0, 0, 1};
                int[] neighborColumns = {0, -1, 1, 0};

                for (int i = 0; i < neighborRows.length; i++) {
                    int neighborRow = row + neighborRows[i];
                    int neighborColumn = column + neighborColumns[i];

                    if (isValidCell(matrix, neighborRow, neighborColumn)) {
                        int neighborCellIndex = getCellIndex(matrix, neighborRow, neighborColumn);
                        edgeWeightedDigraph.addEdge(new DirectedEdge(currentCellIndex, neighborCellIndex,
                                matrix[neighborRow][neighborColumn]));
                    }
                }
            }
        }

        int targetCell = matrix.length * matrix.length - 1;

        DijkstraSP dijkstraSP = new DijkstraSP(edgeWeightedDigraph, 0);
        return dijkstraSP.pathTo(targetCell);
    }

    public Iterable<DirectedEdge> shortestPathInGridOnlyRightOrDown(double[][] matrix) {
        EdgeWeightedDigraph edgeWeightedDigraph = new EdgeWeightedDigraph(matrix.length * matrix[0].length);

        for (int row = 0; row < matrix.length; row++) {
            for (int column = 0; column < matrix[0].length; column++) {

                int currentCellIndex = getCellIndex(matrix, row, column);

                int[] neighborRows = {0, 1};
                int[] neighborColumns = {1, 0};

                for (int i = 0; i < neighborRows.length; i++) {
                    int neighborRow = row + neighborRows[i];
                    int neighborColumn = column + neighborColumns[i];

                    if (isValidCell(matrix, neighborRow, neighborColumn)) {
                        int neighborCellIndex = getCellIndex(matrix, neighborRow, neighborColumn);
                        edgeWeightedDigraph.addEdge(new DirectedEdge(currentCellIndex, neighborCellIndex,
                                matrix[neighborRow][neighborColumn]));
                    }
                }
            }
        }

        int targetCell = matrix.length * matrix.length - 1;

        DijkstraSP dijkstraSP = new DijkstraSP(edgeWeightedDigraph, 0);
        return dijkstraSP.pathTo(targetCell);
    }

    private boolean isValidCell(double[][] matrix, int row, int column) {
        return row >= 0 && row < matrix.length && column >= 0 && column < matrix[0].length;
    }

    private int getCellIndex(double[][] matrix, int row, int column) {
        return matrix.length * row + column;
    }

    public static void main(String[] args) {
        Exercise33_ShortestPathInAGrid shortestPathInAGrid = new Exercise33_ShortestPathInAGrid();

        StdOut.println("Moving either up, down, left or right:");
        double[][] matrix1 = {
                {0, 1},
                {3, 1}
        };

        StdOut.print("Path:     ");
        Iterable<DirectedEdge> shortestPath1 = shortestPathInAGrid.shortestPathInGridAll4Directions(matrix1);
        for (DirectedEdge edge : shortestPath1) {
            StdOut.print(edge.from() + "->" + edge.to() + " ");
        }
        StdOut.println("\nExpected: 0->1 1->3");

        double[][] matrix2 = {
                {0, 2, 1},
                {1, 3, 2},
                {4, 2, 5}
        };

        StdOut.print("\nPath:     ");
        Iterable<DirectedEdge> shortestPath2 = shortestPathInAGrid.shortestPathInGridAll4Directions(matrix2);
        for (DirectedEdge edge : shortestPath2) {
            StdOut.print(edge.from() + "->" + edge.to() + " ");
        }
        StdOut.println("\nExpected: 0->1 1->2 2->5 5->8");

        double[][] matrix3 = {
                {0,   4, 10, 10, 10},
                {1,   8,  1,  1,  1},
                {1,   8,  1, 10,  1},
                {1,   1,  1, 10,  1},
                {10, 10, 10, 10,  2}
        };

        StdOut.print("\nPath:     ");
        Iterable<DirectedEdge> shortestPath3 = shortestPathInAGrid.shortestPathInGridAll4Directions(matrix3);
        for (DirectedEdge edge : shortestPath3) {
            StdOut.print(edge.from() + "->" + edge.to() + " ");
        }
        StdOut.println("\nExpected: 0->5 5->10 10->15 15->16 16->17 17->12 12->7 7->8 8->9 9->14 14->19 19->24");

        StdOut.println("\nMoving only right and down:");
        StdOut.print("Path:     ");

        Iterable<DirectedEdge> shortestPath4 = shortestPathInAGrid.shortestPathInGridOnlyRightOrDown(matrix3);
        for (DirectedEdge edge : shortestPath4) {
            StdOut.print(edge.from() + "->" + edge.to() + " ");
        }
        StdOut.println("\nExpected: 0->5 5->6 6->7 7->8 8->9 9->14 14->19 19->24");
    }
}


Question: Bitonic shortest path. Given a digraph, find a bitonic shortest path from s to every other vertex (if one exists). A path is bitonic if there is an intermediate vertex v such that the edges on the path from s to v are strictly increasing and the edges on the path from v to t are strictly decreasing. The path should be simple (no repeated vertices).

Answer:
package chapter4.section4;

import chapter2.section4.PriorityQueueResize;
import chapter3.section4.SeparateChainingHashTable;
import edu.princeton.cs.algs4.StdOut;

import java.util.*;

/**
 * Created by Rene Argento on 10/12/17.
 */
// If we didn't need a simple path (if repeated vertices were allowed), it would be possible to
    // 1- relax all edges in ascending order, starting from vertex s, to get the shortest ascending distance from it
    // to all other vertices.
    // 2- for every vertex v: relax all edges in descending order starting from v to get the shortest descending distance
    // from it to all other vertices.
    // 3- test all combinations of ascending order paths and descending order paths to get the shortest bitonic path
    // (based on http://tryalgo.org/en/shortest-paths/2016/06/25/bitonic-shortest-path/ )

    // Since we need simple paths, the algorithm can be simplified to just relaxing all edges in ascending order
    // from s to all other vertices and then relaxing all edges again, in descending order from s to all other vertices
public class Exercise35_BitonicShortestPath {

    public class Path implements Comparable<Path> {

        private Path previousPath;
        private DirectedEdge directedEdge;
        private double weight;
        private boolean isDescending;
        private int numberOfEdges;
        private HashSet<Integer> verticesInPath;

        Path(DirectedEdge directedEdge) {
            this.directedEdge = directedEdge;
            weight = directedEdge.weight();
            verticesInPath = new HashSet<>();
            verticesInPath.add(directedEdge.from());
            verticesInPath.add(directedEdge.to());

            numberOfEdges = 1;
        }

        Path(Path previousPath, DirectedEdge directedEdge) {
            this(directedEdge);
            this.previousPath = previousPath;

            weight += previousPath.weight();
            numberOfEdges += previousPath.numberOfEdges;

            verticesInPath = new HashSet<>(previousPath.verticesInPath);
            verticesInPath.add(directedEdge.to());

            if (previousPath != null && previousPath.directedEdge.weight() > directedEdge.weight()) {
                isDescending = true;
            }
        }

        public boolean containsVertex(int vertex) {
            return verticesInPath.contains(vertex);
        }

        public double weight() {
            return weight;
        }

        public boolean isDescending() {
            return isDescending;
        }

        public int numberOfEdges() {
            return numberOfEdges;
        }

        public Iterable<DirectedEdge> getPath() {
            LinkedList<DirectedEdge> path = new LinkedList<>();

            Path iterator = previousPath;

            while (iterator != null && iterator.directedEdge != null) {
                path.addFirst(iterator.directedEdge);

                iterator = iterator.previousPath;
            }
            path.add(directedEdge);

            return path;
        }

        @Override
        public int compareTo(Path other) {
            if (this.weight < other.weight) {
                return -1;
            } else if (this.weight > other.weight) {
                return 1;
            } else {
                return 0;
            }
        }
    }

    public class VertexInformation {

        private DirectedEdge[] edges;
        private int edgeIteratorPosition;

        VertexInformation(DirectedEdge[] edges) {
            this.edges = edges;
            edgeIteratorPosition = 0;
        }

        public void incrementEdgeIteratorPosition() {
            edgeIteratorPosition++;
        }

        public DirectedEdge[] getEdges() {
            return edges;
        }

        public int getEdgeIteratorPosition() {
            return edgeIteratorPosition;
        }
    }

    public class BitonicSP {

        private Path[] bitonicPathTo;  // bitonic path to vertex

        // O(P lg P), where P is the number of paths in the digraph
        // Includes optimization to prune paths that are not bitonic, ie. ascending + descending + ascending
        // or descending + ascending
        public BitonicSP(EdgeWeightedDigraph edgeWeightedDigraph, int source) {

            bitonicPathTo = new Path[edgeWeightedDigraph.vertices()];

            // 1- Relax edges in ascending order to get a monotonic increasing shortest path
            Comparator<DirectedEdge> edgesComparator = new Comparator<DirectedEdge>() {
                @Override
                public int compare(DirectedEdge edge1, DirectedEdge edge2) {
                    if (edge1.weight() > edge2.weight()) {
                        return -1;
                    } else if (edge1.weight() < edge2.weight()) {
                        return 1;
                    } else {
                        return 0;
                    }
                }
            };

            List<Path> allCurrentPaths = new ArrayList<>();

            relaxAllEdgesInSpecificOrder(edgeWeightedDigraph, source, edgesComparator, allCurrentPaths,true);

            // 2- Relax edges in descending order to get a monotonic decreasing shortest path
            edgesComparator = new Comparator<DirectedEdge>() {
                @Override
                public int compare(DirectedEdge edge1, DirectedEdge edge2) {
                    if (edge1.weight() < edge2.weight()) {
                        return -1;
                    } else if (edge1.weight() > edge2.weight()) {
                        return 1;
                    } else {
                        return 0;
                    }
                }
            };

            relaxAllEdgesInSpecificOrder(edgeWeightedDigraph, source, edgesComparator, allCurrentPaths, false);
        }

        private void relaxAllEdgesInSpecificOrder(EdgeWeightedDigraph edgeWeightedDigraph, int source,
                                                  Comparator<DirectedEdge> edgesComparator, List<Path> allCurrentPaths,
                                                  boolean isAscendingOrder) {

            // Create a map with vertices as keys and sorted outgoing edges as values
            SeparateChainingHashTable<Integer, VertexInformation> verticesInformation = new SeparateChainingHashTable<>();
            for (int vertex = 0; vertex < edgeWeightedDigraph.vertices(); vertex++) {
                DirectedEdge[] edges = new DirectedEdge[edgeWeightedDigraph.outdegree(vertex)];

                int edgeIndex = 0;
                for (DirectedEdge edge : edgeWeightedDigraph.adjacent(vertex)) {
                    edges[edgeIndex++] = edge;
                }

                Arrays.sort(edges, edgesComparator);

                verticesInformation.put(vertex, new VertexInformation(edges));
            }

            PriorityQueueResize<Path> priorityQueue = new PriorityQueueResize<>(PriorityQueueResize.Orientation.MIN);

            // If we are relaxing edges for the first time, add the initial paths to the priority queue
            if (isAscendingOrder) {
                VertexInformation sourceVertexInformation = verticesInformation.get(source);
                while (sourceVertexInformation.getEdgeIteratorPosition() < sourceVertexInformation.getEdges().length) {
                    DirectedEdge edge = sourceVertexInformation.getEdges()[sourceVertexInformation.getEdgeIteratorPosition()];
                    sourceVertexInformation.incrementEdgeIteratorPosition();

                    Path path = new Path(edge);
                    priorityQueue.insert(path);

                    allCurrentPaths.add(path);
                }
            }

            // If we are relaxing edges for the second time, add all existing ascending paths to the priority queue
            if (!allCurrentPaths.isEmpty()) {
                for (Path currentPath : allCurrentPaths) {
                    priorityQueue.insert(currentPath);
                }
            }

            while (!priorityQueue.isEmpty()) {
                Path currentShortestPath = priorityQueue.deleteTop();

                DirectedEdge currentEdge = currentShortestPath.directedEdge;

                int nextVertexInPath = currentEdge.to();
                VertexInformation nextVertexInformation = verticesInformation.get(nextVertexInPath);

                // Edge case: a bitonic path consisting of 2 edges of the same weight.
                // s to v with only one edge is strictly increasing, v to t with only one edge is strictly decreasing
                boolean isEdgeCase = false;

                if (currentShortestPath.numberOfEdges() == 2
                        && currentEdge.weight() == currentShortestPath.previousPath.directedEdge.weight()) {
                    isEdgeCase = true;
                }

                if ((currentShortestPath.isDescending() || isEdgeCase)
                        && (currentShortestPath.weight() < bitonicPathDistTo(nextVertexInPath)
                        || bitonicPathTo[nextVertexInPath] == null)) {
                    bitonicPathTo[nextVertexInPath] = currentShortestPath;
                }

                double weightInPreviousEdge = currentEdge.weight();

                while (nextVertexInformation.getEdgeIteratorPosition() < nextVertexInformation.getEdges().length) {
                    DirectedEdge edge =
                            verticesInformation.get(nextVertexInPath).getEdges()[nextVertexInformation.getEdgeIteratorPosition()];

                    boolean isEdgeInEdgeCase = currentShortestPath.numberOfEdges() == 1
                            && edge.weight() == weightInPreviousEdge;

                    if (!isEdgeInEdgeCase && ((isAscendingOrder && edge.weight() <= weightInPreviousEdge)
                            || (!isAscendingOrder && edge.weight() >= weightInPreviousEdge))) {
                        break;
                    }

                    nextVertexInformation.incrementEdgeIteratorPosition();

                    if (currentShortestPath.containsVertex(edge.to())) {
                        continue;
                    }

                    Path path = new Path(currentShortestPath, edge);
                    priorityQueue.insert(path);

                    // If we are relaxing edges for the first time, store the ascending paths so they can be further
                    // relaxed when computing the descending paths on the second relaxation
                    if (isAscendingOrder) {
                        allCurrentPaths.add(path);
                    }
                }
            }
        }

        public double bitonicPathDistTo(int vertex) {
            if (hasBitonicPathTo(vertex)) {
                return bitonicPathTo[vertex].weight();
            } else {
                return Double.POSITIVE_INFINITY;
            }
        }

        public boolean hasBitonicPathTo(int vertex) {
            return bitonicPathTo[vertex] != null;
        }

        public Iterable<DirectedEdge> bitonicPathTo(int vertex) {
            if (!hasBitonicPathTo(vertex)) {
                return null;
            }

            return bitonicPathTo[vertex].getPath();
        }
    }

    public static void main(String[] args) {
        EdgeWeightedDigraph edgeWeightedDigraph = new EdgeWeightedDigraph(13);
        edgeWeightedDigraph.addEdge(new DirectedEdge(0, 1, 4));
        edgeWeightedDigraph.addEdge(new DirectedEdge(1, 2, 5));
        edgeWeightedDigraph.addEdge(new DirectedEdge(2, 3, 4));
        edgeWeightedDigraph.addEdge(new DirectedEdge(3, 4, 1));
        edgeWeightedDigraph.addEdge(new DirectedEdge(1, 5, 5));
        edgeWeightedDigraph.addEdge(new DirectedEdge(5, 6, 3));
        edgeWeightedDigraph.addEdge(new DirectedEdge(6, 7, 8));
        edgeWeightedDigraph.addEdge(new DirectedEdge(0, 8, 2));
        edgeWeightedDigraph.addEdge(new DirectedEdge(8, 9, 1));
        edgeWeightedDigraph.addEdge(new DirectedEdge(1, 2, 4));

        // With the following 3 edges, there is now a shortest monotonic ascending path from 0 to 3:
        // 0->1 (1.0) 1->2 (2.0) 2->3 (3.0)
        // but it is not bitonic, so it should not be selected
        edgeWeightedDigraph.addEdge(new DirectedEdge(0, 1, 1));
        edgeWeightedDigraph.addEdge(new DirectedEdge(1, 2, 2));
        edgeWeightedDigraph.addEdge(new DirectedEdge(2, 3, 3));

        // Edge case: a bitonic path consisting of 2 edges of the same weight
        // 0->10 (3.0) 10->11 (3.0)
        // Should be in the final solution
        edgeWeightedDigraph.addEdge(new DirectedEdge(0, 10, 3));
        edgeWeightedDigraph.addEdge(new DirectedEdge(10, 11, 3));

        // Not an edge case: 3 edges of the same weight in the path
        // 0->10 (3.0) 10->11 (3.0) 11->12 (3.0)
        // Should not be in the final solution
        edgeWeightedDigraph.addEdge(new DirectedEdge(11, 12, 3));

        Exercise35_BitonicShortestPath.BitonicSP bitonicSP =
                new Exercise35_BitonicShortestPath().new BitonicSP(edgeWeightedDigraph, 0);

        StdOut.print("Bitonic shortest paths: ");

        for (int vertex = 0; vertex < edgeWeightedDigraph.vertices(); vertex++) {
            StdOut.print("\nPath from vertex 0 to vertex " + vertex + ": ");

            if (bitonicSP.hasBitonicPathTo(vertex)) {
                for (DirectedEdge edge : bitonicSP.bitonicPathTo(vertex)) {
                    StdOut.print(edge.from() + "->" + edge.to() + " (" + edge.weight() + ") ");
                }
            } else {
                StdOut.print("There is no bitonic path to vertex " + vertex);
            }
        }

        StdOut.println("\n\nExpected bitonic paths");
        StdOut.println("Vertex 0: There is no bitonic path to vertex 0"); // There is a path but it is not bitonic
        StdOut.println("Vertex 1: There is no bitonic path to vertex 1"); // There is a path but it is not bitonic
        StdOut.println("Vertex 2: 0->1 (4.0) 1->2 (2.0)");
        StdOut.println("Vertex 3: 0->1 (1.0) 1->2 (4.0) 2->3 (3.0)");
        StdOut.println("Vertex 4: 0->1 (1.0) 1->2 (2.0) 2->3 (3.0) 3->4 (1.0)");
        StdOut.println("Vertex 5: There is no bitonic path to vertex 5"); // There is a path but it is not bitonic
        StdOut.println("Vertex 6: 0->1 (1.0) 1->5 (5.0) 5->6 (3.0)");
        StdOut.println("Vertex 7: There is no bitonic path to vertex 7"); // There is a path but it is not bitonic
        StdOut.println("Vertex 8: There is no bitonic path to vertex 8"); // There is a path but it is not bitonic
        StdOut.println("Vertex 9: 0->8 (2.0) 8->9 (1.0)");
        StdOut.println("Vertex 10: There is no bitonic path to vertex 10"); // There is a path but it is not bitonic
        StdOut.println("Vertex 11: 0->10 (3.0) 10->11 (3.0)"); // An edge case
        StdOut.println("Vertex 12: There is no bitonic path to vertex 12"); // There is a path but it is not bitonic

        double[] expectedDistances = {
                Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, 6, 8, 7, Double.POSITIVE_INFINITY,
                9, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, 3, Double.POSITIVE_INFINITY,
                6, Double.POSITIVE_INFINITY
        };

        for (int vertex = 0; vertex < edgeWeightedDigraph.vertices(); vertex++) {
            StdOut.print("\nDistance to vertex " + vertex + ": " + bitonicSP.bitonicPathDistTo(vertex)
                    + " Expected: " + expectedDistances[vertex]);
        }
    }
}


Question:
Give a trace for LSD string sort for the keys
 no is th ti fo al go pe to co to th ai of th pa

Answer:
5.1.2

Trace for LSD string sort (same model as used in the book):

input   d=1   d=0   output
no      pa    ai    ai
is      pe    al    al
th      of    co    co
ti      th    fo    fo
fo      th    go    go
al      th    is    is
go      ti    no    no
pe      ai    of    of
to      al    pa    pa
co      no    pe    pe
to      fo    th    th
th      go    th    th
ai      to    th    th
of      co    ti    ti
th      to    to    to
pa      is    to    to


Question:
Give a trace for MSD string sort for the keys
 no is th ti fo al go pe to co to th ai of th pa

Answer:
5.1.3

According to ASC II values, indices of chars 'a' through 'z' start at 97.
But this trace indices follow the book convention of 'a' starting at 0.

Trace for MSD string sort (same model as used in the book):

Top level of sort(array, 0, 15, 0):

input
0  no
1  is
2  th
3  ti
4  fo
5  al
6  go
7  pe
8  to
9  co
10 to
11 th
12 ai
13 of
14 th
15 pa

d=0
Count frequencies
0    0
1    0
2  a 2
3  b 0
4  c 1
5  d 0
6  e 0
7  f 1
8  g 1
9  h 0
10 i 1
11 j 0
12 k 0
13 l 0
14 m 0
15 n 1
16 o 1
17 p 2
18 q 0
19 r 0
20 s 0
21 t 6
22 u 0
23 v 0
24 x 0
25 w 0
26 y 0
27 z 0

Transform counts to indices
0    0
1    0
2  a 2
3  b 2
4  c 3
5  d 3
6  e 3
7  f 4
8  g 5
9  h 5
10 i 6
11 j 6
12 k 6
13 l 6
14 m 6
15 n 7
16 o 8
17 p 10
18 q 10
19 r 10
20 s 10
21 t 16
22 u 16
23 v 16
24 x 16
25 w 16
26 y 16
27 z 16

Distribute and copy back
0  al
1  ai
2  co
3  fo
4  go
5  is
6  no
7  of
8  pe
9  pa
10 th
11 ti
12 to
13 to
14 th
15 th

Indices at completion of distribute phase
0    0
1    2
2  a 2
3  b 3
4  c 3
5  d 3
6  e 4
7  f 5
8  g 5
9  h 6
10 i 6
11 j 6
12 k 6
13 l 6
14 m 7
15 n 8
16 o 10
17 p 10
18 q 10
19 r 10
20 s 16
21 t 16
22 u 16
23 v 16
24 x 16
25 w 16
26 y 16
27 z 16

Recursively sort subarrays
sort(a, 0, 1, 1);
sort(a, 2, 1, 1);
sort(a, 2, 2, 1);
sort(a, 3, 2, 1);
sort(a, 3, 2, 1);
sort(a, 3, 3, 1);
sort(a, 4, 4, 1);
sort(a, 5, 4, 1);
sort(a, 5, 5, 1);
sort(a, 6, 5, 1);
sort(a, 6, 5, 1);
sort(a, 6, 5, 1);
sort(a, 6, 5, 1);
sort(a, 6, 6, 1);
sort(a, 7, 7, 1);
sort(a, 8, 9, 1);
sort(a, 10, 9, 1);
sort(a, 10, 9, 1);
sort(a, 10, 9, 1);
sort(a, 10, 15, 1);
sort(a, 16, 15, 1);
sort(a, 16, 15, 1);
sort(a, 16, 15, 1);
sort(a, 16, 15, 1);
sort(a, 16, 15, 1);
sort(a, 16, 15, 1);
sort(a, 16, 15, 1);

Sorted result
0  ai
1  al
2  co
3  fo
4  go
5  is
6  no
7  of
8  pa
9  pe
10 th
11 th
12 th
13 ti
14 to
15 to

Trace of recursive calls for MSD string sort (no cutoff for small subarrays, subarrays of size 0 and 1 omitted)

input   __      __                      output
no      al      ai      ai      ai      ai
is      ai      al      al      al      al
th      co      --      co      co      co
ti      fo      co      fo      fo      fo
fo      go      fo      go      go      go
al      is      go      is      is      is
go      no      is      no      no      no
pe      of      no      of      of      of
to      pe      of      --      pa      pa
co      pa      pe      pa      pe      pe
to      th      pa      pe      --      th
th      ti      th      --      th      th
ai      to      ti      th      th      th
of      to      to      ti      th      ti
th      th      to      to      ti      to
pa      th      th      to      to      to
        --      th      th      to
                        th      --


Question:
Give a trace for 3-way string quicksort for the keys
 no is th ti fo al go pe to co to th ai of th pa

Answer:
5.1.4

Trace for 3-way string quicksort (same model as used in the book):

                   --      --      --
0  no      is      ai      ai      ai      ai
1  is      ai      co      al      --      al
2  th      co      fo      --      al      co
3  ti      fo      al      fo      --      fo
4  fo      al      go      go      co      go
5  al      go      --      co      --      is
6  go      --      is      --      fo      no
7  pe      no      --      is      --      of
8  to      --      no      --      go      pa
9  co      to      --      no      --      pe
10 to      pe      pe      --      is      th
11 th      to      of      of      --      th
12 ai      th      pa      --      no      th
13 of      ti      --      pe      --      ti
14 th      of      th      pa      of      to
15 pa      th      ti      --      --      to
           pa      to      th      pa
           th      th      th      --
                   to      th      pe
                   th      --      --
                   --      to      th
                           to      th
                           ti      th
                                   --
                                   ti
                                   --
                                   to
                                   to
                                   --


Question:
Give a trace for MSD string sort for the keys
 now is the time for all good people to come to the aid of

Answer:
5.1.5

According to ASC II values, indices of chars 'a' through 'z' start at 97.
But this trace indices follow the book convention of 'a' starting at 0.

Trace for MSD string sort (same model as used in the book):

Top level of sort(array, 0, 13, 0):

input
0  now
1  is
2  the
3  time
4  for
5  all
6  good
7  people
8  to
9  come
10 to
11 the
12 aid
13 of

d=0
Count frequencies
0    0
1    0
2  a 2
3  b 0
4  c 1
5  d 0
6  e 0
7  f 1
8  g 1
9  h 0
10 i 1
11 j 0
12 k 0
13 l 0
14 m 0
15 n 1
16 o 1
17 p 1
18 q 0
19 r 0
20 s 0
21 t 5
22 u 0
23 v 0
24 x 0
25 w 0
26 y 0
27 z 0

Transform counts to indices
0    0
1    0
2  a 2
3  b 2
4  c 3
5  d 3
6  e 3
7  f 4
8  g 5
9  h 5
10 i 6
11 j 6
12 k 6
13 l 6
14 m 6
15 n 7
16 o 8
17 p 9
18 q 9
19 r 9
20 s 9
21 t 14
22 u 14
23 v 14
24 x 14
25 w 14
26 y 14
27 z 14

Distribute and copy back
0  all
1  aid
2  come
3  for
4  good
5  is
6  now
7  of
8  people
9  the
10 time
11 to
12 to
13 the

Indices at completion of distribute phase
0    0
1    2
2  a 2
3  b 3
4  c 3
5  d 3
6  e 4
7  f 5
8  g 5
9  h 6
10 i 6
11 j 6
12 k 6
13 l 6
14 m 7
15 n 8
16 o 9
17 p 9
18 q 9
19 r 9
20 s 14
21 t 14
22 u 14
23 v 14
24 x 14
25 w 14
26 y 14
27 z 14

Recursively sort subarrays
sort(a, 0, 1, 1);
sort(a, 2, 1, 1);
sort(a, 2, 2, 1);
sort(a, 3, 2, 1);
sort(a, 3, 2, 1);
sort(a, 3, 3, 1);
sort(a, 4, 4, 1);
sort(a, 5, 4, 1);
sort(a, 5, 5, 1);
sort(a, 6, 5, 1);
sort(a, 6, 5, 1);
sort(a, 6, 5, 1);
sort(a, 6, 5, 1);
sort(a, 6, 6, 1);
sort(a, 7, 7, 1);
sort(a, 8, 8, 1);
sort(a, 9, 8, 1);
sort(a, 9, 8, 1);
sort(a, 9, 8, 1);
sort(a, 9, 13, 1);
sort(a, 13, 14, 1);
sort(a, 13, 14, 1);
sort(a, 13, 14, 1);
sort(a, 13, 14, 1);
sort(a, 13, 14, 1);
sort(a, 13, 14, 1);
sort(a, 13, 14, 1);

Sorted result
0  aid
1  all
2  come
3  for
4  good
5  is
6  now
7  of
8  people
9  the
10 the
11 time
12 to
13 to

Trace of recursive calls for MSD string sort (no cutoff for small subarrays, subarrays of size 0 and 1 omitted)

input       ____        ___                                 output
now         all         aid         aid         aid         aid
is          aid         all         all         all         all
the         come        ---         come        come        come
time        for         come        for         for         for
for         good        for         good        good        good
all         is          good        is          is          is
good        now         is          now         now         now
people      of          now         of          of          of
to          people      of          people      people      people
come        the         people      ---         ---         the
to          time        the         the         the         the
the         to          time        the         the         time
aid         to          to          time        ---         to
of          the         to          to          time        to
            ----        the         to          to
                                    ---         to


Question:
Give a trace for 3-way string quicksort for the keys
 now is the time for all good people to come to the aid of

Answer:
5.1.6

Trace for 3-way string quicksort (same model as used in the book):

                    ----     ---      ---      ---
0  now      is      aid      aid      aid      aid      aid
1  is       aid     come     all      ---      ---      all
2  the      come    for      ---      all      all      come
3  time     for     all      for      ---      ---      for
4  for      all     good     good     come     come     good
5  all      good    ----     come     ----     ----     is
6  good     ---     is       ----     for      for      now
7  people   now     ---      is       ----     ----     of
8  to       ---     now      ---      good     good     people
9  come     to      ---      now      ----     ----     the
10 to       people  people   ---      is       is       the
11 the      to      of       of       ---      ---      time
12 aid      the     ---      ------   now      now      to
13 of       time    to       people   ---      ---      to
            of      the      ------   of       of
            the     time     the      ------   ------
                    to       time     people   people
                    the      the      ------   ------
                    ---      ----     the      the
                             to       the      the
                             to       ----     ----
                             ----     time     time
                                      ----     ----
                                      to       to
                                      to       to
                                      --       --


Question: Give the number of characters examined by MSD string sort and 3-way string quicksort for a file of N keys a, aa, aaa, aaaa, aaaaa, . . .

Answer:
5.1.8

Both MSD string sort and 3-way string quicksort examine all characters in the N keys.
That number is equal to 1 + 2 + ... + N = (N^2 + N) / 2 characters.
MSD string sort, however, generates (R - 1) * N empty subarrays (an empty subarray for all digits in R other than 'a', in every pass) while 3-way string quicksort generates 2N empty subarrays (empty subarrays for digits smaller than 'a' and for digits higher than 'a', or empty subarrays for digits smaller than '-1' and for digits equal to '-1', in every pass).

MSD string sort trace (no cutoff for small subarrays, subarrays of size 0 and 1 omitted):

input   ----
a       a       a       a       a       a       a
aa      aa      ----    aa      aa      aa      aa
aaa     aaa     aa      ----    aaa     aaa     aaa
aaaa    aaaa    aaa     aaa     ----    aaaa    aaaa
...     ...     aaaa    aaaa    aaaa    ----    ...
        ----    ...     ...     ...     ...
                ----    ----    ----    ----

3-way string quicksort trace:

input   ----
a       a       a       a       a       a       a
aa      aa      ----    aa      aa      aa      aa
aaa     aaa     aa      ----    aaa     aaa     aaa
aaaa    aaaa    aaa     aaa     ----    aaaa    aaaa
...     ...     aaaa    aaaa    aaaa    ----    ...
        ----    ...     ...     ...     ...
                ----    ----    ----    ----


Question: What is the total number of characters examined by 3-way string quicksort when sorting N fixed-length strings (all of length W), in the worst case?

Answer:
5.1.10

The total number of characters examined by 3-way string quicksort when sorting N fixed-length strings (all of length W) in the worst case is O(N * W * R).

This can be seen with a recurrence relation T(W).

The base case T(1) is when all the strings have length 1.
An example with R = 3 is { "a", "b", "c" }.
In the worst case they are in reverse order.
For example: { "c", "b", "a" }.
In this case we only remove one string from the list in each pass.
If we consider N = R^W (in this case, W = 1), the number of comparisons is equal to:
Characters examined = Sum[i=0..R] i
Characters examined = R * (R + 1) / 2

To build the worst case for strings of length 2 (T(2)), we take each string from T(1) and append it to the end of each character in R.
So for single character strings "a", "b", "c", with R = 3, the two character list is: "aa", "ab", "ac", "ba", "bb", "bc", "ca", "cb", "cc".
The list can then be split into R groups: one for each character in R that is a prefix to every string of length W - 1.
During the partitioning phase all strings that start with "a" will be in the same partition and the algorithm will do the same process as in T(1) because removing the first character 'a' will lead to the same 1-length strings { "c", "b", "a" } as before.
The same thing happens for strings starting with "b" and "c".
So, for R = 3, the algorithm will check 3 * R + 2 * R + R characters in the first position of the strings (which is 3 + 2 + 1 characters times R groups).
Then it will check the second characters in the strings in each of the R groups.

For T(W), where W > 2, the list will then again be split into R groups: one for each character in R that is a prefix to every string of length W - 2.
Quicksort will then remove R strings from the list in each partition.
It will then check R * T(W - 1) more characters for each of those groups.
This gives the recurrence T(W) = (R^(W - 1) * Sum[i=0..R] R - i) + R * T(W - 1), which simplifies to:
T(W) = R^(W + 1) + R^W  + R * T(W - 1)
      -----------------
              2
Solving the recurrence gives us:
T(W) = W * (R^W) * (R + 1)
      ---------------------
                2
Substituting N = R^W:
T(W) = W * N * (R + 1)
      -----------------
              2
Which is O(N * W * R).

Thanks to dragon-dreamer (https://github.com/dragon-dreamer) for finding a more accurate worst case.
https://github.com/reneargento/algorithms-sedgewick-wayne/issues/153
Thanks to GenevaS (https://github.com/GenevaS) for finding a more accurate worst case.
https://github.com/reneargento/algorithms-sedgewick-wayne/issues/245


Question: Hybrid sort. Investigate the idea of using standard MSD string sort for large arrays, in order to get the advantage of multiway partitioning, and 3-way string quicksort for smaller arrays, in order to avoid the negative effects of large numbers of empty bins.

Answer:
5.1.13 - Hybrid sort

Idea: using standard MSD string sort for large arrays, in order to get the advantage of multiway partitioning, and 3-way string quicksort for smaller arrays, in order to avoid the negative effects of large numbers of empty bins.

This idea will work well for random strings because, in general, the higher the number of keys to be sorted, the higher the number of non-empty subarrays generated on each pass of MSD string sort. Such scenario would work well due to the advantage of having multiway partitioning.
However, MSD string sort will still generate a large number of empty subarrays if there is a large number of equal keys (or a large number of keys with long common prefixes).

3-way string quicksort will avoid the negative effects of large numbers of empty bins not only for smaller arrays, but also for large arrays, while also having the benefit of using less space than MSD string sort since it does not require space for frequency counts or for an auxiliary array. On the other hand, it envolves more data movement than MSD string sort when the number of nonempty subarrays is large because it has to do a series of 3-way partitions to get the effect of the multiway partition. This would not be a problem in the hybrid sort if there were many equal keys in smaller arrays, since 3-way string quicksort would be the algorithm of choice in such situation.

Overall, hybrid sort would be a good choice for random strings. However, a version of hybrid sort that chooses between MSD string sort and 3-way string quicksort based on the percentage of equal keys (choosing MSD string sort if there is a low percentage of equal keys and choosing 3-way string quicksort if there is a high number of equal keys) would be more effective than a version that makes the choice based on the number of keys.


Question: In-place key-indexed counting. Develop a version of key-indexed counting that uses only a constant amount of extra space. Prove that your version is stable or provide a counterexample.

Answer:
// Exercise17_InPlaceKeyIndexedCounting.java
package chapter5.section1;

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

import java.util.StringJoiner;

/**
 * Created by Rene Argento on 13/01/18.
 */
public class Exercise17_InPlaceKeyIndexedCounting {

    private class Element implements Comparable<Element> {
        String value;
        int originalIndex;

        Element(String value, int originalIndex) {
            this.value = value;
            this.originalIndex = originalIndex;
        }

        @Override
        public int compareTo(Element other) {
            return this.value.compareTo(other.value);
        }
    }

    public class LeastSignificantDigitInPlace {

        public void lsdSort(Element[] array, int stringsLength) {

            int alphabetSize = 256; // Extended ASCII characters

            for (int digit = stringsLength - 1; digit >= 0; digit--) {
                // Sort by key-indexed counting on digitTh char

                // Compute frequency counts
                int count[] = new int[alphabetSize + 1];
                int[] startIndex = new int[alphabetSize + 1];

                for (int i = 0; i < array.length; i++) {
                    int digitIndex = array[i].value.charAt(digit);
                    count[digitIndex + 1]++;
                    startIndex[digitIndex + 1]++;
                }

                // Transform counts to indices
                for (int r = 0; r < alphabetSize; r++) {
                    count[r + 1] += count[r];
                    startIndex[r + 1] += startIndex[r];
                }

                // Distribute
                for (int i = 0; i < array.length; i++) {

                    // Continue placing items in the correct place until array[i] is in the correct place
                    while (true) {
                        int digitIndex = array[i].value.charAt(digit);

                        // Do not move items that are already in the correct place
                        if (startIndex[digitIndex] <= i && i < count[digitIndex]) {
                            break;
                        }

                        int newIndex = count[digitIndex]++;
                        ArrayUtil.exchange(array, i, newIndex);
                    }
                }
            }
        }
    }

    public class MostSignificantDigitInPlace {
        private int alphabetSize = 256; // Extended ASCII characters; radix
        private final int CUTOFF_FOR_SMALL_SUBARRAYS = 15;

        public void msdSort(Element[] array) {
            sort(array, 0, array.length - 1, 0);
        }

        private void sort(Element[] array, int low, int high, int digit) {
            // Do not use Insertion sort in this case to prove that the sort is not stable

            // Sort from array[low] to array[high], starting at the digitTh character
//            if (low + CUTOFF_FOR_SMALL_SUBARRAYS >= high) {
//                InsertionSort insertionSort = new InsertionSort();
//                insertionSort.sort(array, low, high, digit);
//                return;
//            }

            if (low > high) {
                return;
            }

            // Compute frequency counts
            int[] count = new int[alphabetSize + 2];
            int[] startIndex = new int[alphabetSize + 2];

            for (int i = low; i <= high; i++) {
                int digitIndex = charAt(array[i].value, digit) + 2;
                count[digitIndex]++;
                startIndex[digitIndex]++;
            }

            // Transform counts to indices
            for (int r = 0; r < alphabetSize + 1; r++) {
                count[r + 1] += count[r];
                startIndex[r + 1] += startIndex[r];
            }

            // Distribute
            for (int i = low; i <= high; i++) {

                // Continue placing items in the correct place until array[i] is in the correct place
                while (true) {
                    int digitIndex = charAt(array[i].value, digit) + 1;

                    // Do not move items that are already in the correct place
                    if (startIndex[digitIndex] + low <= i && i < count[digitIndex] + low) {
                        break;
                    }

                    int newIndex = count[digitIndex]++;
                    ArrayUtil.exchange(array, i, newIndex + low);
                }
            }

            // Recursively sort for each character value
            for (int r = 0; r < alphabetSize; r++) {
                sort(array, low + count[r], low + count[r + 1] - 1,digit + 1);
            }
        }

        private int charAt(String string, int digit) {
            if (digit < string.length()) {
                return string.charAt(digit);
            } else {
                return -1;
            }
        }

        // Insertion sort for Strings whose first digit characters are equal
        public class InsertionSort {

            public void sort(Element[] array, int low, int high, int digit) {
                // Sort from array[low] to array[high], starting at the digitTh character
                for (int i = low; i <= high; i++) {
                    for (int j = i; j > low && less(array[j].value, array[j - 1].value, digit); j--) {
                        ArrayUtil.exchange(array, j, j - 1);
                    }
                }
            }

            private boolean less(String string1, String string2, int digit) {
                for (int i = digit; i < Math.min(string1.length(), string2.length()); i++) {
                    if (string1.charAt(i) < string2.charAt(i)) {
                        return true;
                    } else if (string1.charAt(i) > string2.charAt(i)) {
                        return false;
                    }
                }
                return string1.length() < string2.length();
            }
        }
    }

    public static void main(String[] args) {
        Exercise17_InPlaceKeyIndexedCounting inPlaceKeyIndexedCounting = new Exercise17_InPlaceKeyIndexedCounting();

        StdOut.println("In-place LSD tests\n");
        LeastSignificantDigitInPlace leastSignificantDigitInplace =
                inPlaceKeyIndexedCounting.new LeastSignificantDigitInPlace();

        Element[] array1 = new Element[13];
        array1[0] = inPlaceKeyIndexedCounting.new Element("4PGC938", 0);
        array1[1] = inPlaceKeyIndexedCounting.new Element("2IYE230", 1);
        array1[2] = inPlaceKeyIndexedCounting.new Element("3CIO720", 2);
        array1[3] = inPlaceKeyIndexedCounting.new Element("1ICK750", 3);
        array1[4] = inPlaceKeyIndexedCounting.new Element("1OHV845", 4);
        array1[5] = inPlaceKeyIndexedCounting.new Element("4JZY524", 5);
        array1[6] = inPlaceKeyIndexedCounting.new Element("1ICK750", 6);
        array1[7] = inPlaceKeyIndexedCounting.new Element("3CIO720", 7);
        array1[8] = inPlaceKeyIndexedCounting.new Element("1OHV845", 8);
        array1[9] = inPlaceKeyIndexedCounting.new Element("1OHV845", 9);
        array1[10] = inPlaceKeyIndexedCounting.new Element("2RLA629", 10);
        array1[11] = inPlaceKeyIndexedCounting.new Element("2RLA629", 11);
        array1[12] = inPlaceKeyIndexedCounting.new Element("3ATW723", 12);

        int stringsLength1 = 7;
        leastSignificantDigitInplace.lsdSort(array1, stringsLength1);

        StringJoiner sortedArray1 = new StringJoiner(" ");

        for (Element element : array1) {
            sortedArray1.add(element.value);
        }
        StdOut.println("Sorted array 1 with lost of stability");
        StdOut.println(sortedArray1);
        StdOut.println("Expected if there was no lost of stability: \n" +
                "1ICK750 1ICK750 1OHV845 1OHV845 1OHV845 2IYE230 2RLA629 2RLA629 3ATW723 3CIO720 3CIO720 " +
                "4JZY524 4PGC938");

        Element[] array2 = new Element[3];
        array2[0] = inPlaceKeyIndexedCounting.new Element("CAA", 0);
        array2[1] = inPlaceKeyIndexedCounting.new Element("ABB", 1);
        array2[2] = inPlaceKeyIndexedCounting.new Element("ABB", 2);

        int stringsLength2 = 3;
        leastSignificantDigitInplace.lsdSort(array2, stringsLength2);

        StringJoiner sortedArray2 = new StringJoiner("\n");

        for (Element element : array2) {
            sortedArray2.add("Element: " + element.value + " Original index: " + element.originalIndex);
        }
        StdOut.println("\nSorted array 2 with lost of stability");
        StdOut.println(sortedArray2);

        leastSignificantDigitInplace.lsdSort(array2, 3);

        StdOut.println("\nIn-place MSD tests\n");

        MostSignificantDigitInPlace mostSignificantDigitInplace =
                inPlaceKeyIndexedCounting.new MostSignificantDigitInPlace();

        Element[] array3 = new Element[18];
        array3[0] = inPlaceKeyIndexedCounting.new Element("Rene", 0);
        array3[1] = inPlaceKeyIndexedCounting.new Element("Argento", 1);
        array3[2] = inPlaceKeyIndexedCounting.new Element("Arg", 2);
        array3[3] = inPlaceKeyIndexedCounting.new Element("Alg", 3);
        array3[4] = inPlaceKeyIndexedCounting.new Element("Algorithms", 4);
        array3[5] = inPlaceKeyIndexedCounting.new Element("LSD", 5);
        array3[6] = inPlaceKeyIndexedCounting.new Element("MSD", 6);
        array3[7] = inPlaceKeyIndexedCounting.new Element("3WayStringQuickSort", 7);
        array3[8] = inPlaceKeyIndexedCounting.new Element("Dijkstra", 8);
        array3[9] = inPlaceKeyIndexedCounting.new Element("Floyd", 9);
        array3[10] = inPlaceKeyIndexedCounting.new Element("Warshall", 10);
        array3[11] = inPlaceKeyIndexedCounting.new Element("Johnson", 11);
        array3[12] = inPlaceKeyIndexedCounting.new Element("Sedgewick", 12);
        array3[13] = inPlaceKeyIndexedCounting.new Element("Wayne", 13);
        array3[14] = inPlaceKeyIndexedCounting.new Element("Bellman", 14);
        array3[15] = inPlaceKeyIndexedCounting.new Element("Ford", 15);
        array3[16] = inPlaceKeyIndexedCounting.new Element("BFS", 16);
        array3[17] = inPlaceKeyIndexedCounting.new Element("DFS", 17);

        mostSignificantDigitInplace.msdSort(array3);

        StringJoiner sortedArray3 = new StringJoiner(" ");

        for (Element element : array3) {
            sortedArray3.add(element.value);
        }
        StdOut.println("Sorted array 3");
        StdOut.println(sortedArray3);
        StdOut.println("Expected: \n3WayStringQuickSort Alg Algorithms Arg Argento BFS Bellman DFS Dijkstra Floyd Ford " +
                "Johnson LSD MSD Rene Sedgewick Warshall Wayne\n");

        Element[] array4 = new Element[3];
        array4[0] = inPlaceKeyIndexedCounting.new Element("CAA", 0);
        array4[1] = inPlaceKeyIndexedCounting.new Element("ABB", 1);
        array4[2] = inPlaceKeyIndexedCounting.new Element("ABB", 2);

        mostSignificantDigitInplace.msdSort(array4);

        StringJoiner sortedArray4 = new StringJoiner("\n");

        for (Element element : array4) {
            sortedArray4.add("Element: " + element.value + " Original index: " + element.originalIndex);
        }
        StdOut.println("Sorted array 4 with lost of stability");
        StdOut.println(sortedArray4);
    }
}

Additional notes/results:
5.1.17 - In-place key-indexed counting

LSD and MSD sorts that use only a constant amount of extra space are not stable.

Counterexample for LSD sort:

The array ["4PGC938", "2IYE230", "3CIO720", "1ICK750", "1OHV845", "4JZY524", "1ICK750", "3CIO720", "1OHV845", "1OHV845", "2RLA629", "2RLA629", "3ATW723"] after being sorted by in-place LSD becomes:
["1OHV845", "1OHV845", "1OHV845", "1ICK750", "1ICK750", "2RLA629", "2IYE230", "2RLA629", "3ATW723", "3CIO720", "3CIO720", "4PGC938", "4JZY524"]
If it were sorted by non-in-place LSD the output would be:
["1ICK750", "1ICK750", "1OHV845", "1OHV845", "1OHV845", "2IYE230", "2RLA629", "2RLA629", "3ATW723", "3CIO720", "3CIO720", "4JZY524", "4PGC938"]

Counterexample for both LSD and MSD sorts:

The array ["CAA" (index 0), "ABB" (index 1), "ABB" (index 2)] after being sorted by either in-place LSD or in-place MSD becomes:
["ABB" (original index 2), "ABB" (original index 1), "CAA" (original index 0)]
If it were sorted by non-in-place LSD or MSD the output would be:
["ABB" (original index 1), "ABB" (original index 2), "CAA" (original index 0)]


Question: Timings. Compare the running times of MSD string sort and 3-way string quicksort, using various key generators. For fixed-length keys, include LSD string sort.

Answer:
5.1.22 - Timings

Running 10 experiments with 1000000 strings for random decimal keys (with fixed-length of 10 characters), random CA license plates, random fixed-length words (with fixed-length of 10 characters) and random variable length items (with given values 'A' and 'B'). The cutoff for small subarrays used in Most-Significant-Digit sort was equal to 15.

     Random string type |                 Sort type |  Average time spent
           Decimal keys     Least-Significant-Digit                  2.30
           Decimal keys      Most-Significant-Digit                  0.45
           Decimal keys      3-way string quicksort                  0.32
      CA license plates     Least-Significant-Digit                  1.48
      CA license plates      Most-Significant-Digit                  0.41
      CA license plates      3-way string quicksort                  0.33
     Fixed length words     Least-Significant-Digit                  2.52
     Fixed length words      Most-Significant-Digit                  0.28
     Fixed length words      3-way string quicksort                  0.35
  Variable length items      Most-Significant-Digit                  1.80
  Variable length items      3-way string quicksort                  0.55

The experiment results show that for all random string types, LSD sort had the worst results.
For random decimal keys, random CA license plates and random variable length items 3-way string quicksort had the best results.
For random fixed-length words, MSD had the best running time.
Having to always scan all characters in all keys may explain why LSD sort had the slowest running times when sorting all random string types. 3-way string quicksort may have had good results because it does not create a high number of empty subarrays, as MSD sort does, and because it can handle well keys with long common prefixes (which are likely to happen in random decimal keys, random CA license plates and random variable length items).
Random fixed-length words are less likely to have long common prefixes (because all their characters are in the range [40, 125]), which may explain why MSD sort had better results than both LSD sort and 3-way string quicksort during their sort.


Question: Array accesses. Compare the number of array accesses used by MSD string sort and 3-way string sort, using various key generators. For fixed-length keys, include LSD string sort.

Answer:
5.1.23 - Array accesses

Running 10 experiments with 1000000 strings for random decimal keys (with fixed-length of 10 characters), random CA license plates, random fixed-length words (with fixed-length of 10 characters) and random variable length items (with given values 'A' and 'B'). The cutoff for small subarrays used in Most-Significant-Digit sort was equal to 15.

     Random string type |                 Sort type |  Number of array accesses
           Decimal keys     Least-Significant-Digit                    40000000
           Decimal keys      Most-Significant-Digit                    35443947
           Decimal keys      3-way string quicksort                    78124405
      CA license plates     Least-Significant-Digit                    28000000
      CA license plates      Most-Significant-Digit                    25703588
      CA license plates      3-way string quicksort                    82889002
     Fixed length words     Least-Significant-Digit                    40000000
     Fixed length words      Most-Significant-Digit                    14841196
     Fixed length words      3-way string quicksort                    95310075
  Variable length items      Most-Significant-Digit                    72121457
  Variable length items      3-way string quicksort                    97523400

The experiment results show that for all random string types, 3-way string quicksort accessed the array more times than LSD and MSD sort; LSD sort accessed the array more times than MSD sort; and MSD sort had the lowest number of array accesses.
A possible explanation for these results is the fact that 3-way string quicksort accesses the array 4 times for each exchange operation, which leads to more array accesses than both LSD and MSD sorts, that do not make inplace exchanges.
LSD sort will always access the array 4 * N * W times, where N is the number of strings and W is the length of the strings (which is equivalent to 4 array accesses for each character in the keys) and MSD sort will only access the array while the strings have common prefixes, which explains why MSD sort has the lowest number of array accesses of all sort types.


Question: Rightmost character accessed. Compare the position of the rightmost character accessed for MSD string sort and 3-way string quicksort, using various key generators.

Answer:
5.1.24 - Rightmost character accessed

Running 1 experiment with 1000000 strings for random decimal keys (with fixed-length of 10 characters), random CA license plates, random fixed-length words (with fixed-length of 10 characters) and random variable length items (with given values 'A' and 'B'). The cutoff for small subarrays used in Most-Significant-Digit sort was equal to 15.

     Random string type |                 Sort type |  Rightmost character accessed
           Decimal keys      Most-Significant-Digit                               9
           Decimal keys      3-way string quicksort                               9
      CA license plates      Most-Significant-Digit                               6
      CA license plates      3-way string quicksort                               6
     Fixed length words      Most-Significant-Digit                               5
     Fixed length words      3-way string quicksort                               5
  Variable length items      Most-Significant-Digit                              20
  Variable length items      3-way string quicksort                              20

In all experiments the rightmost character position accessed in MSD sort and in 3-way string quicksort was the same, which shows that both algorithms scan the same characters.


Question:
Draw the TST that results when the keys
 no is th ti fo al go pe to co to th ai of th pa
are inserted in that order into an initially empty TST.

Answer:
5.2.2

Ternary search trie after insertion of keys:
no is th ti fo al go pe to co to th ai of th pa

Empty TST:

Insert: no
 n
/|\
 o
/|\

Insert: is
       n
    /  |  \
   i   o
  /|\ /|\
   s
  /|\

Insert: th
       n
    /  |   \
   i   o    t
  /|\ /|\  /|\
   s        h
  /|\      /|\

Insert: ti
       n
    /  |   \
   i   o    t
  /|\ /|\  /|\
   s        h
  /|\      /|\
              i
             /|\

Insert: fo
          n
     /    |   \
    i     o    t
  / | \  /|\  /|\
 f   s         h
/|\ /|\       /|\
 o               i
/|\             /|\

Insert: al
              n
         /    |   \
        i     o    t
      / | \  /|\  /|\
     f   s         h
  /  |\ /|\       /|\
 a   o               i
/|\ /|\             /|\
 l
/|\

Insert: go
                  n
             /    |   \
            i     o    t
      /     | \  /|\  /|\
     f       s         h
  /  | \    /|\       /|\
 a   o   g               i
/|\ /|\ /|\             /|\
 l       o
/|\     /|\

Insert: pe
                  n
             /    |     \
            i     o       t
      /     | \  /|\   /  | \
     f       s        p   h
  /  | \    /|\      /|\ /|\
 a   o   g            e     i
/|\ /|\ /|\          /|\   /|\
 l       o
/|\     /|\

Insert: to
                  n
             /    |     \
            i     o       t
      /     | \  /|\   /  | \
     f       s        p   h
  /  | \    /|\      /|\ /|\
 a   o   g            e     i
/|\ /|\ /|\          /|\   /|\
 l       o                    o
/|\     /|\                  /|\

Insert: co
                      n
                 /    |     \
                i     o       t
          /     | \  /|\   /  | \
         f       s        p   h
  /      | \    /|\      /|\ /|\
 a       o   g            e     i
/| \    /|\ /|\          /|\   /|\
 l   c      o                     o
/|\ /|\    /|\                   /|\
     o
    /|\

Insert: to
                      n
                 /    |     \
                i     o       t
          /     | \  /|\   /  | \
         f       s        p   h
  /      | \    /|\      /|\ /|\
 a       o   g            e     i
/| \    /|\ /|\          /|\   /|\
 l   c      o                     o
/|\ /|\    /|\                   /|\
     o
    /|\

Insert: th
                      n
                 /    |     \
                i     o       t
          /     | \  /|\   /  | \
         f       s        p   h
  /      | \    /|\      /|\ /|\
 a       o   g            e     i
/| \    /|\ /|\          /|\   /|\
 l   c      o                     o
/|\ /|\    /|\                   /|\
     o
    /|\

Insert: ai
                        n
                   /    |     \
                  i     o       t
            /     | \  /|\   /  | \
           f       s        p   h
    /      | \    /|\      /|\ /|\
   a       o   g            e     i
  /| \    /|\ /|\          /|\   /|\
   l   c      o                     o
  /|\ /|\    /|\                   /|\
 i     o
/|\   /|\

Insert: of
                        n
                   /    |        \
                  i     o          t
            /     | \  /|\     /    | \
           f       s          p     h
    /      | \    /|\      /  |\   /|\
   a       o   g          o   e       i
  /| \    /|\ /|\        /|\ /|\     /|\
   l   c      o           f             o
  /|\ /|\    /|\         /|\           /|\
 i     o
/|\   /|\

Insert: th
                        n
                   /    |        \
                  i     o          t
            /     | \  /|\     /    | \
           f       s          p     h
    /      | \    /|\      /  |\   /|\
   a       o   g          o   e       i
  /| \    /|\ /|\        /|\ /|\     /|\
   l   c      o           f             o
  /|\ /|\    /|\         /|\           /|\
 i     o
/|\   /|\

Insert: pa
                        n
                   /    |          \
                  i     o            t
            /     | \  /|\       /    | \
           f       s            p     h
    /      | \    /|\      /    |\   /|\
   a       o   g          o     e       i
  /| \    /|\ /|\        /|\   /|\     /|\
   l   c      o           f   a           o
  /|\ /|\    /|\         /|\ /|\         /|\
 i     o
/|\   /|\


Question:
Draw the R-way trie that results when the keys
 now is the time for all good people to come to the aid of
are inserted in that order into an initially empty trie (do not draw null links).

Answer:
5.2.3

R-way trie after insertion of keys:
now is the time for all good people to come to the aid of

Empty trie:
O

Insert: now
O
|
n
|
o
|
w

Insert: is
  O
 / \
i   n
|   |
s   o
    |
    w

Insert: the
   O
 / | \
i  n  t
|  |  |
s  o  h
   |  |
   w  e

Insert: time
   O
 / |  \
i  n    t
|  |   /|
s  o  h i
   |  | |
   w  e m
        |
        e

Insert: for
      O
  / / |  \
 f i  n    t
 | |  |   /|
 o s  o  h i
 |    |  | |
 r    w  e m
           |
           e

Insert: all
        O
  / / / |  \
 a f i  n    t
 | | |  |   /|
 l o s  o  h i
 | |    |  | |
 l r    w  e m
             |
             e

Insert: good
          O
  / / / / |  \
 a f g i  n    t
 | | | |  |   /|
 l o o s  o  h i
 | | |    |  | |
 l r o    w  e m
     |         |
     d         e

Insert: people
          O
  / / / / | \   \
 a f g i  n  p   t
 | | | |  |  |  /|
 l o o s  o  e h i
 | | |    |  | | |
 l r o    w  o e m
     |       |   |
     d       p   e
             |
             l
             |
             e

Insert: to
          O
  / / / / | \   \
 a f g i  n  p   t
 | | | |  |  |  /|\
 l o o s  o  e h i o
 | | |    |  | | |
 l r o    w  o e m
     |       |   |
     d       p   e
             |
             l
             |
             e

Insert: come
            O
  / / / / / | \   \
 a c f g i  n  p   t
 | | | | |  |  |  /|\
 l o o o s  o  e h i o
 | | | |    |  | | |
 l m r o    w  o e m
   |   |       |   |
   e   d       p   e
               |
               l
               |
               e

Insert: to
            O
  / / / / / | \   \
 a c f g i  n  p   t
 | | | | |  |  |  /|\
 l o o o s  o  e h i o
 | | | |    |  | | |
 l m r o    w  o e m
   |   |       |   |
   e   d       p   e
               |
               l
               |
               e

Insert: the
            O
  / / / / / | \   \
 a c f g i  n  p   t
 | | | | |  |  |  /|\
 l o o o s  o  e h i o
 | | | |    |  | | |
 l m r o    w  o e m
   |   |       |   |
   e   d       p   e
               |
               l
               |
               e

Insert: aid
             O
   / / / / / | \   \
  a c f g i  n  p   t
 /| | | | |  |  |  /|\
i l o o o s  o  e h i o
| | | | |    |  | | |
d l m r o    w  o e m
    |   |       |   |
    e   d       p   e
                |
                l
                |
                e

Insert: of
             O
   / / / / / | \ \   \
  a c f g i  n  o p   t
 /| | | | |  |  | |  /|\
i l o o o s  o  f e h i o
| | | | |    |    | | |
d l m r o    w    o e m
    |   |         |   |
    e   d         p   e
                  |
                  l
                  |
                  e


Question:
Draw the TST that results when the keys
 now is the time for all good people to come to the aid of
are inserted in that order into an initially empty TST.

Answer:
5.2.4

Ternary search trie after insertion of keys:
now is the time for all good people to come to the aid of

Empty TST:

Insert: now
 n
/|\
 o
/|\
 w
/|\

Insert: is
       n
    /  |  \
   i   o
  /|\ /|\
   s   w
  /|\ /|\

Insert: the
       n
    /  |   \
   i   o    t
  /|\ /|\  /|\
   s   w    h
  /|\ /|\  /|\
            e
           /|\

Insert: time
        n
    /   |    \
   i    o     t
  /|\  /|\   /|\
   s    w     h
  /|\  /|\  / |  \
              e   i
             /|\ /|\
                  m
                 /|\
                  e
                 /|\

Insert: for
          n
      /   |    \
     i    o     t
  /  |\  /|\   /|\
 f   s    w     h
/|\ /|\  /|\  / |  \
 o              e   i
/|\            /|\ /|\
 r                  m
/|\                /|\
                    e
                   /|\

Insert: all
              n
          /   |    \
         i    o     t
      /  |\  /|\   /|\
     f   s    w     h
  /  |\ /|\  /|\  / |  \
 a   o              e   i
/|\ /|\            /|\ /|\
 l   r                  m
/|\ /|\                /|\
 l                      e
/|\                    /|\

Insert: good
                  n
              /   |    \
             i    o     t
       /     |\  /|\   /|\
     f       s    w     h
  /  |  \   /|\  /|\  / |  \
 a   o   g              e   i
/|\ /|\ /|\            /|\ /|\
 l   r   o                  m
/|\ /|\ /|\                /|\
 l       o                  e
/|\     /|\                /|\
         d
        /|\

Insert: people
                  n
              /   |       \
             i    o         t
       /     |\  /|\    /   |\
     f       s    w    p    h
  /  |  \   /|\  /|\  /|\ / |  \
 a   o   g             e    e   i
/|\ /|\ /|\           /|\  /|\ /|\
 l   r   o             o        m
/|\ /|\ /|\           /|\      /|\
 l       o             p        e
/|\     /|\           /|\      /|\
         d             l
        /|\           /|\
                       e
                      /|\

Insert: to
                  n
              /   |       \
             i    o         t
       /     |\  /|\    /   |\
     f       s    w    p    h
  /  |  \   /|\  /|\  /|\ / |  \
 a   o   g             e    e    i
/|\ /|\ /|\           /|\  /|\ / |  \
 l   r   o             o         m   o
/|\ /|\ /|\           /|\       /|\ /|\
 l       o             p         e
/|\     /|\           /|\       /|\
         d             l
        /|\           /|\
                       e
                      /|\

Insert: come
                      n
                  /   |       \
                 i    o         t
           /     |\  /|\    /   |\
         f       s    w    p    h
  /      |  \   /|\  /|\  /|\ / |  \
 a       o   g             e    e    i
/|  \   /|\ /|\           /|\  /|\ / |  \
 l   c   r   o             o         m   o
/|\  |  /|\ /|\           /|\       /|\ /|\
 l   o       o             p         e
/|\ /|\     /|\           /|\       /|\
     m       d             l
    /|\     /|\           /|\
     e                     e
    /|\                   /|\

Insert: to
                      n
                  /   |       \
                 i    o         t
           /     |\  /|\    /   |\
         f       s    w    p    h
  /      |  \   /|\  /|\  /|\ / |  \
 a       o   g             e    e    i
/|  \   /|\ /|\           /|\  /|\ / |  \
 l   c   r   o             o         m   o
/|\  |  /|\ /|\           /|\       /|\ /|\
 l   o       o             p         e
/|\ /|\     /|\           /|\       /|\
     m       d             l
    /|\     /|\           /|\
     e                     e
    /|\                   /|\

Insert: the
                      n
                  /   |       \
                 i    o         t
           /     |\  /|\    /   |\
         f       s    w    p    h
  /      |  \   /|\  /|\  /|\ / |  \
 a       o   g             e    e    i
/|  \   /|\ /|\           /|\  /|\ / |  \
 l   c   r   o             o         m   o
/|\  |  /|\ /|\           /|\       /|\ /|\
 l   o       o             p         e
/|\ /|\     /|\           /|\       /|\
     m       d             l
    /|\     /|\           /|\
     e                     e
    /|\                   /|\

Insert: aid
                          n
                      /   |       \
                     i    o         t
               /     |\  /|\    /   |\
             f       s    w    p    h
      /      |  \   /|\  /|\  /|\ / |  \
     a       o   g             e    e    i
     |  \   /|\ /|\           /|\  /|\ / |  \
     l   c   r   o             o         m   o
    /|\  |  /|\ /|\           /|\       /|\ /|\
 i   l   o       o             p         e
/|\ /|\ /|\     /|\           /|\       /|\
 d       m       d             l
/|\     /|\     /|\           /|\
         e                     e
        /|\                   /|\

Insert: of
                          n
                      /   |           \
                     i    o             t
               /     |\  /|\        /   |\
             f       s    w        p    h
      /      |  \   /|\  /|\   /   |\ / |  \
     a       o   g            o    e    e    i
     |  \   /|\ /|\          /|\  /|\  /|\ / |  \
     l   c   r   o            f    o         m   o
    /|\  |  /|\ /|\          /|\  /|\       /|\ /|\
 i   l   o       o                 p         e
/|\ /|\ /|\     /|\               /|\       /|\
 d       m       d                 l
/|\     /|\     /|\               /|\
         e                         e
        /|\                       /|\