Une question d'entrevue intéressante qu'un de mes collègues utilise:
Supposons que l'on vous donne une très longue liste non triée d'entiers 64 bits non signés. Comment trouveriez-vous le plus petit entier non négatif qui n'apparaît pas dans la liste?
SUIVI: Maintenant que la solution évidente par le tri a été proposée, pouvez-vous le faire plus rapidement que O (n log n)?
SUIVI: votre algorithme doit s'exécuter sur un ordinateur avec, par exemple, 1 Go de mémoire
CLARIFICATION: La liste est en RAM, bien qu'elle puisse en consommer une grande quantité. On vous donne la taille de la liste, disons N, à l'avance.
Réponses:
Si la structure de données peut être mutée sur place et prend en charge l'accès aléatoire, vous pouvez le faire en temps O (N) et en espace supplémentaire O (1). Parcourez simplement le tableau de manière séquentielle et pour chaque index, écrivez la valeur à l'index de l'index spécifié par valeur, en plaçant de manière récursive toute valeur à cet emplacement à sa place et en jetant les valeurs> N. Ensuite, parcourez à nouveau le tableau à la recherche de l'endroit où la valeur ne correspond pas à l'index - c'est la plus petite valeur ne figurant pas dans le tableau. Il en résulte au plus des comparaisons 3N et n'utilise que quelques valeurs représentant un espace temporaire.
la source
Voici une
O(N)
solution simple qui utilise l'O(N)
espace. Je suppose que nous limitons la liste d'entrée aux nombres non négatifs et que nous voulons trouver le premier nombre non négatif qui n'est pas dans la liste.N
.N
booléens, initialisé à tousfalse
.X
de la liste, siX
est inférieur àN
, définissez l'X'th
élément du tableau surtrue
.0
, à la recherche du premier élémentfalse
. Si vous trouvez le premierfalse
à l'indexI
, alorsI
est la réponse. Sinon (c'est-à-dire lorsque tous les éléments le sonttrue
), la réponse estN
.En pratique, le "tableau de
N
booléens" serait probablement codé comme un "bitmap" ou un "jeu de bits" représenté par un tableaubyte
ouint
. Cela utilise généralement moins d'espace (selon le langage de programmation) et permetfalse
d'effectuer plus rapidement l'analyse du premier .C'est comment / pourquoi l'algorithme fonctionne.
Supposons que les
N
nombres de la liste ne soient pas distincts ou qu'un ou plusieurs d'entre eux soient supérieurs àN
. Cela signifie qu'il doit y avoir au moins un nombre dans la plage0 .. N - 1
qui n'est pas dans la liste. Le problème de trouver le plus petit nombre manquant doit donc se réduire au problème de trouver le plus petit nombre manquant inférieur àN
. Cela signifie que nous n'avons pas besoin de garder une trace des nombres supérieurs ou égaux àN
... car ils ne seront pas la réponse.L'alternative au paragraphe précédent est que la liste est une permutation des nombres de
0 .. N - 1
. Dans ce cas, l'étape 3 définit tous les éléments du tableau surtrue
, et l'étape 4 nous indique que le premier nombre «manquant» estN
.La complexité de calcul de l'algorithme est
O(N)
avec une constante de proportionnalité relativement petite. Il effectue deux passages linéaires dans la liste, ou un seul passage si la longueur de la liste est connue pour commencer. Il n'est pas nécessaire de représenter l'ensemble de la liste en mémoire, de sorte que l'utilisation asymptotique de la mémoire de l'algorithme est exactement ce qui est nécessaire pour représenter le tableau de booléens; c'est-à-dire desO(N)
bits.(En revanche, les algorithmes qui reposent sur le tri ou le partitionnement en mémoire supposent que vous pouvez représenter la liste entière en mémoire. Dans la forme où la question a été posée, cela nécessiterait
O(N)
des mots de 64 bits.)@Jorn commente que les étapes 1 à 3 sont une variante du tri par comptage. Dans un sens, il a raison, mais les différences sont importantes:
Xmax - Xmin
compteurs oùXmax
est le plus grand nombre de la liste etXmin
le plus petit nombre de la liste. Chaque compteur doit pouvoir représenter N états; c'est-à-dire en supposant une représentation binaire, il doit avoir un type entier (au moins)ceiling(log2(N))
bits.Xmax
etXmin
.ceiling(log2(N)) * (Xmax - Xmin)
bits.En revanche, l'algorithme présenté ci-dessus nécessite simplement des
N
bits dans le pire et le meilleur des cas.Cependant, cette analyse conduit à l'intuition que si l'algorithme effectuait un premier passage dans la liste à la recherche d'un zéro (et en comptant les éléments de la liste si nécessaire), il donnerait une réponse plus rapide en utilisant aucun espace du tout s'il trouvait le zéro. Cela vaut vraiment la peine de le faire s'il y a une forte probabilité de trouver au moins un zéro dans la liste. Et cette passe supplémentaire ne change pas la complexité globale.
EDIT: J'ai changé la description de l'algorithme pour utiliser "tableau de booléens" car les gens ont apparemment trouvé ma description originale utilisant des bits et des bitmaps pour être déroutante.
la source
bool[]
ou par un bitmap n'est pas pertinent pour la solution générale.Étant donné que l'OP a maintenant spécifié que la liste d'origine est conservée dans la RAM et que l'ordinateur ne dispose que, disons, de 1 Go de mémoire, je vais prendre des précautions et prédire que la réponse est zéro.
1 Go de RAM signifie que la liste peut contenir au plus 134 217 728 numéros. Mais il y a 2 64 = 18,446,744,073,709,551,616 nombres possibles. Ainsi, la probabilité que zéro soit dans la liste est de 1 sur 137 438 953 472.
En revanche, mes chances d'être frappé par la foudre cette année sont de 1 sur 700 000. Et mes chances d' être touché par une météorite sont d'environ 1 sur 10 billions. Je suis donc environ dix fois plus susceptible d'être écrit dans une revue scientifique en raison de ma mort prématurée par un objet céleste que la réponse n'étant pas zéro.
la source
Comme indiqué dans d'autres réponses, vous pouvez faire un tri, puis simplement scanner jusqu'à ce que vous trouviez un espace.
Vous pouvez améliorer la complexité algorithmique en O (N) et conserver l'espace O (N) en utilisant un QuickSort modifié où vous éliminez les partitions qui ne sont pas des candidats potentiels pour contenir l'écart.
Cela permet d'économiser un grand nombre de calculs.
la source
Pour illustrer l'un des pièges de la
O(N)
réflexion, voici unO(N)
algorithme qui utilise l'O(1)
espace.la source
Puisque les nombres ont tous une longueur de 64 bits, nous pouvons utiliser le tri par base sur eux, qui est O (n). Triez-les, puis scannez-les jusqu'à ce que vous trouviez ce que vous cherchez.
si le plus petit nombre est zéro, scannez vers l'avant jusqu'à ce que vous trouviez un espace. Si le plus petit nombre n'est pas zéro, la réponse est zéro.
la source
Pour une méthode efficace d'espace et toutes les valeurs sont distinctes, vous pouvez le faire dans l'espace
O( k )
et le tempsO( k*log(N)*N )
. C'est peu encombrant et il n'y a pas de déplacement de données et toutes les opérations sont élémentaires (ajout de soustraction).U = N; L=0
k
régions. Comme ça:0->(1/k)*(U-L) + L
,0->(2/k)*(U-L) + L
,0->(3/k)*(U-L) + L
...0->(U-L) + L
count{i}
) dans chaque région. (N*k
étapes)h
) qui n'est pas pleine. Cela veut direcount{h} < upper_limit{h}
. (k
étapes)h - count{h-1} = 1
tu as ta réponseU = count{h}; L = count{h-1}
cela peut être amélioré en utilisant le hachage (merci pour Nic cette idée).
k
régions. Comme ça:L + (i/k)->L + (i+1/k)*(U-L)
inc count{j}
en utilisantj = (number - L)/k
(if L < number < U)
h
) qui ne contient pas k élémentscount{h} = 1
h est ta réponseU = maximum value in region h
L = minimum value in region h
Cela fonctionnera
O(log(N)*N)
.la source
U-L < k
Je les trierais simplement puis je parcourais la séquence jusqu'à ce que je trouve un espace (y compris l'écart au début entre zéro et le premier nombre).
En termes d'algorithme, quelque chose comme ça le ferait:
Bien sûr, si vous avez beaucoup plus de mémoire que CPU grunt, vous pouvez créer un masque de bits de toutes les valeurs 64 bits possibles et définir simplement les bits pour chaque nombre de la liste. Recherchez ensuite le premier 0 bit dans ce masque de bits. Cela en fait une opération O (n) en termes de temps mais assez chère en termes de besoins en mémoire :-)
Je doute que vous puissiez améliorer O (n) car je ne vois pas de moyen de le faire qui n'implique pas de regarder chaque nombre au moins une fois.
L'algorithme pour celui-là serait du type:
la source
Triez la liste, regardez les premier et deuxième éléments et commencez à remonter jusqu'à ce qu'il y ait un espace.
la source
Vous pouvez le faire dans un temps O (n) et un espace supplémentaire O (1), bien que le facteur caché soit assez important. Ce n'est pas un moyen pratique de résoudre le problème, mais cela peut néanmoins être intéressant.
Pour chaque entier 64 bits non signé (par ordre croissant), parcourez la liste jusqu'à ce que vous trouviez l'entier cible ou que vous atteigniez la fin de la liste. Si vous atteignez la fin de la liste, l'entier cible est le plus petit entier ne figurant pas dans la liste. Si vous atteignez la fin des entiers 64 bits, chaque entier 64 bits est dans la liste.
Le voici en tant que fonction Python:
Cette fonction est volontairement inefficace pour la garder O (n). Notez en particulier que la fonction continue de vérifier les entiers cibles même après avoir trouvé la réponse. Si la fonction retournait dès que la réponse était trouvée, le nombre d'exécutions de la boucle externe serait lié par la taille de la réponse, qui est liée par n. Ce changement rendrait le temps d'exécution O (n ^ 2), même si ce serait beaucoup plus rapide.
la source
Merci à egon, swilden et Stephen C pour mon inspiration. Tout d'abord, nous connaissons les limites de la valeur de l'objectif car elle ne peut pas être supérieure à la taille de la liste. En outre, une liste de 1 Go peut contenir au plus 134217728 (128 * 2 ^ 20) entiers 64 bits.
Partie de hachage
Je propose d'utiliser le hachage pour réduire considérablement notre espace de recherche. Tout d'abord, racine carrée la taille de la liste. Pour une liste de 1 Go, c'est N = 11586. Configurez un tableau d'entiers de taille N. Parcourez la liste et prenez la racine carrée * de chaque nombre que vous trouvez comme votre hachage. Dans votre table de hachage, incrémentez le compteur de ce hachage. Ensuite, parcourez votre table de hachage. Le premier compartiment que vous trouvez qui n'est pas égal à sa taille maximale définit votre nouvel espace de recherche.
Partie bitmap
maintenant régulière égale à la taille de votre nouvel espace de recherche, puis parcourez à nouveau la liste source, en remplissant la bitmap lorsque vous trouvez chaque numéro dans votre espace de recherche. Lorsque vous avez terminé, le premier bit non défini dans votre bitmap vous donnera votre réponse.
Ceci sera complété en temps O (n) et en espace O (sqrt (n)).
(* Vous pouvez utiliser quelque chose comme le décalage de bits pour le faire beaucoup plus efficacement, et simplement varier le nombre et la taille des seaux en conséquence.)
la source
Eh bien, s'il n'y a qu'un seul nombre manquant dans une liste de nombres, le moyen le plus simple de trouver le nombre manquant est de additionner la série et de soustraire chaque valeur de la liste. La valeur finale est le nombre manquant.
la source
la source
Nous pourrions utiliser une table de hachage pour contenir les nombres. Une fois tous les nombres terminés, lancez un compteur de 0 jusqu'à ce que nous trouvions le plus bas. Un hachage raisonnablement bon hachera et stockera en temps constant, et récupère en temps constant.
Le pire des cas s'il y a des
n
éléments dans le tableau et le sont{0, 1, ... n-1}
, auquel cas, la réponse sera obtenue àn
, en la conservant toujoursO(n)
.la source
Voici ma réponse écrite en Java:
Idée de base: 1- Faites une boucle dans le tableau en jetant les nombres positifs, zéros et négatifs en double tout en résumant le reste, en obtenant également le nombre positif maximum et en conservant les nombres positifs uniques dans une carte.
2- Calculez la somme comme max * (max + 1) / 2.
3- Trouvez la différence entre les sommes calculées aux étapes 1 et 2
4- Bouclez à nouveau de 1 au minimum de [somme différence, max] et retournez le premier nombre qui ne se trouve pas dans la carte renseignée à l'étape 1.
la source
Comme Stephen C l'a intelligemment souligné, la réponse doit être un nombre inférieur à la longueur du tableau. Je trouverais alors la réponse par recherche binaire. Cela optimise le pire des cas (de sorte que l'intervieweur ne peut pas vous attraper dans un scénario pathologique «et si»). Dans une interview, indiquez que vous faites cela pour optimiser le pire des cas.
La façon d'utiliser la recherche binaire est de soustraire le nombre que vous recherchez de chaque élément du tableau et de vérifier les résultats négatifs.
la source
J'aime l'approche "suppose zéro". Si les nombres étaient aléatoires, zéro est hautement probable. Si «l'examinateur» a défini une liste non aléatoire, ajoutez-en une et devinez à nouveau:
Le pire des cas est n * N avec n = N, mais en pratique n est très probablement un petit nombre (par exemple 1)
la source
Je ne sais pas si j'ai bien compris la question. Mais si pour la liste 1,2,3,5,6 et que le nombre manquant est 4, alors le nombre manquant peut être trouvé dans O (n) par: (n + 2) (n + 1) / 2- (n + 1) n / 2
EDIT: désolé, je suppose que je pensais trop vite la nuit dernière. Quoi qu'il en soit, la deuxième partie devrait en fait être remplacée par sum (list), qui est là où O (n) vient. La formule révèle l'idée derrière elle: pour n entiers séquentiels, la somme doit être (n + 1) * n / 2. S'il manque un nombre, la somme sera égale à la somme de (n + 1) entiers séquentiels moins le nombre manquant.
Merci d'avoir souligné le fait que je mettais quelques pièces du milieu dans mon esprit.
la source
Bravo aux fourmis Aasma! J'ai réfléchi à la réponse pendant environ 15 minutes et je suis parvenu indépendamment à une réponse dans la même veine que la vôtre:
m représente "la sortie maximale possible actuelle étant donné ce que je sais des i premières entrées et en supposant rien d'autre sur les valeurs jusqu'à l'entrée à m-1".
Cette valeur de m ne sera retournée que si (a [i], ..., a [m-1]) est une permutation des valeurs (i, ..., m-1). Ainsi, si a [i]> = m ou si a [i] <i ou si a [i] == a [a [i]] nous savons que m est la mauvaise sortie et doit être au moins un élément plus bas. Donc, en décrémentant m et en échangeant un [i] avec le a [m], nous pouvons récurer.
Si ce n'est pas vrai mais a [i]> i alors sachant que a [i]! = A [a [i]] nous savons que permuter a [i] avec a [a [i]] augmentera le nombre d'éléments à leur place.
Sinon a [i] doit être égal à i auquel cas on peut incrémenter i sachant que toutes les valeurs jusqu'à et y compris cet indice sont égales à leur indice.
La preuve que cela ne peut pas entrer dans une boucle infinie est laissée comme exercice au lecteur. :)
la source
Le fragment Dafny de la réponse d'Ants montre pourquoi l'algorithme en place peut échouer. La
requires
pré-condition décrit que les valeurs de chaque élément ne doivent pas dépasser les limites du tableau.Collez le code dans le validateur avec et sans la
forall ...
clause pour voir l'erreur de vérification. La deuxième erreur est due au fait que le vérificateur ne peut pas établir une condition de fin pour la boucle Pass 1. Cela revient à quelqu'un qui comprend mieux l'outil.la source
Voici une réponse en Java qui ne modifie pas l'entrée et utilise le temps O (N) et N bits plus une petite surcharge constante de mémoire (où N est la taille de la liste):
la source
Obtenu 100% pour la solution ci-dessus.
la source
1) Filtre négatif et zéro
2) Trier / distinguer
3) Visitez le tableau
Complexité : O (N) ou O (N * log (N))
en utilisant Java8
la source
Un unordered_set peut être utilisé pour stocker tous les nombres positifs, puis nous pouvons itérer de 1 à la longueur de unordered_set, et voir le premier nombre qui ne se produit pas.
la source
Solution via javascript de base
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))
J'espère que cela aide quelqu'un.
la source
Avec python ce n'est pas le plus efficace, mais correct
la source
la source
cela peut aider:
la source