Qual é a maneira mais rápida de encontrar o primeiro inteiro (menor) que não existe em uma determinada lista de números inteiros não classificados (e que é maior que o menor valor da lista)?
Minha abordagem primitiva é classificá-los e percorrer a lista, existe uma maneira melhor?
algorithms
search
list
numbers
Fabian Zeindl
fonte
fonte
If you have a question about… •algorithm and data structure concepts
está no tópico IMHO.Respostas:
Supondo que você queira dizer "número inteiro" quando diz "número", você pode usar um vetor de bit de tamanho 2 ^ n, em que n é o número de elementos (digamos que seu intervalo inclua números inteiros entre 1 e 256, use um número 256- bit ou 32 bytes, vetor de bits). Quando você encontrar um número inteiro na posição n do seu intervalo, defina o enésimo bit.
Quando você termina de enumerar a coleção de números inteiros, itera sobre os bits no seu vetor de bits, procurando a posição de qualquer conjunto de bits 0. Eles agora correspondem à posição n dos números inteiros ausentes.
Isso é O (2 * N), portanto O (N) e provavelmente é mais eficiente em termos de memória do que classificar a lista inteira.
fonte
Se você classificar a lista inteira primeiro, garante o pior tempo de execução. Além disso, sua escolha do algoritmo de classificação é crítica.
Aqui está como eu abordaria esse problema:
return
: Você encontrou sua resposta.Aqui está uma visualização de uma classificação de heap .
fonte
Para ser esotérico e "inteligente", no caso especial da matriz com apenas um "furo", você pode tentar uma solução baseada em XOR:
Isso será executado em aproximadamente 2N tempo semelhante à solução de vetor de bits, mas requer menos espaço de memória para qualquer tamanho N> de (int). No entanto, se a matriz tiver vários "orifícios", X será a "soma" XOR de todos os orifícios, o que será difícil ou impossível de separar nos valores reais do orifício. Nesse caso, você volta a algum outro método, como as abordagens "pivot" ou "bitvector" de outras respostas.
Você pode recursar isso também usando algo semelhante ao método de pivô para reduzir ainda mais a complexidade. Reorganize a matriz com base em um ponto de articulação (que será o máximo do lado esquerdo e o mínimo da direita; será trivial encontrar o máximo e o mínimo de toda a matriz durante a rotação). Se o lado esquerdo do pivô tiver um ou mais orifícios, recue somente nesse lado; caso contrário, recuar para o outro lado. Em qualquer ponto em que você possa determinar que há apenas um furo, use o método XOR para encontrá-lo (que deve ser mais barato no geral do que continuar girando até uma coleção de dois elementos com um furo conhecido, que é o caso base para o algoritmo de pivô puro).
fonte
Qual é o intervalo de números que você encontrará? Se esse intervalo não for muito grande, você pode resolver isso com duas varreduras (tempo linear O (n)) usando uma matriz com tantos elementos quanto números, trocando espaço por tempo. Você pode encontrar o intervalo dinamicamente com mais uma varredura. Para reduzir o espaço, você pode atribuir 1 bit a cada número, fornecendo 8 números de armazenamento por byte.
Sua outra opção, que pode ser melhor para cenários iniciais e seria insituitária, em vez de copiar memória, é modificar a classificação para sair mais cedo, se o mínimo encontrado em um passe de digitalização não for 1 a mais que o último min encontrado.
fonte
Não, na verdade não. Como qualquer número ainda não escaneado pode sempre ser um número que preenche um determinado "buraco", não é possível evitar escanear cada número pelo menos uma vez e depois compará-lo com seus possíveis vizinhos. Você provavelmente poderia acelerar as coisas construindo uma árvore binária mais ou menos e percorrendo-a da esquerda para a direita até encontrar um buraco, mas isso é essencialmente da mesma complexidade de tempo da classificação, uma vez que está sendo classificada. E você provavelmente não terá nada mais rápido que o Timsort .
fonte
A maioria das idéias aqui não passa de uma triagem. A versão do bitvector é Bucketsort simples. O tipo de pilha também foi mencionado. Basicamente, tudo se resume a escolher o algoritmo de classificação correto, que depende dos requisitos de tempo / espaço e também do intervalo e do número de elementos.
Na minha opinião, o uso de uma estrutura de heap é provavelmente a solução mais geral (um heap basicamente fornece os menores elementos de maneira eficiente, sem uma classificação completa).
Você também pode analisar abordagens que encontrem os menores números primeiro e depois procurar por um número inteiro maior que isso. Ou você encontra os 5 menores números esperando que o espaço fique vazio.
Todos esses algoritmos têm sua força, dependendo das características de entrada e dos requisitos do programa.
fonte
Uma solução que não usa armazenamento adicional ou assume a largura (32 bits) de números inteiros.
Em uma passagem linear, encontre o menor número. Vamos chamar isso de "min". O (n) complexidade do tempo.
Escolha um elemento dinâmico aleatório e faça uma partição no estilo quicksort.
Se o pivô acabar na posição = ("pivô" - "min"), recue no lado direito da partição, caso contrário, recue no lado esquerdo da partição. A idéia aqui é que, se não houver furos desde o início, o pivô estará na ("pivô" - "min") na posição, portanto, o primeiro furo deve ficar à direita da partição e vice-versa.
A caixa base é uma matriz de 1 elemento e o orifício fica entre esse elemento e o próximo.
A complexidade total esperada do tempo de execução é O (n) (8 * n com as constantes) e o pior caso é O (n ^ 2). A análise da complexidade do tempo para um problema semelhante pode ser encontrada aqui .
fonte
Acredito que criei algo que deve funcionar de maneira geral e eficiente se você não tiver duplicatas * (no entanto, deve ser extensível a qualquer número de furos e a qualquer intervalo de números inteiros).
A idéia por trás desse método é como quicksort, pois encontramos um pivô e o particionamos em volta dele, depois recuamos no (s) lado (s) com um furo. Para ver quais lados têm o furo, encontramos os números mais baixo e mais alto e os comparamos com o pivô e o número de valores desse lado. Digamos que o pivô seja 17 e o número mínimo seja 11. Se não houver furos, deve haver 6 números (11, 12, 13, 14, 15, 16, 17). Se houver 5, sabemos que existe um buraco nesse lado e podemos recuar desse lado para encontrá-lo. Estou tendo problemas para explicar mais claramente do que isso, então vamos dar um exemplo.
Pivô:
15 é o pivô, indicado pelos tubos (
||
). Existem 5 números no lado esquerdo do pivô, como deve haver (15 - 10) e 9 à direita, onde deve haver 10 (25 - 15). Então, nós recursamos no lado direito; notaremos que o limite anterior era 15, caso o buraco esteja adjacente a ele (16).Agora, existem 4 números no lado esquerdo, mas deve haver 5 (21 - 16). Então, recuamos lá e, novamente, notamos o limite anterior (entre colchetes).
O lado esquerdo tem os 2 números corretos (18 - 16), mas o direito tem 1 em vez de 2 (20 - 18). Dependendo de nossas condições finais, poderíamos comparar o número 1 com os dois lados (18, 20) e ver que 19 está faltando ou se repetir mais uma vez:
O lado esquerdo tem um tamanho de zero, com um espaço entre o pivô (20) e o limite anterior (18), então 19 é o furo.
*: Se houver duplicatas, você provavelmente poderá usar um conjunto de hash para removê-las em O (N), mantendo o método geral O (N), mas isso pode levar mais tempo do que usar outro método.
fonte