Рубрики
Без рубрики

Найти самый маленький недостающий целый ряд в массиве

Узнайте, как найти самый маленький недостающий целый ряд в массиве.

Автор оригинала: François Dupire.

1. Обзор

В этом учебнике мы увидим различные алгоритмы, позволяющие нам найти самый маленький недостающий положительный целый ряд в массиве.

Во-первых, мы пройдемся по объяснению проблемы. После этого мы увидим три различных алгоритма, которые подойдут нашим потребностям. Наконец, мы обсудим их сложности.

2. Объяснение проблемы

Во-первых, давайте объясним, какова цель алгоритма. Мы хотим найти самый маленький недостающий положительный целый ряд в массиве положительных целых. То есть, в массиве x элементы, найти самый маленький элемент между 0 и x – 1 этого нет в массиве. Если массив содержит их все, то решение x , размер массива.

Например, рассмотрим следующий массив: 0, 1, 3, 5, 6 . Он 5 азы. Это означает, что мы ищем самый маленький integer между 0 и 4 этого нет в этом массиве. В данном конкретном случае, это 2 .

Теперь давайте представим себе другой массив: (0, 1, 2, 3) . Как и 4 элементы, мы ищем интегратор между 0 и 3 . Ни один не отсутствует, таким образом, самый маленький целый, который не находится в массиве 4 .

3. Отсортированная решетка

Теперь давайте посмотрим, как найти самый маленький недостающий номер в отсортированного массива. В отсортированного массива, самый маленький недостающий целый будет первый индекс, который не держит себя в качестве значения.

Рассмотрим следующий отсортированную массив: (0, 1, 3, 4, 6, 7) . Теперь давайте посмотрим, какое значение соответствует какому индексу:

Index: 0 1 2 3 4 5
Value: 0 1 3 4 6 7

Как мы видим, индекс стоимости не держит интегратор 2 , поэтому 2 является самым маленьким недостающим целым в массиве.

Как насчет реализации этого алгоритма на Java? Давайте сначала создадим класс Самый маленькийМиссингПозитивИнтегер методом поискInSortedArray () :

public class SmallestMissingPositiveInteger {
    public static int searchInSortedArray(int[] input) {
        // ...
    }
}

Теперь мы можем итерировать над массивом и поиск первого индекса, который не содержит себя в качестве значения и вернуть его в результате:

for (int i = 0; i < input.length; i++) {
    if (i != input[i]) {
        return i;
    }
}

Наконец, если мы заверим цикл, не найдя недостающий элемент, мы должны вернуть следующий целый ряд, который является длиной массива , как мы начинаем с индексного 0 :

return input.length;

Давайте проверим, что все это работает, как ожидалось. Представьте себе массив целых 0 5 , с номером 3 недостающий:

int[] input = new int[] {0, 1, 2, 4, 5};

Тогда, если мы ищем первый пропавший integer, 3 должны быть возвращены:

int result = SmallestMissingPositiveInteger.searchInSortedArray(input);

assertThat(result).isEqualTo(3);

Но, если мы ищем недостающий номер в массиве без каких-либо пропавших без вести целых:

int[] input = new int[] {0, 1, 2, 3, 4, 5};

Мы обнаружим, что первый пропавший integer 6 , которая является длина массива:

int result = SmallestMissingPositiveInteger.searchInSortedArray(input);

assertThat(result).isEqualTo(input.length);

Далее мы увидим, как обрабатывать несортированные массивы.

4. Несортированный массив

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

4.1. Сортировка массива сначала

Начнем с первого решения и создадим новую поискInUnsortedArraySortingFirst () метод.

Итак, мы будем повторно использовать наш алгоритм, но сначала нам нужно сортировать наш входной массив. Для этого мы будем использовать Arrays.sort () :

Arrays.sort(input);

Этот метод сортирует свой вход в соответствии с его естественным порядком. Для интеграторов это означает от самого маленького к величайшему. Более подробная информация об алгоритмах сортировки содержится в нашей статье о сортировке массивов на Java.

После этого мы можем вызвать наш алгоритм с отсортированным вводом:

return searchInSortedArray(input);

Вот и все, теперь мы можем проверить, что все работает, как ожидалось. Давайте представим себе следующий массив с несортированной целых и недостающих номеров 1 и 3 :

int[] input = new int[] {4, 2, 0, 5};

Как 1 является самым маленьким недостающим integer, мы ожидаем, что это будет результатом вызова нашего метода:

int result = SmallestMissingPositiveInteger.searchInUnsortedArraySortingFirst(input);

assertThat(result).isEqualTo(1);

Теперь давайте попробуем его на массиве без отсутствуют номера:

int[] input = new int[] {4, 5, 1, 3, 0, 2};

int result = SmallestMissingPositiveInteger.searchInUnsortedArraySortingFirst(input);

assertThat(result).isEqualTo(input.length);

Вот и все, алгоритм возвращается 6 , то есть длина массива.

4.2. Использование Boolean Array

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

Во-первых, давайте создадим третий метод, поискInUnsortedArrayBooleanArray () .

После этого давайте создадим массив булеан, флаги , и для каждого целого ряда входного массива, который соответствует индексу булеан массива, мы устанавливаем соответствующее значение для истинное :

boolean[] flags = new boolean[input.length];
for (int number : input) {
    if (number < flags.length) {
        flags[number] = true;
    }
}

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

for (int i = 0; i < flags.length; i++) {
    if (!flags[i]) {
        return i;
    }
}

return flags.length;

Опять же, давайте попробуем этот алгоритм с нашими примерами. Сначала мы повторно использовать массив отсутствует 1 и 3 :

int[] input = new int[] {4, 2, 0, 5};

Затем, при поиске самых маленьких пропавших без вести integer с нашим новым алгоритмом, ответ по-прежнему 1 :

int result = SmallestMissingPositiveInteger.searchInUnsortedArrayBooleanArray(input);

assertThat(result).isEqualTo(1);

И для полного массива, ответ не меняется либо и по-прежнему 6 :

int[] input = new int[] {4, 5, 1, 3, 0, 2};

int result = SmallestMissingPositiveInteger.searchInUnsortedArrayBooleanArray(input);

assertThat(result).isEqualTo(input.length);

5. Сложности

Теперь, когда мы рассмотрели алгоритмы, давайте поговорим об их сложностях, используя Big O нотации .

5.1. Отсортированная решетка

Начнем с первого алгоритма, по которому ввод уже отсортирован. В этом случае наихудшим сценарием является не поиск отсутствуюшего целых ряда и, следовательно, прохождение всего массива. Это означает у нас есть линейная сложность , что отмечается О(н) , учитывая n является длина нашего вклада.

5.2. Несортированный массив с алгоритмом сортировки

Теперь рассмотрим наш второй алгоритм. В этом случае входной массив не сортирован, и мы сортим его перед применением первого алгоритма. Вот, сложность будет наибольшей между механизмом сортировки и алгоритмом, который .

По данный вопрос о Java 11 Arrays.sort () метод использует двухъявимную алгоритм быстрого сортировки сортировать массивы. Сложность этого алгоритма сортировки, как правило, O (n log(n)) , хотя это может ухудшиться до O(n2) . Это означает, сложность нашего алгоритма будет O (n log(n)) в целом, а также может деградировать до квадратной сложности O(n2) .

Это для сложности времени, но давайте не будем забывать о пространстве. Хотя алгоритм поиска не занимает дополнительное пространство, алгоритм сортировки делает. Алгоритм быстрого сортировки занимает до O (журнал(n)) пространство для выполнения. Это то, что мы можем рассмотреть при выборе алгоритма для больших массивов.

5.3. Несортированный массив с Boolean Array

Наконец, давайте посмотрим, как работает наш третий и последний алгоритм. Для этого мы не сортим входной массив, что означает мы не страдаем от сложности сортировки . На самом деле, мы проходим только два массива, оба одного размера. Это означает, наша сложность времени должна быть O(2n) , который упрощается до О(н) . Это лучше, чем предыдущий алгоритм.

Но, когда дело доходит до сложности пространства, мы создаем второй массив такого же размера, как вход. Это означает, у нас О(н) сложность пространства , что хуже предыдущего алгоритма.

Зная все это, это до нас, чтобы выбрать алгоритм, который наилучшим образом соответствует нашим потребностям, в зависимости от условий, в которых он будет использоваться.

6. Заключение

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

Как обычно, полные примеры кода, показанные в этой статье, доступны более на GitHub .