La brillante risposta di caf stampa ogni numero che appare k volte nell'array k-1 volte. È un comportamento utile, ma la domanda richiede probabilmente che ogni duplicato venga stampato una sola volta, e allude alla possibilità di farlo senza superare i limiti di tempo lineare / spazio costante. Questo può essere fatto sostituendo il suo secondo ciclo con il seguente pseudocodice:
for (i = 0; i < N; ++i) {
if (A[i] != i && A[A[i]] == A[i]) {
print A[i];
A[A[i]] = i;
}
}
Questo sfrutta la proprietà che, dopo l'esecuzione del primo ciclo, se un valore m
appare più di una volta, è garantito che uno di questi aspetti sia nella posizione corretta, ovvero A[m]
. Se stiamo attenti, possiamo utilizzare quella posizione "home" per memorizzare le informazioni sul fatto che eventuali duplicati siano stati ancora stampati o meno.
Nella versione caf, mentre passavamo attraverso l'array, A[i] != i
implicava che A[i]
fosse un duplicato. Nella mia versione, mi affido a un invariante leggermente diverso: ciò A[i] != i && A[A[i]] == A[i]
implica che A[i]
è un duplicato che non abbiamo mai visto prima . (Se si elimina la parte "che non abbiamo visto prima", il resto può essere visto come implicito dalla verità dell'invariante caf e dalla garanzia che tutti i duplicati abbiano una copia in una posizione domestica.) Questa proprietà è valida per all'inizio (al termine del primo ciclo di caf) e di seguito mostro che viene mantenuto dopo ogni passaggio.
Mentre passiamo attraverso l'array, il successo da A[i] != i
parte del test implica che A[i]
potrebbe essere un duplicato che non è stato visto prima. Se non l'abbiamo mai visto prima, allora ci aspettiamo che A[i]
la posizione della casa punti a se stessa - questo è ciò che viene testato dalla seconda metà della if
condizione. In tal caso, lo stampiamo e modifichiamo la posizione della casa in modo che faccia riferimento al primo duplicato trovato, creando un "ciclo" in due fasi.
Per vedere che questa operazione non altera il nostro invariante, supponiamo m = A[i]
per una particolare posizione i
soddisfacente A[i] != i && A[A[i]] == A[i]
. E 'ovvio che il cambiamento che facciamo ( A[A[i]] = i
) lavorerà per evitare che altri eventi non-casa di m
di essere uscita come duplicati provocando la seconda metà delle loro if
condizioni di fallire, ma funzionerà quando i
arriva alla posizione della propria abitazione, m
? Sì, lo sarà, perché ora, anche se in questa nuova situazione i
troviamo che la prima metà della if
condizione A[i] != i
,, è vera, la seconda metà verifica se la posizione a cui punta è una posizione domestica e scopre che non lo è. In questa situazione non sappiamo più se m
o A[m]
era il valore duplicato, ma sappiamo che in entrambi i casi,è già stato segnalato , perché è garantito che questi 2 cicli non compaiono nel risultato del primo ciclo di caf. (Nota che se m != A[m]
poi esattamente uno di m
e si A[m]
verifica più di una volta e l'altro non si verifica affatto.)
a[a[i]]
, e il vincolo di spazio O (1) suggerisce che l'swap()
operazione sia la chiave.