Как выбрать те числа из массива, которые лежат в заданом диапазоне?

Рейтинг: -1Ответов: 2Опубликовано: 25.02.2023

Задача:

Дан массив возрастающих чисел. Даны два числа. Написать метод, который из данного массива достанет ту часть, которая лежит между данными числами вкл.

Моё решение:

 public static int[] getSubArrayBetween(int[] numbers, int start, int end) {
    int k = 0;
    int[] result = new int[k];
    for (int i = 0; i <= numbers.length; i++) {
        if (numbers[i] >= start && numbers[i] <= end) {
            result[k] = numbers[i];
        }
    }
    return result;
}

}

Подскажите, что я делаю не так?

Ответы

▲ 2Принят

В вашем коде всё не так:

  1. Создаётся пустой массив, так как k == 0 в момент создания массива
  2. Нестрогое сравнение индекса с длиной массива: i <= numbers.length вызовет выход за пределы массива ArrayIndexOutOfBoundsException при обращении к элементам массива numbers[i]
  3. Попытка обращения к элементам пустого массива result[k] также вызовет ArrayIndexOutOfBoundsException
  4. Индекс k не меняется, то есть все подходящие числа исходного массива пишутся в одну ячейку.

Исправление указанных ошибок не является целью данного ответа.


Самое простое/лаконичное решение -- с использованием Stream API, показанное в ответе Byb:

public static int[] getSubArrayBetween(int[] numbers, int start, int end) {
    return IntStream.of(numbers).filter(n -> start <= n && n <= end).toArray();
}

Для более классического решения, без использования коллекций, следует найти минимальный и максимальный индексы элементов входного массива. Если такие индексы не существуют, вернуть пустой массив, иначе копию массива между найденными индексами при помощи Arrays.copyOfRange:

public static int[] getSubArrayBetween(int[] numbers, int start, int end) {
    if (numbers == null || numbers.length == 0 || numbers[0] > end || numbers[numbers.length - 1] < start) {
        return new int[0];
    }
    int min = 0;
    while (numbers[min] < start) min++;
    int max = arr.length - 1;
    while (numbers[max] > end) max--;
    return Arrays.copyOfRange(numbers, min, max + 1);
}
▲ 1

Так как мы заранее не знаем, сколько чисел может лежать между заданными двумя числами start и end, то есть два варианта.

  1. Использовать массив, но выделить заранее очень много памяти. Разумеется, такой подход очень плох, и поэтому его мы рассматривать не будем.
  2. Использовать коллекции: они могут расширяться "на ходу", таким образом, сколько чисел будет лежать между start и end, - ровно столько мы и добавим.

Ниже приведён вариант, как можно исправить ваш код, используя коллекции.

public static List<Integer> getSubArrayBetween(int[] numbers, int start, int end) {
    List<Integer> integers = new ArrayList<>();
    for (Integer integer : numbers) {
        if (integer >= start && integer <= end) {
            integers.add(integer);
        }
    }
    return integers;
}

Чтобы вернуть именно массив, код можно изменить так:

public static Integer[] getSubArrayBetween(int[] numbers, int start, int end) {
    List<Integer> integers = new ArrayList<>();
    for (Integer integer : numbers) {
        if (integer >= start && integer <= end) {
            integers.add(integer);
        }
    }
    return integers.toArray(new Integer[0]);
}

Хотя в таком варианте возвращается массив оболочек Integer, а не примитивного типа данных int. Вернуть массив именно int можно, если использовать Stream API, с которым задача решается в целом проще (при условии, если все числа в массиве действительно идут по возрастанию).

public static int[] getSubArrayBetween(int[] numbers, int start, int end) {
    return IntStream.of(numbers)
            .filter(integer -> integer >= start && integer <= end)
            .toArray();
}