Encontre o menor inteiro que não está em uma lista

86

Uma pergunta de entrevista interessante que um colega meu usa:

Suponha que você receba uma lista muito longa e não classificada de inteiros de 64 bits não assinados. Como você encontraria o menor inteiro não negativo que não ocorre na lista?

SEGUIMENTO: Agora que a solução óbvia por classificação foi proposta, você pode fazer isso mais rápido do que O (n log n)?

SEGUIMENTO: seu algoritmo deve ser executado em um computador com, digamos, 1 GB de memória

ESCLARECIMENTO: A lista está na RAM, embora possa consumir uma grande quantidade dela. Você recebe o tamanho da lista, digamos N, com antecedência.

PeterAllenWebb
fonte
6
Eu acho que você pode deixar de fora a parte não negativa, visto que você está falando sobre um inteiro sem sinal.
KevenDenen,
4
A pergunta é bem básica, a menos que eu esteja muuuuito fora da base, IMO, mas, como outros mencionaram, há perguntas a serem feitas ou suposições que devem ser declaradas.
James Black,
8
@paxdiablo: Este é um caso em que dizer O (n) não significa muito. Mesmo se você armazenar sua matriz de 2 ^ 64 bits em tabuletas de argila na Ilha de Páscoa e acessá-la por um pombo-correio, o algoritmo ainda é O (n).
IJ Kennedy,
6
Mudar os requisitos de memória na metade torna esta uma ótima pergunta da entrevista ;-)
Chris Ballance
1
Acho engraçado que todas as respostas tenham a mesma solução geral (classificar a matriz e encontrar o primeiro valor que quebra a sequência), mas todas usam uma classificação diferente. (Quicksort modificado, radix sort, ...) A resposta aceita é equivalente a uma classificação de contagem que descarta os elementos acima de N.
Joren,

Respostas:

119

Se a estrutura de dados pode sofrer mutação no local e suportar acesso aleatório, você pode fazer isso em tempo O (N) e espaço adicional O (1). Basta percorrer a matriz sequencialmente e, para cada índice, escreva o valor do índice no índice especificado por valor, colocando recursivamente qualquer valor naquele local em seu lugar e jogando fora os valores> N. Em seguida, percorra novamente a matriz procurando o local onde o valor não corresponde ao índice - esse é o menor valor que não está na matriz. Isso resulta em no máximo 3N comparações e usa apenas alguns valores de espaço temporário.

# Pass 1, move every value to the position of its value
for cursor in range(N):
    target = array[cursor]
    while target < N and target != array[target]:
        new_target = array[target]
        array[target] = target
        target = new_target

# Pass 2, find first location where the index doesn't match the value
for cursor in range(N):
    if array[cursor] != cursor:
        return cursor
return N
Formigas Aasma
fonte
9
Pequenos detalhes. Você perdeu um caso trivial: quando a lista é {0, ..., N-1}. Nesse caso, a passagem 1 não faz nada e na passagem 2 matriz [cursor] == cursor para todas as entradas na lista, de modo que o algoritmo não retorna. Portanto, você precisa de uma instrução 'return N' no final.
Alex
12
Sua solução combina o domínio e o intervalo (a meta é um valor e um índice). O intervalo é limitado pelo armazenamento disponível a 128 milhões de elementos, mas o domínio tem 2 GB de tamanho. Ele falhará com uma única entrada com um valor maior do que o número de entradas que podem ser alocadas na matriz. Se a pergunta não especificou 'muito longa', a resposta é elegante, mesmo que destrua a entrada. A relação tempo-espaço é muito aparente neste problema, e uma solução O (N) pode não ser possível sob as restrições fornecidas.
Pekka
2
A segunda passagem pode usar a pesquisa binária em vez da pesquisa linear.
user448810
4
Esta solução funciona apenas se a faixa de valores e índice forem comparáveis.
Dubby de
7
Funcionará bem com valores maiores. Os valores maiores podem ser ignorados porque eles não podem ter nada a ver com o menor valor que não está na matriz. Para o seu exemplo, a primeira passagem fará um loop sobre a matriz, ignorando todos os valores devido ao destino <N e retornará 0 na primeira iteração da segunda passagem.
Formigas Aasma
89

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.

  1. Encontre o comprimento da lista; vamos dizer que é N.
  2. Aloque uma matriz de Nbooleanos, inicializada para todos false.
  3. Para cada número Xna lista, se Xfor menor que N, defina o X'thelemento da matriz como true.
  4. 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.

Stephen C
fonte
3
@ adi92 Se a etapa 3 fornecer um bitmap com todos os bits definidos como 1, a lista conterá todos os valores de 0 a N-1. Isso significa que o menor inteiro não negativo na lista é N. Se houver qualquer valor entre 0 e N-1 que NÃO esteja na lista, o bit correspondente não será definido. O menor desses valores é, portanto, a resposta.
divegeek
4
@ adi92 Em seu exemplo, a lista conteria 300 elementos. Isso significa que se houver algum valor "ausente", ele deve ser menor que 300. Executando o algoritmo, criaríamos um campo de bits com 300 slots e, em seguida, definiríamos repetidamente os bits nos slots 1, 2 e 3, deixando todos os outros slots - 0 e 4 a 299 - estão livres. Ao escanear o campo de bits, encontraríamos o sinalizador no slot 0 livre, então saberíamos que 0 é a resposta.
divegeek
4
Observe que esse algoritmo pode ser compreendido de forma mais simples, sem a manipulação de bits: "Crie um array booleano de tamanho N" etc. Uma vez que você o entenda dessa forma, mover para uma versão bit a bit é conceitualmente fácil.
Jon Skeet,
2
Ao fornecer uma solução abstrata, use a maneira conceitualmente mais simples que funcione e não se especialize demais. Sua solução exige o uso de um array booleano (abstrato), então chame-o assim. O fato de você poder implementar essa matriz por bool[]ou por um bitmap é irrelevante para a solução geral.
Joren,
2
Acho que essa solução pode ser melhor descrita por "Use uma classificação de contagem que desconsidere os elementos acima de N e, em seguida, encontre o primeiro elemento ausente fazendo uma pesquisa linear desde o início."
Joren,
13

Como o OP agora especificou que a lista original é mantida na RAM e que o computador tem apenas, digamos, 1 GB de memória, vou arriscar e prever que a resposta é zero.

1 GB de RAM significa que a lista pode ter no máximo 134.217.728 números. Mas existem 2 64 = 18.446.744.073.709.551.616 números possíveis. Portanto, a probabilidade de que zero esteja na lista é 1 em 137.438.953.472.

Em contraste, minhas chances de ser atingido por um raio este ano são de 1 em 700.000. E minhas chances de ser atingido por um meteorito são de cerca de 1 em 10 trilhões. Portanto, tenho cerca de dez vezes mais probabilidade de ser escrito em um jornal científico devido à minha morte prematura por um objeto celestial do que a resposta não ser zero.

Barry Brown
fonte
11
Seu cálculo só é válido se os valores forem uniformemente distribuídos e selecionados aleatoriamente. Eles poderiam muito bem ter sido gerados sequencialmente.
divegeek de
1
Você está correto, é claro. Mas meu objetivo é otimizar para o caso comum. :)
Barry Brown,
10
Então, quais são as chances de o entrevistado ser selecionado com essa resposta?
Amarghosh,
6
A questão não diz que os números são selecionados uniformemente ao acaso. Eles são selecionados pela pessoa que fez a pergunta. Diante disso, a probabilidade de 0 estar na lista é muito maior do que 1 em 137.438.953.472, provavelmente ainda maior do que 1 em 2. :-)
ShreevatsaR
8
@Amarghosh A resposta a essa pergunta também é zero.
PeterAllenWebb
10

Conforme indicado em outras respostas, você pode fazer uma classificação e, em seguida, simplesmente examinar até encontrar uma lacuna.

Você pode melhorar a complexidade algorítmica para O (N) e manter o espaço O (N) usando um QuickSort modificado, onde você elimina partições que não são candidatas em potencial para conter a lacuna.

  • Na primeira fase de partição, remova as duplicatas.
  • Assim que o particionamento estiver completo, olhe para o número de itens na partição inferior
  • Este valor é igual ao valor usado para criar a partição?
    • Nesse caso, isso significa que a lacuna está na partição superior.
      • Continue com a classificação rápida, ignorando a partição inferior
    • Caso contrário, a lacuna está na partição inferior
      • Continue com a classificação rápida, ignorando a partição superior

Isso economiza um grande número de cálculos.

cdiggins
fonte
Isso é muito bacana. Isso pressupõe que você pode calcular o comprimento da partição em menos do que o tempo linear, o que pode ser feito se armazenado junto com a matriz de partição. Também assume que a lista original é mantida na RAM.
Barry Brown,
2
Se você conhece o comprimento da lista, também pode eliminar quaisquer valores maiores que len (lista). Pelo princípio do escaninho, quaisquer 'buracos' devem ser menores que len (lista).
divegeek
1
Não acho que seja O (n) ... Por um lado, não tenho certeza se você pode remover duplicatas até que uma lista esteja totalmente classificada. Em segundo lugar, embora você possa garantir o descarte de metade do espaço de pesquisa a cada iteração (porque você dividiu em abaixo e acima do ponto médio), você ainda tem várias passagens (dependentes de n) sobre os dados que dependem de n.
paxdiablo
1
paxdiablo: Você pode construir uma nova lista com apenas valores únicos usando um método de bitmap como o que Stephen C propôs. Isso funciona em tempo e espaço O (n). Não tenho certeza se isso pode ser feito melhor do que isso.
Nic de
8

Como todos os números têm 64 bits, podemos usar a ordenação de raiz neles, que é O (n). Classifique-os e examine-os até encontrar o que procura.

se o menor número for zero, avance até encontrar uma lacuna. Se o menor número não for zero, a resposta será zero.

Barry Brown
fonte
É verdade, mas os requisitos de memória podem ficar muito intensos para o tipo de raiz.
PeterAllenWebb
1
A classificação Radix não funciona para conjuntos de dados muito grandes. Mas a classificação por partição e raiz pode funcionar.
DarthVader
8

Para ilustrar uma das armadilhas do O(N)pensamento, aqui está um O(N)algoritmo que usa O(1)espaço.

for i in [0..2^64):
  if i not in list: return i

print "no 64-bit integers are missing"
IJ Kennedy
fonte
1
Will está certo. Isso não é O (n) porque você realmente tem dois loops aqui, mas um está implícito. Determinar se um valor está em uma lista é uma operação O (n), e você está fazendo isso n vezes em seu loop for. Isso o torna O (n ^ 2).
Nic de
6
Nic, Will, é O (n * N) onde n é o tamanho da lista e N é o tamanho do domínio (inteiros de 64 bits). Enquanto N é um número enorme, ainda é uma constante, então formalmente a complexidade do problema conforme declarado é O (n).
Formigas Aasma
1
Formigas, concordo que é O (n N), mas N não é constante. Como o algoritmo termina quando encontra a resposta, o número de iterações completas no loop externo é igual à resposta, que por si só é limitada pelo tamanho da lista. Portanto, O (N n) é O (n ^ 2) neste caso.
Will Harris,
12
Procurar um número em uma lista de N elementos é claramente O (N). Fazemos isso 2 ^ 64 vezes. Embora grande, 2 ^ 64 é uma CONSTANTE. Portanto, o algoritmo é C * O (N), que ainda é O (N).
IJ Kennedy
3
Devo retratar minha declaração anterior; pela definição mais estrita, esta operação é de fato O (n).
Nic de
5

Para um método eficiente de espaço e todos os valores são distintos, você pode fazê-lo no espaço O( k )e no tempo O( k*log(N)*N ). É eficiente em termos de espaço e não há movimentação de dados e todas as operações são elementares (adição e subtração).

  1. conjunto U = N; L=0
  2. Primeiro particione o espaço numérico em kregiões. Como isso:
    • 0->(1/k)*(U-L) + L, 0->(2/k)*(U-L) + L, 0->(3/k)*(U-L) + L...0->(U-L) + L
  3. Descubra quantos números ( count{i}) existem em cada região. ( N*kpassos)
  4. Encontre a primeira região ( h) que não está completa. Isso significa count{h} < upper_limit{h}. ( kpassos)
  5. se h - count{h-1} = 1você tem sua resposta
  6. conjunto U = count{h}; L = count{h-1}
  7. vá para 2

isso pode ser melhorado usando hashing (obrigado por Nic esta ideia).

  1. mesmo
  2. Primeiro particione o espaço numérico em kregiões. Como isso:
    • L + (i/k)->L + (i+1/k)*(U-L)
  3. inc count{j} usando j = (number - L)/k (if L < number < U)
  4. encontre a primeira região ( h) que não contém k elementos
  5. se count{h} = 1h é sua resposta
  6. conjunto U = maximum value in region h L = minimum value in region h

Isso vai entrar em ação O(log(N)*N).

Egon
fonte
Eu realmente gosto dessa resposta. Foi um pouco difícil de ler, mas é muito parecido com o que eu tinha na cabeça quando li a pergunta.
Nic de
também em algum ponto seria inteligente mudar para a solução de bitmap de Stephen C. provavelmente quandoU-L < k
Egon
Isso não funciona em O (log (N) * N), mas em O (N). Sua resposta é uma generalização da resposta @cdiggins e é executada em O (N) porque soma (1 / k ** i para i no intervalo (ceil (log_k (n)))) <= 2.
Lapinot
Em cada iteração que você passa por O (N) números, leva O (log_k (N)) iterações totais. Portanto, O (log_k (N) * N) == O (log (N) * N). Os números originais não são classificados / agrupados e você precisa passar por todos eles.
Egon
Mas se você particionou a lista original em k regiões (de tamanho n / k), então seleciona a primeira região que não está cheia. Portanto, na próxima iteração você só precisa considerar a região selecionada e dividi-la em k novas regiões (de tamanho n / k ** 2) etc. Na verdade você não itera em toda a lista todas as vezes (senão qual é o ponto de particionamento ?).
Lapinot
3

Eu apenas classificaria e, em seguida, percorreria a sequência até encontrar uma lacuna (incluindo a lacuna no início entre zero e o primeiro número).

Em termos de algoritmo, algo como isto faria:

def smallest_not_in_list(list):
    sort(list)
    if list[0] != 0:
        return 0
    for i = 1 to list.last:
        if list[i] != list[i-1] + 1:
            return list[i-1] + 1
    if list[list.last] == 2^64 - 1:
        assert ("No gaps")
    return list[list.last] + 1

Obviamente, se você tiver muito mais memória do que a CPU grunhida, poderá criar uma máscara de bits de todos os valores possíveis de 64 bits e apenas definir os bits para cada número da lista. Em seguida, procure o primeiro bit 0 nessa máscara de bits. Isso a transforma em uma operação O (n) em termos de tempo, mas muito cara em termos de requisitos de memória :-)

Duvido que você possa melhorar em O (n), pois não vejo uma maneira de fazer isso que não envolva olhar para cada número pelo menos uma vez.

O algoritmo para isso seria ao longo das linhas de:

def smallest_not_in_list(list):
    bitmask = mask_make(2^64) // might take a while :-)
    mask_clear_all (bitmask)
    for i = 1 to list.last:
        mask_set (bitmask, list[i])
    for i = 0 to 2^64 - 1:
        if mask_is_clear (bitmask, i):
            return i
    assert ("No gaps")
paxdiablo
fonte
Pela descrição, parece excluir 0 ao primeiro elemento, pois é o menor que não está na lista. Mas, essa é uma suposição que fiz, posso estar errado.
James Black,
Meus pensamentos eram que, se a sequência classificada fosse 4,5,6, então 0 seria a menor que não está na lista.
paxdiablo
Espero que 2, 3, 5, a resposta seja 4, mas posso estar errado.
James Black,
Uma pergunta que deve ser respondida pelo OP. O espaço de pesquisa é "todos os inteiros sem sinal de 64 bits" ou "todos os números entre os mais baixos e mais altos da lista"?
paxdiablo
Concordo que, no pior caso, você deve olhar pelo menos uma vez, a menos que já tenha sido classificado em uma árvore binária, talvez.
James Black,
2

Classifique a lista, observe o primeiro e o segundo elementos e comece a subir até que haja uma lacuna.

James Black
fonte
Depende de como você define, não está na lista.
James Black,
@PeterAllenWebb - Haverá, mas os números estão em ordem aleatória ou classificados?
James Black,
1

Você pode fazer isso em tempo O (n) e espaço adicional O (1), embora o fator oculto seja muito grande. Esta não é uma maneira prática de resolver o problema, mas pode ser interessante mesmo assim.

Para cada inteiro não assinado de 64 bits (em ordem crescente), itere na lista até encontrar o inteiro de destino ou chegar ao final da lista. Se você chegar ao final da lista, o inteiro de destino é o menor inteiro que não está na lista. Se você chegar ao final dos inteiros de 64 bits, todos os inteiros de 64 bits estarão na lista.

Aqui está como uma função Python:

def smallest_missing_uint64(source_list):
    the_answer = None

    target = 0L
    while target < 2L**64:

        target_found = False
        for item in source_list:
            if item == target:
                target_found = True

        if not target_found and the_answer is None:
            the_answer = target

        target += 1L

    return the_answer

Esta função é deliberadamente ineficiente para mantê-lo O (n). Observe especialmente que a função continua verificando os inteiros alvo mesmo depois que a resposta for encontrada. Se a função retornasse assim que a resposta fosse encontrada, o número de vezes que o loop externo funcionava seria limitado pelo tamanho da resposta, que é limitado por n. Essa mudança tornaria o tempo de execução O (n ^ 2), embora fosse muito mais rápido.

Will Harris
fonte
Verdade. É engraçado como alguns dos algoritmos que são O (1) espaço e O (n) tempo falham na prática com essa questão.
PeterAllenWebb
1

Agradeço a egon, swilden e Stephen C pela minha inspiração. Primeiro, sabemos os limites do valor da meta porque ele não pode ser maior do que o tamanho da lista. Além disso, uma lista de 1 GB pode conter no máximo 134217728 (128 * 2 ^ 20) inteiros de 64 bits.

Parte de hash
Proponho usar hashing para reduzir drasticamente nosso espaço de busca. Primeiro, faça a raiz quadrada do tamanho da lista. Para uma lista de 1 GB, isso é N = 11.586. Configure uma matriz de inteiros de tamanho N. Repita a lista e tire a raiz quadrada * de cada número que encontrar como seu hash. Em sua tabela de hash, incremente o contador desse hash. Em seguida, itere em sua tabela de hash. O primeiro intervalo que você descobrir que não é igual ao tamanho máximo define seu novo espaço de pesquisa.

Parte do bitmap
Agora configure um bitmap regular igual ao tamanho do seu novo espaço de pesquisa e, novamente, itere pela lista de origem, preenchendo o bitmap à medida que encontra cada número em seu espaço de pesquisa. Quando terminar, o primeiro bit não definido em seu bitmap lhe dará sua resposta.

Isso será concluído em tempo O (n) e espaço O (sqrt (n)).

(* Você poderia usar algo como deslocamento de bits para fazer isso com muito mais eficiência e apenas variar o número e o tamanho dos intervalos de acordo.)

Nic
fonte
1
Gosto da ideia de dividir o espaço de pesquisa em depósitos Root-N para reduzir o consumo de memória, mas as duplicatas na lista quebrariam esse método. Eu me pergunto se isso pode ser consertado.
PeterAllenWebb
Você está certo, esqueci de considerar as entradas duplicadas. Não tenho certeza se isso pode ser contornado.
Nic de
1

Bem, se houver apenas um número ausente em uma lista de números, a maneira mais fácil de encontrar o número ausente é somar a série e subtrair cada valor na lista. O valor final é o número que falta.

Jeff Lundstrom
fonte
Sim. Essa é outra pergunta clássica da entrevista.
PeterAllenWebb
1
Ainda mais fácil do que isso é XOR os números na lista juntos, XOR os números no intervalo juntos e XOR os resultados juntos.
John Kurlak
1
 int i = 0;
            while ( i < Array.Length)
            {

                if (Array[i] == i + 1)
                {
                    i++;
                }

                if (i < Array.Length)
                {
                    if (Array[i] <= Array.Length)
                    {//SWap

                        int temp = Array[i];
                        int AnoTemp = Array[temp - 1];
                        Array[temp - 1] = temp;
                        Array[i] = AnoTemp;

                    }
                    else
                       i++;



                }
            }

            for (int j = 0; j < Array.Length; j++)
            {
                if (Array[j] > Array.Length)
                {
                    Console.WriteLine(j + 1);
                    j = Array.Length;
                }
                else
                    if (j == Array.Length - 1)
                        Console.WriteLine("Not Found !!");

            }
        }
rana_stack
fonte
1

Poderíamos usar uma tabela hash para armazenar os números. Assim que todos os números estiverem prontos, execute um contador de 0 até encontrar o mais baixo. Um hash razoavelmente bom faz o hash e o armazenamento em tempo constante e recupera em tempo constante.

for every i in X         // One scan Θ(1)
   hashtable.put(i, i);  // O(1)

low = 0;

while (hashtable.get(i) <> null)   // at most n+1 times
   low++;

print low;

O pior caso se houver nelementos no array, e forem {0, 1, ... n-1}, nesse caso, a resposta será obtida em n, ainda mantendo-o O(n).

Milind C
fonte
1

Aqui está minha resposta escrita em Java:

Ideia Básica: 1- Faça um loop pela matriz jogando fora os números positivos, zeros e negativos duplicados enquanto soma o resto, obtendo também o número positivo máximo, e mantenha os números positivos únicos em um Mapa.

2- Calcule a soma como max * (max + 1) / 2.

3- Encontre a diferença entre as somas calculadas nas etapas 1 e 2

4- Faça um loop novamente de 1 ao mínimo de [soma a diferença, máx.] E retorne o primeiro número que não está no mapa preenchido na etapa 1.

public static int solution(int[] A) {
    if (A == null || A.length == 0) {
        throw new IllegalArgumentException();
    }

    int sum = 0;
    Map<Integer, Boolean> uniqueNumbers = new HashMap<Integer, Boolean>();
    int max = A[0];
    for (int i = 0; i < A.length; i++) {
        if(A[i] < 0) {
            continue;
        }
        if(uniqueNumbers.get(A[i]) != null) {
            continue;
        }
        if (A[i] > max) {
            max = A[i];
        }
        uniqueNumbers.put(A[i], true);
        sum += A[i];
    }
    int completeSum = (max * (max + 1)) /  2;
    for(int j = 1; j <= Math.min((completeSum - sum), max); j++) {
        if(uniqueNumbers.get(j) == null) { //O(1)
            return j;
        }
    }
    //All negative case
    if(uniqueNumbers.isEmpty()) {
        return 1;
    }
    return 0;
}
Rami
fonte
0

Como Stephen C observou com inteligência, a resposta deve ser um número menor do que o comprimento do array. Eu então encontraria a resposta por pesquisa binária. Isso otimiza o pior caso (para que o entrevistador não possa pegá-lo em um cenário patológico 'e se'). Em uma entrevista, indique que você está fazendo isso para otimizar para o pior caso.

A maneira de usar a pesquisa binária é subtrair o número que você está procurando de cada elemento da matriz e verificar se há resultados negativos.

Emilio M Bumachar
fonte
0

Eu gosto da abordagem "acho que zero". Se os números forem aleatórios, zero é altamente provável. Se o "examinador" definir uma lista não aleatória, adicione uma e tente novamente:

LowNum=0
i=0
do forever {
  if i == N then leave /* Processed entire array */
  if array[i] == LowNum {
     LowNum++
     i=0
     }
   else {
     i++
   }
}
display LowNum

O pior caso é n * N com n = N, mas na prática n é altamente provável que seja um número pequeno (por exemplo, 1)

NealB
fonte
0

Não tenho certeza se entendi a pergunta. Mas se para a lista 1,2,3,5,6 e o ​​número ausente for 4, então o número ausente pode ser encontrado em O (n) por: (n + 2) (n + 1) / 2- (n + 1) n / 2

EDIT: desculpe, acho que estava pensando muito rápido na noite passada. De qualquer forma, a segunda parte deve ser substituída por soma (lista), que é de onde vem O (n). A fórmula revela a ideia por trás disso: para n inteiros sequenciais, a soma deve ser (n + 1) * n / 2. Se houver um número ausente, a soma seria igual à soma de (n + 1) inteiros sequenciais menos o número ausente.

Obrigado por apontar o fato de que eu estava colocando algumas peças intermediárias em minha mente.

Codismo
fonte
1
Não vejo, à primeira vista, como isso funcionaria. No seu caso, n = 5 e a fórmula será fixa, não importa o número que esteja faltando.
sisve
Simon: você poderia agora remover o voto negativo de acordo com a minha edição?
Codism
0

Muito bem Formigas Aasma! Eu pensei sobre a resposta por cerca de 15 minutos e, de forma independente, encontrei uma resposta em uma linha de pensamento semelhante à sua:

#define SWAP(x,y) { numerictype_t tmp = x; x = y; y = tmp; }
int minNonNegativeNotInArr (numerictype_t * a, size_t n) {
    int m = n;
    for (int i = 0; i < m;) {
        if (a[i] >= m || a[i] < i || a[i] == a[a[i]]) {
            m--;
            SWAP (a[i], a[m]);
            continue;
        }
        if (a[i] > i) {
            SWAP (a[i], a[a[i]]);
            continue;
        }
        i++;
    }
    return m;
}

m representa "a saída máxima possível atual, dado o que sei sobre as primeiras entradas i e assumindo nada mais sobre os valores até a entrada em m-1".

Este valor de m será retornado apenas se (a [i], ..., a [m-1]) for uma permutação dos valores (i, ..., m-1). Assim, se a [i]> = m ou se a [i] <i ou se a [i] == a [a [i]], sabemos que m é a saída errada e deve ser pelo menos um elemento a menos. Portanto, decrementando me trocando a [i] com a [m] podemos recursar.

Se isso não for verdade, mas a [i]> i, então sabendo que a [i]! = A [a [i]], sabemos que trocar a [i] por a [a [i]] aumentará o número de elementos em seu próprio lugar.

Caso contrário, a [i] deve ser igual a i, caso em que podemos incrementar i sabendo que todos os valores de até e incluindo este índice são iguais ao seu índice.

A prova de que isso não pode entrar em um loop infinito é deixada como um exercício para o leitor. :)

Paul Hsieh
fonte
0

O fragmento Dafny da resposta das formigas mostra por que o algoritmo local pode falhar. A requirespré-condição descreve que os valores de cada item não devem ultrapassar os limites da matriz.

method AntsAasma(A: array<int>) returns (M: int)
  requires A != null && forall N :: 0 <= N < A.Length ==> 0 <= A[N] < A.Length;
  modifies A; 
{
  // Pass 1, move every value to the position of its value
  var N := A.Length;
  var cursor := 0;
  while (cursor < N)
  {
    var target := A[cursor];
    while (0 <= target < N && target != A[target])
    {
        var new_target := A[target];
        A[target] := target;
        target := new_target;
    }
    cursor := cursor + 1;
  }

  // Pass 2, find first location where the index doesn't match the value
  cursor := 0;
  while (cursor < N)
  {
    if (A[cursor] != cursor)
    {
      return cursor;
    }
    cursor := cursor + 1;
  }
  return N;
}

Cole o código no validador com e sem a forall ...cláusula para ver o erro de verificação. O segundo erro é o resultado do verificador não ser capaz de estabelecer uma condição de término para o loop da passagem 1. Provar isso é deixado para alguém que entende melhor a ferramenta.

Pekka
fonte
0

Aqui está uma resposta em Java que não modifica a entrada e usa o tempo O (N) e N bits mais uma pequena sobrecarga constante de memória (onde N é o tamanho da lista):

int smallestMissingValue(List<Integer> values) {
    BitSet bitset = new BitSet(values.size() + 1);
    for (int i : values) {
        if (i >= 0 && i <= values.size()) {
            bitset.set(i);
        }
    }
    return bitset.nextClearBit(0);
}
Dave L.
fonte
0
def solution(A):

index = 0
target = []
A = [x for x in A if x >=0]

if len(A) ==0:
    return 1

maxi = max(A)
if maxi <= len(A):
    maxi = len(A)

target = ['X' for x in range(maxi+1)]
for number in A:
    target[number]= number

count = 1
while count < maxi+1:
    if target[count] == 'X':
        return count
    count +=1
return target[count-1] + 1

Obteve 100% para a solução acima.

Angelo
fonte
0

1) Filtro negativo e zero

2) Classificar / distinguir

3) Visit array

Complexidade : O (N) ou O (N * log (N))

usando Java8

public int solution(int[] A) {
            int result = 1;
    boolean found = false;
    A = Arrays.stream(A).filter(x -> x > 0).sorted().distinct().toArray();
    //System.out.println(Arrays.toString(A));
    for (int i = 0; i < A.length; i++) {
        result = i + 1;
        if (result != A[i]) {
            found = true;
            break;
        }
    }
    if (!found && result == A.length) {
        //result is larger than max element in array
        result++;
    }
    return result;
}
Abdullah Lubbadeh
fonte
0

Um unordered_set pode ser usado para armazenar todos os números positivos, e então podemos iterar de 1 até o comprimento de unordered_set e ver o primeiro número que não ocorre.

int firstMissingPositive(vector<int>& nums) {

    unordered_set<int> fre;
    // storing each positive number in a hash.
    for(int i = 0; i < nums.size(); i +=1)
    {
        if(nums[i] > 0)
            fre.insert(nums[i]);
     }

    int i = 1;
    // Iterating from 1 to size of the set and checking 
    // for the occurrence of 'i'

    for(auto it = fre.begin(); it != fre.end(); ++it)
    {
        if(fre.find(i) == fre.end())
            return i;
        i +=1;
    }

    return i;
}
Mohit Anand
fonte
0

Solução através de javascript básico

var a = [1, 3, 6, 4, 1, 2];

function findSmallest(a) {
var m = 0;
  for(i=1;i<=a.length;i++) {
    j=0;m=1;
    while(j < a.length) {
      if(i === a[j]) {
        m++;
      }
      j++;
    }
    if(m === 1) {
      return i;
    }
  }
}

console.log(findSmallest(a))

Espero que isso ajude alguém.

Mano
fonte
0

Com python não é o mais eficiente, mas correto

#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
import datetime

# write your code in Python 3.6

def solution(A):
    MIN = 0
    MAX = 1000000
    possible_results = range(MIN, MAX)

    for i in possible_results:
        next_value = (i + 1)
        if next_value not in A:
            return next_value
    return 1

test_case_0 = [2, 2, 2]
test_case_1 = [1, 3, 44, 55, 6, 0, 3, 8]
test_case_2 = [-1, -22]
test_case_3 = [x for x in range(-10000, 10000)]
test_case_4 = [x for x in range(0, 100)] + [x for x in range(102, 200)]
test_case_5 = [4, 5, 6]
print("---")
a = datetime.datetime.now()
print(solution(test_case_0))
print(solution(test_case_1))
print(solution(test_case_2))
print(solution(test_case_3))
print(solution(test_case_4))
print(solution(test_case_5))
smentek
fonte
0
def solution(A):
    A.sort()
    j = 1
    for i, elem in enumerate(A):
        if j < elem:
            break
        elif j == elem:
            j += 1
            continue
        else:
            continue
    return j
Orfeu
fonte
0

isso pode ajudar:

0- A is [5, 3, 2, 7];
1- Define B With Length = A.Length;                            (O(1))
2- initialize B Cells With 1;                                  (O(n))
3- For Each Item In A:
        if (B.Length <= item) then B[Item] = -1                (O(n))
4- The answer is smallest index in B such that B[index] != -1  (O(n))
Hamed
fonte
Isso difere da resposta de Stephen C ? Quão?
Barba Cinzenta