Aqui está uma O(N)solução simples que usa O(N)espaço. Estou assumindo que estamos restringindo a lista de entrada a números não negativos e que queremos encontrar o primeiro número não negativo que não está na lista.
- Encontre o comprimento da lista; vamos dizer que é
N.
- Aloque uma matriz de
Nbooleanos, inicializada para todos false.
- Para cada número
Xna lista, se Xfor menor que N, defina o X'thelemento da matriz como true.
- Faça a varredura da matriz começando pelo índice
0, procurando o primeiro elemento que é false. Se você encontrar o primeiro falseno índice I, então Ié a resposta. Caso contrário (ou seja, quando todos os elementos estiverem true), a resposta é N.
Na prática, a "matriz de Nbooleanos" provavelmente seria codificada como um "bitmap" ou "bitset" representado como uma matriz byteou int. Isso normalmente usa menos espaço (dependendo da linguagem de programação) e permite que a varredura da primeira falseseja feita mais rapidamente.
É assim / porque o algoritmo funciona.
Suponha que os Nnúmeros da lista não sejam distintos ou que um ou mais deles seja maior que N. Isso significa que deve haver pelo menos um número no intervalo 0 .. N - 1que não está na lista. Portanto, o problema de encontrar o menor número em falta devem, portanto, reduzir o problema de encontrar o número que falta menor menosN . Isso significa que não precisamos controlar os números maiores ou iguais a N... porque eles não serão a resposta.
A alternativa ao parágrafo anterior é que a lista é uma permutação dos números de 0 .. N - 1. Nesse caso, a etapa 3 define todos os elementos da matriz como truee a etapa 4 nos diz que o primeiro número "ausente" é N.
A complexidade computacional do algoritmo é O(N)com uma constante de proporcionalidade relativamente pequena. Ele faz duas passagens lineares pela lista, ou apenas uma passagem se o comprimento da lista for conhecido no início. Não há necessidade de representar o manter a lista inteira na memória, portanto, o uso de memória assintótica do algoritmo é exatamente o que é necessário para representar a matriz de booleanos; ou seja, O(N)bits.
(Por outro lado, algoritmos que dependem de classificação ou particionamento na memória pressupõem que você pode representar a lista inteira na memória. Na forma em que a pergunta foi feita, isso exigiria O(N)palavras de 64 bits.)
@Jorn comenta que as etapas 1 a 3 são uma variação da classificação por contagem. Em certo sentido, ele está certo, mas as diferenças são significativas:
- Uma classificação de contagem requer uma matriz de (pelo menos)
Xmax - Xmincontadores onde Xmaxé o maior número da lista e Xminé o menor número da lista. Cada contador deve ser capaz de representar N estados; isto é, assumindo uma representação binária, ela deve ter um tipo inteiro (pelo menos) ceiling(log2(N))bits.
- Para determinar o tamanho da matriz, uma classificação de contagem precisa fazer uma passagem inicial pela lista para determinar
Xmaxe Xmin.
- O requisito mínimo de espaço no pior caso é, portanto,
ceiling(log2(N)) * (Xmax - Xmin)bits.
Em contraste, o algoritmo apresentado acima simplesmente requer Nbits nos piores e melhores casos.
No entanto, essa análise leva à intuição de que se o algoritmo fizesse uma passagem inicial pela lista procurando um zero (e contando os elementos da lista, se necessário), daria uma resposta mais rápida usando nenhum espaço se encontrasse o zero. Definitivamente, vale a pena fazer isso se houver uma alta probabilidade de encontrar pelo menos um zero na lista. E essa passagem extra não altera a complexidade geral.
EDIT: Eu mudei a descrição do algoritmo para usar "array de booleanos" desde que as pessoas aparentemente acharam minha descrição original usando bits e bitmaps para ser confusa.