Voici ce que j'ai proposé, qui ne nécessite pas le bit de signe supplémentaire:
for i := 0 to n - 1
while A[A[i]] != A[i]
swap(A[i], A[A[i]])
end while
end for
for i := 0 to n - 1
if A[i] != i then
print A[i]
end if
end for
La première boucle permute le tableau de sorte que si l'élément x
est présent au moins une fois, alors l'une de ces entrées sera à la position A[x]
.
Notez qu'il peut ne pas sembler O (n) à première vue, mais c'est le cas - bien qu'il ait une boucle imbriquée, il fonctionne toujours dans le O(N)
temps. Un échange se produit uniquement s'il existe un i
tel que A[i] != i
, et chaque échange définit au moins un élément tel que A[i] == i
, là où ce n'était pas vrai auparavant. Cela signifie que le nombre total de swaps (et donc le nombre total d'exécutions du while
corps de la boucle) est au maximum N-1
.
La deuxième boucle imprime les valeurs de x
pour qui A[x]
n'est pas égal x
- puisque la première boucle garantit que si elle x
existe au moins une fois dans le tableau, l'une de ces instances sera à A[x]
, cela signifie qu'elle imprime les valeurs x
dont ne sont pas présentes dans le tableau.
(Lien Ideone pour que vous puissiez jouer avec)
a[a[i]]
, et la contrainte d'espace O (1) indique que l'swap()
opération est la clé.5
n'est pas dans la plage0..N-1
(N
dans ce cas étant5
).print
déclaration enprint i
transforme cela en une solution à stackoverflow.com/questions/5249985/… et (en supposant que le "sac" est un tableau modifiable) Qk de stackoverflow.com/questions/3492302/… .La réponse brillante de caf imprime chaque nombre qui apparaît k fois dans le tableau k-1 fois. C'est un comportement utile, mais la question demande sans doute que chaque duplicata soit imprimé une seule fois, et il fait allusion à la possibilité de le faire sans faire sauter les limites linéaires temps / espace constant. Cela peut être fait en remplaçant sa deuxième boucle par le pseudocode suivant:
Cela exploite la propriété qu'après l'exécution de la première boucle, si une valeur
m
apparaît plus d'une fois, alors l'une de ces apparences est garantie d'être dans la bonne position, à savoirA[m]
. Si nous faisons attention, nous pouvons utiliser cet emplacement «d'origine» pour stocker des informations indiquant si des doublons ont déjà été imprimés ou non.Dans la version de caf, lorsque nous avons parcouru le tableau,
A[i] != i
cela impliquait qu'il s'agissait d'A[i]
un doublon. Dans ma version, je m'appuie sur un invariant légèrement différent: celaA[i] != i && A[A[i]] == A[i]
implique qu'ilA[i]
s'agit d'un doublon que nous n'avons jamais vu auparavant . (Si vous supprimez la partie "que nous n'avons jamais vu auparavant", le reste peut être vu comme étant impliqué par la vérité de l'invariant de caf et la garantie que tous les doublons ont une copie dans un emplacement d'origine.) Cette propriété tient à le début (après la fin de la première boucle de caf) et je montre ci-dessous qu'elle est maintenue après chaque étape.Au fur et à mesure que nous parcourons le tableau, le succès
A[i] != i
du test implique qu'ilA[i]
pourrait s'agir d' un doublon qui n'a jamais été vu auparavant. Si nous ne l 'avons pas vu auparavant, nous nous attendons à ce que lA[i]
' emplacement d 'origine de la personne se dirige vers lui - c'est ce qui est testé par la seconde moitié de laif
condition. Si tel est le cas, nous l'imprimons et modifions l'emplacement du domicile pour qu'il pointe vers ce premier doublon trouvé, créant un «cycle» en 2 étapes.Pour voir que cette opération ne modifie pas notre invariant, supposons
m = A[i]
qu'une position particulière soiti
satisfaisanteA[i] != i && A[A[i]] == A[i]
. Il est évident que le changement que nous faisons (A[A[i]] = i
) travaillera pour éviter que d' autres événements non domestiques dem
d'être sortie comme des doublons en provoquant la 2ème moitié de leursif
conditions à l' échec, mais il lorsqu'ili
arrive à l'endroit de la maison,m
? Oui, car maintenant, même si à ce nouveaui
nous constatons que la première moitié de laif
conditionA[i] != i
est vraie, la deuxième moitié teste si l'emplacement vers lequel elle pointe est un emplacement d'origine et constate que ce n'est pas le cas. Dans cette situation, nous ne savons plus sim
ouA[m]
était la valeur en double, mais nous savons que de toute façon,cela a déjà été signalé , car ces 2 cycles sont garantis de ne pas apparaître dans le résultat de la 1ère boucle de caf. (Notez que sim != A[m]
alors exactement l'un desm
etA[m]
se produit plus d'une fois, et l'autre ne se produit pas du tout.)la source
Voici le pseudocode
Exemple de code en C ++
la source
-
par~
pour le problème zéro.O(n)
un espace caché - lesn
bits de signe. Si le tableau est défini de telle sorte que chaque élément ne peut contenir que des valeurs comprises entre0
etn-1
, alors cela ne fonctionne évidemment pas.Pour un N relativement petit, nous pouvons utiliser des opérations div / mod
Pas C / C ++ mais de toute façon
http://ideone.com/GRZPI
la source
Pas vraiment joli mais au moins il est facile de voir les propriétés O (N) et O (1). Fondamentalement, nous parcourons le tableau et, pour chaque nombre, nous voyons si la position correspondante a été marquée déjà vue une fois (N) ou déjà vue plusieurs fois (N + 1). S'il est signalé déjà vu une fois, nous l'imprimons et le marquons déjà vu plusieurs fois. S'il n'est pas marqué, nous le marquons déjà vu une fois et nous déplaçons la valeur d'origine de l'index correspondant vers la position actuelle (le marquage est une opération destructive).
ou, mieux encore (plus rapide, malgré la double boucle):
la source
if (value > i) a[i--] = a[value];
fonctionne: sivalue <= i
alors nous avons déjà traité la valeur àa[value]
et pouvons l'écraser en toute sécurité. Je ne dirais pas non plus que la nature O (N) est évidente! Épelez-le: la boucle principale s'exécuteN
, plus le nombre de fois que laa[i--] = a[value];
ligne s'exécute. Cette ligne ne peut s'exécuter que sia[value] < N
, et à chaque fois qu'elle s'exécute, immédiatement après, une valeur de tableau qui n'était pas déjàN
définie est définie surN
, de sorte qu'elle puisse s'exécuter la plupart duN
temps, pour un total d'au plus des2N
itérations de boucle.Une solution en C est:
C'est la complexité du temps O (n) et de l'espace O (1).
la source
Supposons que nous présentions ce tableau comme une structure de données de graphe unidirectionnelle - chaque nombre est un sommet et son index dans le tableau pointe vers un autre sommet formant une arête du graphe.
Pour encore plus de simplicité, nous avons des indices de 0 à n-1 et une plage de nombres de 0..n-1. par exemple
0 (3) -> 3 (3) est un cycle.
Réponse: Traversez simplement le tableau en vous basant sur les indices. si a [x] = a [y] alors c'est un cycle et donc dupliquer. Passer à l'index suivant et continuer encore et ainsi de suite jusqu'à la fin d'un tableau. Complexité: temps O (n) et espace O (1).
la source
Un petit code python pour démontrer la méthode de caf ci-dessus:
la source
i
valeur - notez lewhile
dans ma réponse.L'algorithme peut être facilement vu dans la fonction C suivante. La récupération du tableau d'origine, bien que non obligatoire, sera possible en prenant chaque entrée modulo n .
Ideone Link pour les tests.
la source
la source
J'ai créé un exemple d'application de jeu dans Swift pour trouver des doublons dans une complexité de temps 0 (n) et un espace supplémentaire constant. Veuillez vérifier l'URL Recherche de doublons
La solution IMP ci- dessus fonctionnait lorsqu'un tableau contient des éléments de 0 à n-1, l'un de ces nombres apparaissant un nombre illimité de fois.
la source
la source
Si le tableau n'est pas trop grand, cette solution est plus simple, elle crée un autre tableau de même taille pour le cocher.
1 Créez un bitmap / tableau de même taille que votre tableau d'entrée
2 scannez votre tableau d'entrée et incrémentez son nombre dans le tableau ci-dessus
3 Maintenant, scannez le tableau check_list et imprimez le duplicata une ou autant de fois qu'ils ont été dupliqués
Bien sûr, cela prend deux fois l'espace consommé par la solution donnée ci-dessus, mais l'efficacité en temps est O (2n) qui est fondamentalement O (n).
la source
O(1)
espace.