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 mappare 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] != iimplicava 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] != iparte 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 ifcondizione. 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 isoddisfacente 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 mdi essere uscita come duplicati provocando la seconda metà delle loro ifcondizioni di fallire, ma funzionerà quando iarriva alla posizione della propria abitazione, m? Sì, lo sarà, perché ora, anche se in questa nuova situazione itroviamo che la prima metà della ifcondizione 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 mo 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 me 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.