Supposons que vous ayez un tableau de taille contenant des entiers de à inclus, avec exactement cinq répétitions. J'ai besoin de proposer un algorithme qui puisse trouver les nombres répétés en temps . Je ne peux, pour ma vie, penser à rien. Je pense que le tri, au mieux, serait ? La traversée du tableau serait alors , résultant en . Cependant, je ne suis pas vraiment sûr que le tri soit nécessaire car j'ai vu des choses délicates avec une liste liée, des files d'attente, des piles, etc.
algorithms
arrays
searching
darylnak
la source
la source
Réponses:
Vous pouvez créer un tableau supplémentaire de taille n . Définissez initialement tous les éléments du tableau sur 0 . Faites ensuite une boucle dans le tableau d'entrée A et augmentez B [ A [ i ] ] de 1 pour chaque i . Après cela, vous vérifiez simplement le tableau B : boucle sur A et si B [ A [ i ] ] > 1, alors A [ i ] est répété. Vous le résolvez dans O ( n )B n 0 UNE B [ A [ i ] ] je B UNE B [ A [ i ] ] > 1 A [ i ] O ( n ) temps au prix de la mémoire qui est et parce que vos entiers sont compris entre 1 et n - 5 .O ( n ) 1 n - 5
la source
La solution dans la réponse de fade2black est la solution standard, mais elle utilise l' espace . Vous pouvez améliorer cela à l' espace O ( 1 ) comme suit:O ( n ) O ( 1 )
Cet algorithme suppose le modèle de machine RAM, dans lequel les opérations arithmétiques de base sur les mots bits prennent O ( 1 ) .O ( logn ) O ( 1 )
Une autre façon de formuler cette solution est la suivante:
Cette solution montre que si nous remplaçons 5 par , nous obtenons (je crois) un algorithme O ( d 2 n ) utilisant l' espace O ( d 2 ) , qui effectue des opérations arithmétiques O ( d n ) sur des entiers de longueur de bit O ( d log n ) , en gardant au plus O ( d ) de ceux-ci à un moment donné. (Cela nécessite une analyse minutieuse des multiplications que nous effectuons, dont la plupart impliquent un opérande de longueur uniquement O ( log nré O ( d2n ) O ( d2) O ( dn ) O ( dJournaln ) O ( d) .) Il est concevable que cela puisse être amélioré en temps O ( d n ) et enespace O ( d ) en utilisant l'arithmétique modulaire.O ( logn ) O ( dn ) O ( d)
la source
Il existe également un algorithme de temps linéaire et d'espace constant basé sur le partitionnement, qui peut être plus flexible si vous essayez de l'appliquer à des variantes du problème sur lesquelles l'approche mathématique ne fonctionne pas bien. Cela nécessite de muter le tableau sous-jacent et présente des facteurs constants pires que l'approche mathématique. Plus précisément, je pense que les coûts en termes du nombre total de valeurs et du nombre de doublons d sont respectivement O ( n log d ) et O ( d ) , bien que le prouver rigoureusement prendra plus de temps que je n'en ai actuellement .n d O(nlogd) O(d)
Algorithme
Commencez avec une liste de paires, où la première paire est la plage sur l'ensemble du tableau, ou si indexé 1.[(1,n)]
Répétez les étapes suivantes jusqu'à ce que la liste soit vide:
Analyse cursive de la complexité temporelle.
Les étapes 1 à 6 prennent du temps , car la recherche du minimum et du maximum et le partitionnement peuvent être effectués en temps linéaire.O(j−i)
Chaque paire de la liste est soit la première paire, ( 1 , n ) , soit un enfant d'une paire pour laquelle le sous-tableau correspondant contient un élément en double. Il y a au plus d ⌈ log 2 n + 1 ⌉ de tels parents, car chaque parcours divise par deux la plage dans laquelle un doublon peut être, il y a donc au plus 2 d ⌈ log 2 n + 1 ⌉ au total lorsque des paires sont incluses sur des sous-réseaux sans doublons. À tout moment, la taille de la liste ne dépasse pas 2 jours(i,j) (1,n) d⌈log2n+1⌉ 2d⌈log2n+1⌉ 2d .
Considérez le travail pour trouver un double. Cela consiste en une séquence de paires sur une plage exponentiellement décroissante, donc le travail total est la somme de la séquence géométrique, ou . Cela produit un corollaire évident que le travail total pour les doublons d doit être O ( n d ) , qui est linéaire en n .O(n) d O(nd) n
Pour trouver une limite plus stricte, considérons le pire des cas de doublons répartis au maximum. Intuitivement, la recherche prend deux phases, l'une où le tableau complet est parcouru à chaque fois, en parties progressivement plus petites, et l'autre où les parties sont plus petites que donc seules les parties du tableau sont traversées. La première phase ne peut être quelogd enprofondeur, donc a coûtéO(nlogd), et la deuxième phase a coûtéO(n)parce que la superficie totale recherchée diminue à nouveau de façon exponentielle.nd logd O(nlogd) O(n)
la source
Laissant cela comme une réponse car il a besoin de plus d'espace qu'un commentaire.
Vous faites une erreur dans l'OP lorsque vous suggérez une méthode. Trier une liste puis la traverser temps, pas O ( n 2 log n ) temps. Lorsque vous faites deux choses (qui prennent O ( f ) et O ( g ) respectivement) séquentiellement, la complexité temporelle résultante est O ( f + g ) = O ( max f , g ) (dans la plupart des circonstances).O(nlogn) O(n2logn) O(f) O(g) O(f+g)=O(maxf,g)
Afin de multiplier les complexités temporelles, vous devez utiliser une boucle for. Si vous avez une boucle de longueur et que pour chaque valeur de la boucle vous faites une fonction qui prend O ( g ) , alors vous aurez le temps O ( f g ) .f O(g) O(fg)
Donc, dans votre cas, vous triez dans puis transversalement dans O ( n ) résultant en O ( n log n + n ) = O ( n log n ) . Si pour chaque comparaison de l'algorithme de tri vous deviez faire un calcul qui prend O ( n ) , alors il faudrait O ( n 2 log n ) mais ce n'est pas le cas ici.O(nlogn) O(n) O(nlogn+n)=O(nlogn) O(n) O(n2logn)
Au cas où vous seriez curieux de savoir si j'affirme que , il est important de noter que ce n'est pas toujours vrai. Mais si f ∈ O ( g ) ou g ∈ O ( f ) (qui vaut pour toute une série de fonctions communes), il le sera. Le moment le plus courant qu'il ne tient pas est lorsque des paramètres supplémentaires sont impliqués et que vous obtenez des expressions comme O ( 2 c n + n log n ) .O(f+g)=O(maxf,g) f∈O(g) g∈O(f) O(2cn+nlogn)
la source
Il existe une variante en place évidente de la technique de tableau booléen utilisant l'ordre des éléments comme magasin (où
arr[x] == x
pour les éléments "trouvés"). Contrairement à la variante de partition qui peut être justifiée d'être plus générale, je ne sais pas quand vous auriez réellement besoin de quelque chose comme ça, mais c'est simple.Cela place simplement à plusieurs reprisesn
arr[idx]
l'emplacementarr[idx]
jusqu'à ce que vous trouviez cet emplacement déjà pris, auquel cas il doit s'agir d'un doublon. Notez que le nombre total de swaps est limité par puisque chaque swap rend sa condition de sortie correcte.la source
while
boucle interne s'exécute en temps constant en moyenne. Sinon, ce n'est pas un algorithme à temps linéaire.Soustrayez les valeurs que vous avez de la somme .∑ni=1i=(n−1)⋅n2
Donc, après le temps (en supposant que l'arithmétique est O (1), ce qui n'est pas vraiment, mais supposons), vous avez une somme σ 1 de 5 entiers entre 1 et n:Θ(n) σ1
Soi-disant, ce n'est pas bon, non? Vous ne pouvez pas comprendre comment diviser cela en 5 nombres distincts.
la source
la source
Mappez un tableau sur
1 << A[i]
puis XOR tout ensemble. Vos doublons seront les numéros où le bit correspondant est désactivé.la source
la source
collated[item].append(item)
s'exécute en temps constant. Est-ce vraiment vrai?