Encontre o menor número inteiro ausente em uma matriz

Encontre o menor número inteiro ausente em uma matriz

1. Visão geral

Neste tutorial, veremos diferentes algoritmos que nos permitem encontrar o menor inteiro positivo ausente em uma matriz. Primeiro, examinaremos a explicação do problema. Depois disso, veremos três algoritmos diferentes adequados às nossas necessidades. Finalmente, discutiremos suas complexidades.

2. Problema Explicação

Primeiro, vamos explicar qual é o objetivo do algoritmo. Queremos procurar o menor número inteiro positivo ausente em uma matriz de números inteiros positivos. That is, in an array of x elements, find the smallest element between 0 and x – 1 that is not in the array. Se o array contém todos eles, então a solução éx, o tamanho do array.

Por exemplo, vamos considerar a seguinte matriz:[0, 1, 3, 5, 6]. Possui elementos5. Isso significa que estamos procurando o menor inteiro entre0 e4 que não está nesta matriz. Neste caso específico, é2.

Agora, vamos imaginar outra matriz:[0, 1, 2, 3]. Como tem elementos4, estamos procurando um número inteiro entre0 e3. Nenhum está faltando, portanto, o menor inteiro que não está na matriz é4.

3. Matriz classificada

Agora, vamos ver como encontrar o menor número ausente em uma matriz classificada. Em uma matriz classificada, o menor inteiro ausente seria o primeiro índice que não se mantém como um valor.

Vamos considerar a seguinte matriz classificada:[0, 1, 3, 4, 6, 7]. Agora, vamos ver qual valor corresponde a qual índice:

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

Como podemos ver, o índice de valor não contém inteiro2, portanto,2 é o menor inteiro ausente na matriz.

Que tal implementar esse algoritmo em Java? Vamos primeiro criar uma classeSmallestMissingPositiveInteger com um métodosearchInSortedArray():

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

Agora, podemos iterar sobre a matriz esearch for the first index that doesn’t contain itself as a valuee retorná-lo como o resultado:

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

Finalmente,if we complete the loop without finding a missing element, we must return the next integer, which is the array length, conforme começamos no índice0:

return input.length;

Vamos verificar se tudo isso funciona conforme o esperado. Imagine uma matriz de inteiros de0 a5, com o número3 faltando:

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

Então, se pesquisarmos o primeiro inteiro ausente,3 deve ser retornado:

int result = SmallestMissingPositiveInteger.searchInSortedArray(input);

assertThat(result).isEqualTo(3);

Mas, se procurarmos um número ausente em uma matriz sem nenhum número inteiro ausente:

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

Descobriremos que o primeiro inteiro ausente é6, que é o comprimento da matriz:

int result = SmallestMissingPositiveInteger.searchInSortedArray(input);

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

A seguir, veremos como lidar com matrizes não classificadas.

4. Matriz não classificada

Então, que tal encontrar o menor número inteiro faltando em uma matriz não classificada? Existem várias soluções. O primeiro é simplesmente classificar a matriz primeiro e depois reutilizar nosso algoritmo anterior. Outra abordagem seria usar outra matriz para sinalizar os números inteiros presentes e percorrer essa matriz para encontrar o primeiro ausente.

4.1. Classificando a matriz primeiro

Vamos começar com a primeira solução e criar um novo métodosearchInUnsortedArraySortingFirst().

So, we’ll be reusing our algorithm, but first, we need to sort our input array. Para fazer isso, usaremosArrays.sort():

Arrays.sort(input);

Esse método classifica sua entrada de acordo com sua ordem natural. Para números inteiros, isso significa do menor para o maior. Há mais detalhes sobre algoritmos de classificação em nosso artigo emsorting arrays in Java.

Depois disso, podemos chamar nosso algoritmo com a entrada agora classificada:

return searchInSortedArray(input);

É isso, agora podemos verificar se tudo funciona conforme o esperado. Vamos imaginar a seguinte matriz com inteiros não classificados e números ausentes1e3:

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

Como1 é o menor inteiro ausente, esperamos que seja o resultado da chamada do nosso método:

int result = SmallestMissingPositiveInteger.searchInUnsortedArraySortingFirst(input);

assertThat(result).isEqualTo(1);

Agora, vamos tentar em uma matriz sem nenhum número ausente:

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

int result = SmallestMissingPositiveInteger.searchInUnsortedArraySortingFirst(input);

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

É isso, o algoritmo retorna6, que é o comprimento da matriz.

4.2. Usando uma matriz booleana

Outra possibilidade é usar outro array - tendo o mesmo comprimento do array de entrada - que contém os valoresboolean informando se o inteiro correspondente a um índice foi encontrado no array de entrada ou não.

Primeiro, vamos criar um terceiro método,searchInUnsortedArrayBooleanArray().

Depois disso, vamos criar a matriz booleanaflags efor each integer in the input array that matches an index of the boolean array, we set the corresponding value to true:

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

Agora, nosso arrayflags contémtrue para cada número inteiro presente no array de entrada efalse caso contrário. Então, podemositerate over the flags array and return the first index holding false. Se nenhum, retornamos o comprimento da matriz:

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

return flags.length;

Novamente, vamos tentar esse algoritmo com nossos exemplos. Primeiro, vamos reutilizar a matriz sem1 e3:

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

Então, ao pesquisar o menor inteiro ausente com nosso novo algoritmo, a resposta ainda é1:

int result = SmallestMissingPositiveInteger.searchInUnsortedArrayBooleanArray(input);

assertThat(result).isEqualTo(1);

E para a matriz completa, a resposta não muda e ainda é6:

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

int result = SmallestMissingPositiveInteger.searchInUnsortedArrayBooleanArray(input);

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

5. Complexidades

Agora que cobrimos os algoritmos, vamos falar sobre suas complexidades, usandoBig O notation.

5.1. Matriz classificada

Vamos começar com o primeiro algoritmo, para o qual a entrada já está classificada. Nesse caso, o pior cenário não é encontrar um número inteiro ausente e, portanto, percorrer toda a matriz. Isso significawe have linear complexity, que é observado comoO(n), considerando é o comprimento de nossa entrada.

5.2. Matriz não classificada com algoritmo de classificação

Agora, vamos considerar nosso segundo algoritmo. Nesse caso, a matriz de entrada não é classificada e a classificamos antes de aplicar o primeiro algoritmo. Aqui,the complexity will be the greatest between that of the sorting mechanism and that of the algorithm itself.

No Java 11, o métodoArrays.sort() usa um dual-pivotquick-sort algorithm para classificar arrays. A complexidade desse algoritmo de classificação é, em geral,O(n log(n)), embora possa degradar atéO(n²). Isso significathe complexity of our algorithm will be O(n log(n)) in general and can also degrade up to a quadratic complexity of O(n²).

Isso é devido à complexidade do tempo, mas não vamos esquecer o espaço. Embora o algoritmo de pesquisa não ocupe espaço extra, o algoritmo de classificação sim. Quick-sort algorithm takes up to O(log(n)) space to execute. Isso é algo que podemos considerar ao escolher um algoritmo para matrizes grandes.

5.3. Matriz não classificada com matriz booleana

Finalmente, vamos ver o desempenho do nosso terceiro e último algoritmo. Para este, não ordenamos a matriz de entrada, o que significawe don’t suffer the complexity of sorting. De fato, apenas atravessamos duas matrizes, ambas do mesmo tamanho. Isso significaour time complexity should be O(2n), which is simplified to O(n). Isso é melhor do que o algoritmo anterior.

Mas, quando se trata de complexidade de espaço, estamos criando uma segunda matriz do mesmo tamanho da entrada. Isso significawe have O(n) space complexity, que é pior do que o algoritmo anterior.

Sabendo de tudo isso, cabe a nós escolher o algoritmo que melhor se adapte às nossas necessidades, dependendo das condições em que será usado.

6. Conclusão

Neste artigo, vimos algoritmos para encontrar o menor inteiro positivo ausente em uma matriz. Vimos como conseguir isso em uma matriz classificada, bem como em uma matriz não classificada. Também discutimos as complexidades de tempo e espaço dos diferentes algoritmos, permitindo escolher um sabiamente de acordo com nossas necessidades.

Como de costume, os exemplos de código completos mostrados neste artigo estão disponíveisover on GitHub.