import java.util.ArrayList; /* A concrete instance of this abstract class has a "resident" two-dimensional ** matrix of integer values, provided at construction. One can search within ** the matrix to find all the locations at which a specified key occurs. ** Unless constraints are imposed upon the matrix, such a search would require ** that every element be accessed. Here the assumption is that the values ** along each row and column are in increasing order. ** ** Author: R. McCloskey ** Date: May 2022 */ public abstract class SaddleBackSearcher { // instance variables // ------------------ protected int probeCntr; // # probes performed since last reset protected int[][] matrix; // resident matrix (in which searches occur) protected int key; // value most recently searched for // list of locations at which the search key was found during the most // recent call to findAll() protected ArrayList locList; // constructor // ----------- /* Establishes the resident matrix of this MatrixSearcher. ** It is verified that the values in the matrix increase along each ** row and column. If not, an IllegalArgumentException is thrown. */ public SaddleBackSearcher(int[][] ary) { if (isValidMatrix(ary)) { matrix = ary; resetProbeCount(); } else { throw new IllegalArgumentException("Unsuitable matrix"); } } // observers // --------- /* Returns (a reference to) the resident matrix. */ public int[][] residentMatrix() { return matrix; } /* Returns the number of probes performed since the last time ** the probe counter was reset. */ public int probeCount() { return probeCntr; } /* Returns the key that was searched for by the most recent call ** to findAll(). */ public int getKey() { return key; } /* Returns the list of locations produced by the most recent call ** to findAll(). */ public ArrayList getLocs() { return locList; } // mutators // -------- /* Resets the probe counter to zero. */ public void resetProbeCount() { probeCntr = 0; } /* Builds an ArrayList (which subsequently can be retrieved via a call to ** getLocs()) containing all the locations at which the given key occurs ** within the resident matrix. */ public void findAll(int key) { int numRows = matrix.length; int numCols = matrix[0].length; GridLocation bottom = new GridLocation(0,0); GridLocation top = new GridLocation(numRows-1, numCols-1); findAll(key, bottom, top); } /* Builds an ArrayList (which subsequently can be retrieved via a call to ** getLocs()) containing all the locations at which the given key occurs ** within the specified rectangular region within the resident matrix. */ public void findAll(int key, GridLocation bottom, GridLocation top) { this.key = key; locList = new ArrayList(); findAllAux(bottom, top); } // abstract method (to be completed in each child class) // ----------------------------------------------------- /* Adds to the ArrayList 'locList' (an instance variable) all the ** locations that contain the search key (instance variable 'key') ** within the specified rectangular region of the resident matrix. */ protected abstract void findAllAux(GridLocation lowCorner, GridLocation highCorner); // private methods // --------------- /* Reports whether or not the given two-dimensional array satisfies ** the conditions required here. One condition is that all rows ** have the same length. The other condition is that the values ** increase along each row and each column. */ private boolean isValidMatrix(int[][] ary) { boolean result; if (!isRectangular(ary)) { result = false; } else { // ary is rectangular; check rows and columns. result = true; int r = 0; while (r != ary.length && result) { result = isIncreasing(ary[r]); // check row r r = r+1; } // Now check columns int numCols = ary[0].length; int c = 0; while (c != numCols && result) { result = isIncreasingColumn(ary, c); c = c+1; } } return result; } /* Reports whether the values in the given array are in ** increasing order. */ private boolean isIncreasing(int[] b) { int i = 1; // loop invariant: The values in b[0..i) are increasing. while (i < b.length && b[i-1] < b[i]) { i = i+1; } return i >= b.length; } /* Reports whether the values in the specified column within ** the given two-dimensional array are in increasing order. ** pre: Every row in b[][] has at least col+1 columns. */ private boolean isIncreasingColumn(int[][] b, int col) { int i = 1; // loop invariant: The values in b[0..i)[col] are increasing. while (i < b.length && b[i-1][col] < b[i][col]) { i = i+1; } return i >= b.length; } /* Reports whether the given two-dimensional array is rectangular ** (meaning that all its rows have the same length). ** pre: b.length != 0 */ private boolean isRectangular(int[][] b) { int numCols = b[0].length; int i = 1; // loop invariant: Rows 0 through i-1 all have same length. while (i < b.length && b[i].length == numCols) { i = i+1; } return i >= b.length; } }