J'ai récemment assisté à une interview où on m'a demandé «d'écrire un programme pour trouver les 100 plus grands nombres sur un tableau de 1 milliard de nombres».
Je n'ai pu donner qu'une solution de force brute qui consistait à trier le tableau en complexité temporelle O (nlogn) et à prendre les 100 derniers nombres.
Arrays.sort(array);
L'intervieweur cherchait une meilleure complexité temporelle, j'ai essayé quelques autres solutions mais je n'ai pas réussi à lui répondre. Existe-t-il une meilleure solution de complexité temporelle?
O(1)
dans ce cas, car il n'y a pas d'augmentation de dimension. L'intervieweur aurait dû demander "Comment trouver m les plus grands éléments d'un tableau de n avec n >> m?".Réponses:
Vous pouvez conserver une file d'attente prioritaire des 100 plus grands nombres, parcourir le milliard de nombres, chaque fois que vous rencontrez un nombre supérieur au plus petit nombre dans la file d'attente (le tête de la file d'attente), supprimer le tête de la file d'attente et ajouter le nouveau numéro à la file d'attente.
EDIT: comme l'a noté Dev, avec une file d'attente prioritaire implémentée avec un tas, la complexité de l'insertion dans la file d'attente est
O(logN)
Dans le pire des cas, vous obtenez ce qui est mieux que
billionlog2(100)
billion
log2(billion)
En général, si vous avez besoin des plus grands nombres K d'un ensemble de N nombres, la complexité est
O(NlogK)
plutôt queO(NlogN)
, cela peut être très important lorsque K est très petit par rapport à N.EDIT2:
Le temps attendu de cet algorithme est assez intéressant, car à chaque itération une insertion peut ou non se produire. La probabilité que le ième nombre soit inséré dans la file d'attente est la probabilité qu'une variable aléatoire soit plus grande qu'au moins
i-K
des variables aléatoires de la même distribution (les k premiers nombres sont automatiquement ajoutés à la file d'attente). Nous pouvons utiliser des statistiques de commande (voir lien ) pour calculer cette probabilité. Par exemple, supposons que les nombres ont été sélectionnés aléatoirement de manière uniforme{0, 1}
, la valeur attendue du (iK) ème nombre (parmi i nombres) est(i-k)/i
, et le risque qu'une variable aléatoire soit plus grande que cette valeur est1-[(i-k)/i] = k/i
.Ainsi, le nombre d'insertions attendu est:
Et le temps de fonctionnement prévu peut être exprimé comme suit:
(le
k
temps de générer la file d'attente avec les premiersk
éléments, puis lesn-k
comparaisons et le nombre prévu d'insertions comme décrit ci-dessus, chacun prend unlog(k)/2
temps moyen )Notez que lorsque
N
est très grand par rapport àK
, cette expression est beaucoup plus proche den
plutôt que deNlogK
. C'est quelque peu intuitif, car dans le cas de la question, même après 10000 itérations (ce qui est très petit par rapport à un milliard), la chance qu'un nombre soit inséré dans la file d'attente est très faible.la source
k
constant et petit par rapport àn
. Cependant, il faut toujours garder à l'esprit ces "circonstances normales".Si cela est demandé dans une interview, je pense que l'intervieweur veut probablement voir votre processus de résolution de problèmes, pas seulement votre connaissance des algorithmes.
La description est assez générale, alors vous pouvez peut-être lui demander la plage ou la signification de ces chiffres pour clarifier le problème. Cela peut impressionner un intervieweur. Si, par exemple, ces chiffres représentent l'âge des personnes à l'intérieur d'un pays (par exemple la Chine), alors c'est un problème beaucoup plus facile. En supposant raisonnablement que personne en vie n'a plus de 200 ans, vous pouvez utiliser un tableau int de taille 200 (peut-être 201) pour compter le nombre de personnes du même âge en une seule itération. Ici, l'indice signifie l'âge. Après cela, c'est un morceau de gâteau pour trouver 100 plus grand nombre. D'ailleurs, cet algo est appelé tri de comptage .
Quoi qu'il en soit, rendre la question plus précise et plus claire est bon pour vous lors d'une interview.
la source
Vous pouvez parcourir les nombres qui prennent O (n)
Chaque fois que vous trouvez une valeur supérieure au minimum actuel, ajoutez la nouvelle valeur à une file d'attente circulaire de taille 100.
Le min de cette file d'attente circulaire est votre nouvelle valeur de comparaison. Continuez à ajouter à cette file d'attente. S'il est plein, extrayez le minimum de la file d'attente.
la source
Je me suis rendu compte que cela est étiqueté avec «algorithme», mais jetterai d'autres options, car il devrait probablement également être étiqueté «interview».
Quelle est la source du milliard de chiffres? S'il s'agit d'une base de données, «sélectionner la valeur dans l'ordre des tables en fonction de la valeur de desc limit 100» ferait très bien l'affaire - il pourrait y avoir des différences de dialecte.
S'agit-il d'un cas unique ou de quelque chose qui se répétera? Si répété, à quelle fréquence? S'il s'agit d'une donnée unique et que les données sont dans un fichier, alors 'cat srcfile | trier (options selon les besoins) | head -100 'vous permettra de faire rapidement un travail productif pour lequel vous êtes payé pendant que l'ordinateur gère cette corvée insignifiante.
Si elle se répète, vous conseillerez de choisir une approche décente pour obtenir la réponse initiale et de stocker / mettre en cache les résultats afin que vous puissiez continuellement signaler les 100 premiers.
Enfin, il y a cette considération. Êtes-vous à la recherche d'un emploi d'entrée de gamme et d'un entretien avec un manager geek ou un futur collègue? Si oui, alors vous pouvez lancer toutes sortes d'approches décrivant les avantages et les inconvénients techniques relatifs. Si vous recherchez un emploi plus managérial, abordez-le comme un gestionnaire, soucieux des coûts de développement et de maintenance de la solution, et dites "merci beaucoup" et partez si tel est le cas, l'intervieweur souhaite se concentrer sur les anecdotes CS . Lui et vous n'auriez probablement pas beaucoup de potentiel d'avancement là-bas.
Bonne chance pour la prochaine interview.
la source
Ma réaction immédiate serait d'utiliser un tas, mais il existe un moyen d'utiliser QuickSelect sans garder toutes les valeurs d'entrée à portée de main à tout moment.
Créez un tableau de taille 200 et remplissez-le avec les 200 premières valeurs d'entrée. Exécutez QuickSelect et jetez les 100 bas, vous laissant 100 places libres. Lisez les 100 valeurs d'entrée suivantes et réexécutez QuickSelect. Continuez jusqu'à ce que vous ayez exécuté l'intégralité de l'entrée par lots de 100.
À la fin, vous avez les 100 premières valeurs. Pour N valeurs, vous avez exécuté QuickSelect environ N / 100 fois. Chaque Quickselect coûte environ 200 fois une constante, donc le coût total est 2N fois une constante. Cela semble linéaire dans la taille de l'entrée pour moi, quelle que soit la taille du paramètre que je suis câblé pour être de 100 dans cette explication.
la source
partial_sort
exécutée directement sur un ensemble de données de 200 millions 32 bitsint
(créé via un MT19937, uniformément distribué).Ordering.greatestOf(Iterable, int)
. C'est un temps absolument linéaire et en un seul passage, et c'est un algorithme super mignon. FWIW, nous avons également des repères réels: ses facteurs constants sont un cheveu plus lents que la file d'attente prioritaire traditionnelle dans le cas moyen, mais cette implémentation est beaucoup plus résistante aux entrées du "pire des cas" (par exemple, les entrées strictement ascendantes).Vous pouvez utiliser l' algorithme de sélection rapide pour trouver le nombre à l'index (par ordre) [milliards-101], puis parcourir les nombres et trouver les nombres supérieurs à ce nombre.
Cet algorithme Temps est: 2 XO (N) = O (N) (performance moyenne du cas)
La deuxième option comme le suggère Thomas Jungblut est:
Utilisez la construction de tas , le tas MAX prendra O (N), puis les 100 premiers nombres max seront en haut du tas, tout ce dont vous avez besoin est de les extraire du tas (100 XO (Log (N)).
Cet algorithme Time est: O (N) + 100 XO (Log (N)) = O (N)
la source
O(N)
, faire deux QuickSelects et un autre balayage linéaire est bien plus lourd que nécessaire.100*O(N)
(si c'est une syntaxe valide) =O(100*N)
=O(N)
(certes, 100 peut être variable, si c'est le cas, ce n'est pas strictement vrai). Oh, et Quickselect a la pire performance de O (N ^ 2) (aïe). Et s'il ne tient pas dans la mémoire, vous rechargerez les données du disque deux fois, ce qui est bien pire qu'une fois (c'est le goulot d'étranglement).Bien que l'autre solution quickselect ait été déclassée, le fait demeure que quickselect trouvera la solution plus rapidement que l'utilisation d'une file d'attente de taille 100. Quickselect a un temps d'exécution prévu de 2n + o (n), en termes de comparaisons. Une mise en œuvre très simple serait
Cela prendra en moyenne 3n + o (n) comparaisons. De plus, il peut être rendu plus efficace en utilisant le fait que la sélection rapide laissera les 100 plus grands éléments du tableau dans les 100 emplacements les plus à droite. Donc en fait, le temps de fonctionnement peut être amélioré à 2n + o (n).
Il y a le problème que c'est le temps d'exécution prévu, et non le pire des cas, mais en utilisant une stratégie de sélection de pivot décente (par exemple, choisir 21 éléments au hasard, et choisir la médiane de ces 21 comme pivot), alors le nombre de comparaisons peut être garanti avec une probabilité élevée d'être au plus (2 + c) n pour une constante arbitrairement petite c.
En fait, en utilisant une stratégie d'échantillonnage optimisée (par exemple, échantillonner des éléments sqrt (n) au hasard, et choisir le 99e centile), le temps d'exécution peut être ramené à (1 + c) n + o (n) pour arbitrairement petit c (en supposant que K, le nombre d'éléments à sélectionner est o (n)).
D'un autre côté, l'utilisation d'une file d'attente de taille 100 nécessitera des comparaisons O (log (100) n), et la base de log 2 de 100 est approximativement égale à 6,6.
Si nous pensons à ce problème dans le sens plus abstrait de choisir les plus grands éléments K dans un tableau de taille N, où K = o (N) mais K et N vont à l'infini, alors le temps d'exécution de la version de sélection rapide sera O (N) et la version de file d'attente sera O (N log K), donc dans ce sens, la sélection rapide est également asymptotiquement supérieure.
Dans les commentaires, il a été mentionné que la solution de file d'attente s'exécutera dans le temps prévu N + K log N sur une entrée aléatoire. Bien sûr, l'hypothèse d'entrée aléatoire n'est jamais valide à moins que la question ne l'énonce explicitement. La solution de file d'attente pourrait être faite pour traverser le tableau dans un ordre aléatoire, mais cela entraînera le coût supplémentaire de N appels vers un générateur de nombres aléatoires ainsi que soit en permutant l'ensemble du tableau d'entrée, soit en allouant un nouveau tableau de longueur N contenant le indices aléatoires.
Si le problème ne vous permet pas de vous déplacer dans les éléments du tableau d'origine et que le coût d'allocation de mémoire est élevé, la duplication du tableau n'est pas une option, c'est une autre affaire. Mais strictement en termes de durée de fonctionnement, c'est la meilleure solution.
la source
prendre les 100 premiers chiffres du milliard et les trier. maintenant, parcourez simplement le milliard, si le nombre source est supérieur au plus petit des 100, insérez-les dans l'ordre de tri. Ce que vous obtenez est quelque chose de beaucoup plus proche de O (n) que de la taille de l'ensemble.
la source
Deux options:
(1) Tas (PriorityQueue)
Conservez un min-tas d'une taille de 100. Parcourez le tableau. Une fois que l'élément est plus petit que le premier élément du tas, remplacez-le.
(2) Modèle de réduction de carte.
Ceci est très similaire à l'exemple du nombre de mots dans hadoop. Travail de carte: comptez la fréquence ou le temps d'apparition de chaque élément. Réduire: Obtenez l'élément K supérieur.
Habituellement, je donnais au recruteur deux réponses. Donnez-leur ce qu'ils veulent. Bien sûr, le codage de réduction de la carte serait laborieux car vous devez connaître tous les paramètres exacts. Pas de mal à le pratiquer. Bonne chance.
la source
Une solution très simple serait de parcourir le tableau 100 fois. Ce qui est
O(n)
.Chaque fois que vous retirez le plus grand nombre (et modifiez sa valeur à la valeur minimale, de sorte que vous ne le voyez pas dans l'itération suivante, ou gardez une trace des index des réponses précédentes (en gardant une trace des index que le tableau d'origine peut avoir) multiple du même nombre)). Après 100 itérations, vous avez les 100 plus grands nombres.
la source
Inspiré par la réponse de @ron teller, voici un programme C barebones pour faire ce que vous voulez.
Sur ma machine (Core i3 avec un SSD rapide), cela prend 25 secondes et 1724 tris. J'ai généré un fichier binaire avec
dd if=/dev/urandom/ count=1000000000 bs=1
pour cette course.Évidemment, il y a des problèmes de performances avec la lecture de seulement 4 octets à la fois - à partir du disque, mais c'est par exemple. Du côté positif, très peu de mémoire est nécessaire.
la source
La solution la plus simple consiste à analyser le grand tableau de milliards de chiffres et à conserver les 100 plus grandes valeurs trouvées jusqu'à présent dans un petit tampon de tableau sans aucun tri et à mémoriser la plus petite valeur de ce tampon. J'ai d'abord pensé que cette méthode avait été proposée par fordprefect mais dans un commentaire, il a dit qu'il supposait que la structure de données à 100 nombres était implémentée comme un tas. Chaque fois qu'un nouveau nombre est trouvé qui est plus grand, le minimum dans le tampon est écrasé par la nouvelle valeur trouvée et le tampon est à nouveau recherché pour le minimum actuel. Si les nombres en milliards de tableaux de nombres sont distribués de façon aléatoire la plupart du temps, la valeur du grand tableau est comparée au minimum du petit tableau et jetée. Seulement pour une très petite fraction du nombre, la valeur doit être insérée dans le petit tableau. Ainsi, la différence de manipulation de la structure de données contenant les petits nombres peut être négligée. Pour un petit nombre d'éléments, il est difficile de déterminer si l'utilisation d'une file d'attente prioritaire est en fait plus rapide que mon approche naïve.
Je veux estimer le nombre d'insertions dans le petit tampon de tableau à 100 éléments lorsque le tableau à 10 ^ 9 éléments est analysé. Le programme scanne les 1000 premiers éléments de ce grand tableau et doit insérer au plus 1000 éléments dans le tampon. Le tampon contient 100 éléments sur les 1000 éléments analysés, soit 0,1 de l'élément analysé. Nous supposons donc que la probabilité qu'une valeur du grand tableau soit supérieure au minimum actuel du tampon est d'environ 0,1. Un tel élément doit être inséré dans le tampon. Maintenant, le programme analyse les 10 ^ 4 éléments suivants du grand tableau. Parce que le minimum du tampon augmentera à chaque fois qu'un nouvel élément est inséré. Nous avons estimé que le rapport des éléments supérieurs à notre minimum actuel est d'environ 0,1 et il y a donc 0,1 * 10 ^ 4 = 1000 éléments à insérer. En fait, le nombre attendu d'éléments insérés dans le tampon sera plus petit. Après l'analyse de cette fraction de 10 ^ 4 éléments des nombres dans le tampon, il y aura environ 0,01 des éléments analysés jusqu'à présent. Ainsi, lors de la numérisation des 10 ^ 5 prochains nombres, nous supposons que pas plus de 0,01 * 10 ^ 5 = 1000 seront insérés dans le tampon. Poursuivant cette argumentation, nous avons inséré environ 7000 valeurs après avoir analysé 1000 + 10 ^ 4 + 10 ^ 5 + ... + 10 ^ 9 ~ 10 ^ 9 éléments du grand tableau. Ainsi, lors de la numérisation d'un tableau avec 10 ^ 9 éléments de taille aléatoire, nous n'attendons pas plus de 10 ^ 4 (= 7000 arrondis) insertions dans le tampon. Après chaque insertion dans le tampon, le nouveau minimum doit être trouvé. Si le tampon est un simple tableau, nous avons besoin d'une comparaison de 100 pour trouver le nouveau minimum. Si le tampon est une autre structure de données (comme un tas), nous avons besoin d'au moins 1 comparaison pour trouver le minimum. Pour comparer les éléments du grand tableau, nous avons besoin de 10 ^ 9 comparaisons. Donc, dans l'ensemble, nous avons besoin d'environ 10 ^ 9 + 100 * 10 ^ 4 = 1.001 * 10 ^ 9 comparaisons lors de l'utilisation d'un tableau comme tampon et d'au moins 1.000 * 10 ^ 9 comparaisons lors de l'utilisation d'un autre type de structure de données (comme un tas) . Ainsi, l'utilisation d'un segment n'apporte qu'un gain de 0,1% si les performances sont déterminées par le nombre de comparaison. Mais quelle est la différence de temps d'exécution entre l'insertion d'un élément dans un tas de 100 éléments et le remplacement d'un élément dans un tableau de 100 éléments et la recherche de son nouveau minimum? 000 * 10 ^ 9 comparaisons lors de l'utilisation d'un autre type de structure de données (comme un tas). Ainsi, l'utilisation d'un segment n'apporte qu'un gain de 0,1% si les performances sont déterminées par le nombre de comparaison. Mais quelle est la différence de temps d'exécution entre l'insertion d'un élément dans un tas de 100 éléments et le remplacement d'un élément dans un tableau de 100 éléments et la recherche de son nouveau minimum? 000 * 10 ^ 9 comparaisons lors de l'utilisation d'un autre type de structure de données (comme un tas). Ainsi, l'utilisation d'un segment n'apporte qu'un gain de 0,1% si les performances sont déterminées par le nombre de comparaison. Mais quelle est la différence de temps d'exécution entre l'insertion d'un élément dans un tas de 100 éléments et le remplacement d'un élément dans un tableau de 100 éléments et la recherche de son nouveau minimum?
Au niveau théorique: combien de comparaisons sont nécessaires pour insérer dans un tas. Je sais que c'est O (log (n)) mais quelle est la valeur du facteur constant? je
Au niveau de la machine: quel est l'impact de la mise en cache et de la prédiction de branche sur le temps d'exécution d'une insertion de segment de mémoire et d'une recherche linéaire dans un tableau.
Au niveau de l'implémentation: Quels coûts supplémentaires sont cachés dans une structure de données de tas fournie par une bibliothèque ou un compilateur?
Je pense que ce sont quelques-unes des questions auxquelles il faut répondre avant de pouvoir estimer la vraie différence entre les performances d'un tas de 100 éléments ou d'un tableau de 100 éléments. Il serait donc logique de faire une expérience et de mesurer les performances réelles.
la source
Algorithme Les plus grands éléments x de n:
J'appellerai la valeur de retour LISTE . C'est un ensemble de x éléments (à mon avis qui devrait être une liste liée)
Alors, quel est le pire des cas?
x log (x) + (nx) (log (x) +1) = nlog (x) + n - x
C'est donc le temps O (n) pour le pire des cas. Le +1 est la vérification si le nombre est supérieur au plus petit dans la LISTE. Le temps prévu pour le cas moyen dépendra de la distribution mathématique de ces n éléments.
Améliorations possibles
Cet algorithme peut être légèrement amélioré pour le pire des cas mais à mon humble avis (je ne peux pas prouver cette affirmation) qui dégradera le comportement moyen. Le comportement asymptotique sera le même.
L'amélioration de cet algorithme sera que nous ne vérifierons pas si l'élément est plus grand que le plus petit. Pour chaque élément, nous essaierons de l'insérer et s'il est plus petit que le plus petit, nous l'ignorerons. Bien que cela semble absurde si nous ne considérons que le pire des cas, nous aurons
x log (x) + (nx) log (x) = nlog (x)
opérations.
Pour ce cas d'utilisation, je ne vois aucune autre amélioration. Pourtant, vous devez vous demander - et si je dois faire cela plus que log (n) fois et pour différents x-es? Évidemment, nous trierions ce tableau dans O (n log (n)) et prendrions notre élément x chaque fois que nous en aurions besoin.
la source
On répondrait à cette question avec la complexité N log (100) (au lieu de N log N) avec une seule ligne de code C ++.
La réponse finale serait un vecteur où les 100 premiers éléments sont garantis être les 100 plus grands nombres de votre tableau tandis que les éléments restants ne sont pas ordonnés
C ++ STL (bibliothèque standard) est assez pratique pour ce genre de problèmes.
Remarque: je ne dis pas que c'est la solution optimale, mais cela aurait sauvé votre entretien.
la source
La solution simple consisterait à utiliser une file d'attente prioritaire, à ajouter les 100 premiers numéros à la file d'attente et à garder une trace du plus petit nombre dans la file d'attente, puis à parcourir les autres milliards de numéros, et chaque fois que nous en trouvons un qui est plus grand que le plus grand nombre dans la file d'attente prioritaire, nous supprimons le plus petit numéro, ajoutons le nouveau numéro et gardons à nouveau la trace du plus petit numéro dans la file d'attente.
Si les nombres étaient dans un ordre aléatoire, cela fonctionnerait très bien car comme nous parcourons un milliard de nombres aléatoires, il serait très rare que le nombre suivant soit parmi les 100 plus grands jusqu'à présent. Mais les chiffres ne sont peut-être pas aléatoires. Si le tableau était déjà trié par ordre croissant, nous insérions toujours un élément dans la file d'attente prioritaire.
Nous choisissons donc disons 100 000 nombres aléatoires dans le tableau en premier. Pour éviter un accès aléatoire qui pourrait être lent, nous ajoutons par exemple 400 groupes aléatoires de 250 numéros consécutifs. Avec cette sélection aléatoire, nous pouvons être sûrs que très peu des nombres restants sont dans les cent premiers, donc le temps d'exécution sera très proche de celui d'une simple boucle comparant un milliard de nombres à une valeur maximale.
la source
Il est préférable de trouver les 100 premiers sur un milliard de nombres en utilisant un tas minimal de 100 éléments.
Amorcez d'abord le min-tas avec les 100 premiers nombres rencontrés. min-heap stockera le plus petit des 100 premiers nombres à la racine (en haut).
Maintenant, au fur et à mesure que vous avancez, les autres chiffres ne les comparent qu'à la racine (la plus petite des 100).
Si le nouveau nombre rencontré est supérieur à la racine de min-heap, remplacez la racine par ce nombre, sinon ignorez-la.
Dans le cadre de l'insertion du nouveau numéro dans le tas min, le plus petit nombre dans le tas viendra en haut (racine).
Une fois que nous aurons parcouru tous les nombres, nous aurons les 100 plus grands nombres dans le tas.
la source
J'ai écrit une solution simple en Python au cas où quelqu'un serait intéressé. Il utilise le
bisect
module et une liste de retour temporaire qu'il conserve triés. Ceci est similaire à une implémentation de file d'attente prioritaire.Utilisation avec 100 000 000 d'éléments et entrée dans le pire des cas, qui est une liste triée:
Il a fallu environ 40 secondes pour calculer cela pour 100 000 000 d'éléments, j'ai donc peur de le faire pour 1 milliard. Pour être juste cependant, je lui fournissais l'entrée du pire des cas (ironiquement un tableau qui est déjà trié).
la source
Je vois beaucoup de discussions O (N), donc je propose quelque chose de différent juste pour l'exercice de réflexion.
Existe-t-il des informations connues sur la nature de ces chiffres? Si c'est de nature aléatoire, n'allez pas plus loin et regardez les autres réponses. Vous n'obtiendrez pas de meilleurs résultats qu'eux.
Toutefois! Vérifiez si le mécanisme de remplissage de liste a rempli cette liste dans un ordre particulier. Sont-ils dans un modèle bien défini où vous pouvez savoir avec certitude que la plus grande ampleur des nombres se trouvera dans une certaine région de la liste ou sur un certain intervalle? Il peut y avoir un motif. Si tel est le cas, par exemple s'ils sont garantis dans une sorte de distribution normale avec la bosse caractéristique au milieu, ont toujours des tendances à la hausse répétitives parmi les sous-ensembles définis, ont un pic prolongé à un certain moment T au milieu des données défini comme peut-être une incidence de délits d'initiés ou de panne d'équipement, ou peut-être simplement avoir un "pic" chaque Nième nombre comme dans l'analyse des forces après une catastrophe, vous pouvez réduire le nombre d'enregistrements que vous devez vérifier de manière significative.
Il y a de quoi réfléchir quand même. Peut-être que cela vous aidera à donner aux futurs intervieweurs une réponse réfléchie. Je sais que je serais impressionné si quelqu'un me posait une telle question en réponse à un problème comme celui-ci - cela me dirait qu'il pense à l'optimisation. Il suffit de reconnaître qu'il n'est pas toujours possible d'optimiser.
la source
Créer une liste vide de 100 emplacements vides
Pour chaque numéro dans la liste d'entrée:
Si le nombre est plus petit que le premier, sautez
Sinon, remplacez-le par ce numéro
Ensuite, poussez le numéro à travers l'échange adjacent; jusqu'à ce qu'il soit plus petit que le suivant
Retourner la liste
Remarque: si le
log(input-list.size) + c < 100
, alors le moyen optimal est de trier la liste d'entrée, puis de diviser les 100 premiers éléments.la source
La complexité est O (N)
Créez d'abord un tableau de 100 ints initialisez le premier élément de ce tableau comme premier élément des N valeurs, gardez une trace de l'index de l'élément courant avec une autre variable, appelez-le CurrentBig
Itérer si les valeurs N
une fois terminé, imprimez le tableau M de CurrentBig 100 fois modulo 100 :-) Pour l'étudiant: assurez-vous que la dernière ligne du code ne l'emporte pas sur les données valides juste avant la sortie du code
la source
Un autre algorithme O (n) -
L'algorithme trouve les 100 plus grands par élimination
considérer tous les millions de nombres dans leur représentation binaire. Commencez par le bit le plus significatif. Trouver si le MSB est 1 peut être fait par une multiplication d'opération booléenne avec un nombre approprié. S'il y a plus de 100 1 dans ces millions, éliminez les autres nombres avec des zéros. Maintenant, des nombres restants, passez au bit le plus significatif suivant. compter le nombre de numéros restants après élimination et continuer tant que ce nombre est supérieur à 100.
L'opération booléenne majeure peut être effectuée en parallèle sur les GPU
la source
Je découvrirais qui a eu le temps de mettre un milliard de numéros dans un tableau et de le virer. Doit travailler pour le gouvernement. Au moins, si vous aviez une liste chaînée, vous pourriez insérer un nombre au milieu sans déplacer un demi-milliard pour faire de la place. Encore mieux, un Btree permet une recherche binaire. Chaque comparaison élimine la moitié de votre total. Un algorithme de hachage vous permettrait de remplir la structure de données comme un damier mais pas si bon pour des données éparses. Comme il est préférable de disposer d'un tableau de solutions de 100 entiers et de garder une trace du nombre le plus bas dans votre tableau de solutions afin de pouvoir le remplacer lorsque vous rencontrez un nombre plus élevé dans le tableau d'origine. Vous devez regarder chaque élément du tableau d'origine en supposant qu'il n'est pas trié au départ.
la source
Vous pouvez le faire à
O(n)
temps. Parcourez simplement la liste et suivez les 100 plus grands nombres que vous avez vus à un moment donné et la valeur minimale de ce groupe. Lorsque vous trouvez un nouveau nombre plus grand le plus petit de vos dix, remplacez-le et mettez à jour votre nouvelle valeur minimale de 100 (cela peut prendre un temps constant de 100 pour le déterminer à chaque fois que vous le faites, mais cela n'affecte pas l'analyse globale ).la source
La gestion d'une liste séparée est un travail supplémentaire et vous devez déplacer les choses dans la liste entière chaque fois que vous trouvez un autre remplaçant. Il suffit de le trier et de prendre le top 100.
la source
Veuillez noter esp. la deuxième étape pourrait être facile à calculer en parallèle! Et ce sera également efficace lorsque vous aurez besoin d'un million de plus gros éléments.
la source
C'est une question de Google ou d'autres géants de l'industrie. Le code suivant est peut-être la bonne réponse attendue par votre interlocuteur. Le coût de temps et le coût d'espace dépendent du nombre maximal dans le tableau d'entrée.Pour l'entrée de tableau int 32 bits, le coût d'espace maximal est de 4 * 125M octets, le coût de temps est de 5 * milliards.
la source
j'ai fait mon propre code, je ne sais pas si c'est ce que "l'intervieweur" cherche
la source
Améliorations possibles.
Si le fichier contient 1 milliard, sa lecture peut être très longue ...
Pour améliorer ce travail, vous pouvez:
la source
Prenez d'abord 1000 éléments et ajoutez-les dans un tas maximum. Maintenant, sortez les 100 premiers éléments max et stockez-les quelque part. Maintenant, choisissez les 900 éléments suivants dans le fichier et ajoutez-les dans le tas avec les 100 derniers éléments les plus élevés.
Continuez à répéter ce processus consistant à récupérer 100 éléments du tas et à ajouter 900 éléments à partir du fichier.
Le choix final de 100 éléments nous donnera le maximum de 100 éléments à partir d'un milliard de nombres.
la source
Problème: Trouver m les plus grands éléments de n éléments où n >>> m
La solution la plus simple, qui devrait être évidente pour tout le monde, consiste simplement à effectuer m passes de l'algorithme de tri à bulles.
puis imprimez les n derniers éléments du tableau.
Cela ne nécessite aucune structure de données externe et utilise un algorithme que tout le monde connaît.
Le temps d'exécution estimé est O (m * n). Jusqu'à présent, la meilleure réponse est O (n log (m)), donc cette solution n'est pas beaucoup plus chère pour les petits m.
Je ne dis pas que cela ne pourrait pas être amélioré, mais c'est de loin la solution la plus simple.
la source