简体   繁体   中英

Min value of adjacent array

I need help with this problem, I need an array 3x5 and then when the user selects a position the output will show the min value of the adjacents numbers. Like this:

3 5 6 7 8
6 7 8 2 3
0 9 2 1 1

And the user selects the position 1,1 . // Diagonals count it too.

Output: The min value around is 0.

This is the code that I have, the problem is I´m asking if there is a better way than spamming if and elses everywhere.

private static int checkAdjacentField(int p1, int p2, int[][] ae) {
    int min = Integer.MAX_VALUE;

    if (p1 == 0) {
        if (p2 == 0) {
            if (ae[p1][p2+1] < min) {
                min = ae[p1][p2+1];
            } else if (ae[p1+1][p2+1] < min) {
                min = ae[p1+1][p2+1];
            } else if (ae[p1+1][p2] < min) {
                min = ae[p1+1][p2];
            }
        } else if (p2 == 1) {
            if (ae[p1][p2+1] < min){
                min = ae[p1][p2+1];
            } else if (ae[p1+1][p2+1] < min) {
                min = ae[p1+1][p2+1];
            } else if (ae[p1+1][p2] < min) {
                min = ae[p1+1][p2];
            } else if (ae[p1+1][p2-1] < min) {
                min = ae[p1+1][p2-1];
            } else if (ae[p1][p2-1] < min) {
                min = ae[p1][p2-1];
            }
        }
    }

    return min;
}

public static void main(String[] args) throws IOException {

    BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

    Random r = new Random();

    int [][] ar = new int[3][5];

    for (int i = 0; i < ar.length; i++) {
        System.out.println();
        for (int j = 0; j < 5; j++) {
            int rand = r.nextInt(9) + 1;
            ar[i][j]=rand;
            System.out.printf("%3d",ar[i][j]);
        }
    }
    System.out.println();

    System.out.println("Select a position [][]: ");
    int pos1 = Integer.parseInt(br.readLine());
    int pos2 = Integer.parseInt(br.readLine());

    System.out.println("The min value around is " + checkAdjacentField(pos1,pos2,ar));
}
}

In the code the 0,0 and 0,1 works and yes, I could spend time doing the spamming method of if else but I want to know if there is a better way so I can improve. Thanks for helping, any idea or answer is welcome.

I think the best way to do that is using the following algo:

  • List of all the adjacent positions (regardless of if they are in the array or not)
  • Filter out the ones that are not in the array
  • Map the remaining positions to their values in the array
  • Lookup the smallest one (as we are dealing with integers, you can sort them and take the first one)

this way :

private static int checkAdjacentField(int col, int row, int[][] ae) {
    int nbRows = ae.length;
    int nbCols = ae[0].length;

    // Stream all the 8 positions around your position
    Stream<Point> positions = Stream.of(       
            new Point(col-1, row-1), new Point(col-1, row), new Point(col-1, row+1),
            new Point(col, row-1), new Point(col, row+1),
            new Point(col+1, row-1), new Point(col+1, row), new Point(col+1, row+1));

    return positions
            .filter(p -> p.x>=0 && p.y>=0 && p.x<nbCols && p.y<nbRows)   // keep those inbound
            .mapToInt(p -> ae[p.y][p.x])      // replace positions by their values in the array
            .sorted()                         // sort the values
            .findFirst().orElse(-1);          // take the first one (smallest) 
}

You could even generate the list of points, instead of hard coding them

private static int checkAdjacentField(int col, int row, int[][] ae) {
    int nbRows = ae.length;
    int nbCols = ae[0].length;

    // Stream all the 8 positions around your position
    Stream<Point> positions = IntStream.rangeClosed(-1, 1).boxed() // -1, 0, 1
            .map(c -> IntStream.rangeClosed(-1, 1).boxed()         // -1, 0, 1
                    .map(r -> new Point(col+c, row+r)))
            .flatMap(p -> p)                                       // to a list
            .filter(p -> !(p.x == col && p.y==row));               // remove center point

    // then same as first example
    return  positions
            .filter(p -> p.x>=0 && p.y>=0 && p.x<nbCols && p.y<nbRows)
            .mapToInt(p -> ae[p.y][p.x])
            .sorted()
            .findFirst().orElse(-1);
}

I prefer hard coding them like in the first example though, it is clearer.

How about this?

private static int checkAdjacentField(int p1, int p2, int[][] ae) {
  int[] tmp = new int[8];
  int left = (p2-1) % ae[0].length;
  int right = (p2+1) % ae[0].length;
  int up = (p1-1) % ae.length;
  int down = (p1+1) % ae.length; 

  tmp[0] = ae[up][left];
  tmp[1] = ae[up][p2];
  tmp[2] = ae[up][right];

  tmp[3] = ae[p1][left];
  tmp[4] = ae[p1][right];

  tmp[5] = ae[down][left];
  tmp[6] = ae[down][p2];
  tmp[7] = ae[down][right];

  List<Integer> tmpList = Arrays.stream(tmp).boxed().collect(Collectors.toList());
  return tmpList.stream().mapToInt(i -> i).min().orElse(0);
}

Granted, this doesn't account for edges. If you care about edges, you can use Math.min or Math.max to cover these cases:

private static int checkAdjacentField(int p1, int p2, int[][] ae) {
  int[] tmp = new int[8];
  int left = (Math.max((p2-1),0)) % ae[0].length;
  int right = (Math.min((p2+1), ae[0].length-1)) % ae[0].length;
  int up = (Math.max((p1-1),0)) % ae.length;
  int down = (Math.min((p1+1), ae.length-1)) % ae.length; 

  tmp[0] = ae[up][left];
  tmp[1] = ae[up][p2];
  tmp[2] = ae[up][right];

  tmp[3] = ae[p1][left];
  tmp[4] = ae[p1][right];

  tmp[5] = ae[down][left];
  tmp[6] = ae[down][p2];
  tmp[7] = ae[down][right];

  List<Integer> tmpList = Arrays.stream(tmp).boxed().collect(Collectors.toList());
  return tmpList.stream().mapToInt(i -> i).min().orElse(0);
}

You can use a for loop like this to parse all adjacents cells , put them in a list and then you can easly calculate the min of that list.

This way , you can reduce the use of If branchs.

adjRow == 0 && adjCol == 0 ==> Middle Cell

    int matrix[][]={{3, 5, 6, 7, 8},
                    {6, 7, 8, 2, 3} , 
                    { 0,9,2,1,1}};

    List adjacents = new ArrayList<>(8);

    int row= 0 ; int col=1 ;

    for (int adjRow = -1; adjRow <= 1; ++adjRow) {
        for (int adjCol = -1; adjCol <= 1; ++adjCol) {
            if ( (adjRow != 0 || adjCol != 0) 
                  // Avoid IndexOutOfBoundsException
                  && ( row + adjRow >= 0 && row + adjRow < 3 )
                  && ( col + adjCol >= 0 && col + adjCol < 5 )                    
                ){

                adjacents.add(matrix[row + adjRow][col + adjCol]);
            }
        }
    }

    System.out.println(Collections.min(adjacents));

For (0,1) ==> Min = 3. Hope it helps ^^

Used imports :

import java.util.Collections;
import java.util.List;
import java.util.ArrayList;

At any time, we have 8 possible locations for an adjacent number. N, NW, W, SW, S, SE, E, NE. So, we can add them all to an ArrayList<> to iterate over and check for the minimum number, if we're sure doing so won't get us out of bounds.

This looks verbose, but will handle any case, edge or not, for any size 2d array and works with the code you provided:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Random;

public class Test {
    char[] array;

    public static void main(String[] args) throws NumberFormatException, IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

        Random r = new Random();

        int [][] ar = new int[3][5];

        for (int i = 0; i < ar.length; i++) {
            System.out.println();
            for (int j = 0; j < 5; j++) {
                int rand = r.nextInt(9) + 1;
                ar[i][j]=rand;
                System.out.printf("%3d",ar[i][j]);
            }
        }
        System.out.println();

        System.out.println("Select a position [][]: ");
        int pos1 = Integer.parseInt(br.readLine());
        int pos2 = Integer.parseInt(br.readLine());

        System.out.println("The min value around is " + checkAdjacentField(pos1,pos2,ar));
    }

    private static int checkAdjacentField(int y, int x, int[][] ae) {
        int height = ae.length;
        int width = ae[0].length;

        int min = Integer.MAX_VALUE;
        ArrayList<Integer> adj = new ArrayList<Integer>();

        // CHECK NORTH, only out of bounds if we're at the NORTH (y == 0)
        if (y != 0) { adj.add(ae[y-1][x]); }
        // CHECK NORTHWEST, only out of bounds if we're at the NORTHWEST (y == 0 & x == 0)
        if (y != 0 && x != 0) { adj.add(ae[y-1][x-1]); }
        // CHECK WEST, only out of bounds if we're at the WEST (x == 0)
        if (x != 0) { adj.add(ae[y][x-1]); }
        // CHECK SOUTHWEST, only out of bounds if we're at the SOUTHWEST (y == (height-1) & x == 0)
        if (y != (height-1) && x != 0) { adj.add(ae[y+1][x-1]); }
        // CHECK SOUTH, only out of bounds if we're at the SOUTH (y == (height-1))
        if (y != (height-1)) { adj.add(ae[y+1][x]); }
        // CHECK SOUTHEAST, only out of bounds if we're at the SOUTHEAST (y == (height-1) & x == (width-1))
        if (y != (height-1) && x != (width-1)) { adj.add(ae[y+1][x+1]); }
        // CHECK EAST, only out of bounds if we're at the EAST (x == (width-1))
        if (x != (width-1)) { adj.add(ae[y][x+1]); }
        // CHECK NORTHEAST, only out of bounds if we're at the NORTHEAST (y == 0 & x == (width-1))
        if (y != 0 && x != (width-1)) { adj.add(ae[y-1][x+1]); }

        // Now, we check our min using our list that contains 3-8 entries
        for (Integer num : adj) {
            if (num < min) { min = num; }
        }

        return min;
    }
}

You can iterate only adjacent values of your matrix. For this you need to determine edges of array and skip your element:

private static int checkAdjacentField(int p1, int p2, int[][] ae) {
    int minColIdx = p2 == 0 ? p2 : p2 - 1;
    int minRowIdx = p1 == 0 ? p1 : p1 - 1;
    int maxRowIdx = p1 == ae.length - 1 ? p1 : p1 + 1;

    int result = Integer.MAX_VALUE;

    for (int i = minRowIdx; i <= maxRowIdx; i++) {
        int[] row = ae[i];
        int maxColIdx = p2 == row.length - 1 ? p2 : p2 + 1;
        for (int k = minColIdx; k <= maxColIdx; k++) {
            if (!(p1 == i && p2 == k) && ae[i][k] < result) {
                result = ae[i][k];
            }
        }
    }
    return result;
}

Simple Program for 3x5 matrix:

private static int checkAdjacentField(int p1, int p2, int[][] ae) {
    int min = Integer.MAX_VALUE;

    for (int i = p1 - 1; i <= p1 + 1; i++) {
        for (int j = p2 - 1; j <= p2 + 1; j++) {
            if ((i < 0 || j < 0) || (i > 2 || j > 4) || (i == p1 && j == p2)) {
                continue;
            }
            if (ae[i][j] < min) {
                min = ae[i][j];
            }
        }

    }

    return min;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM