La brillante respuesta de caf imprime cada número que aparece k veces en la matriz k-1 veces. Ese es un comportamiento útil, pero la pregunta podría decirse que cada duplicado se imprima una sola vez, y alude a la posibilidad de hacerlo sin soplar los límites de tiempo lineal / espacio constante. Esto se puede hacer reemplazando su segundo bucle con el siguiente pseudocódigo:
for (i = 0; i < N; ++i) {
if (A[i] != i && A[A[i]] == A[i]) {
print A[i];
A[A[i]] = i;
}
}
Esto explota la propiedad de que después de que se ejecuta el primer ciclo, si algún valor m
aparece más de una vez, se garantiza que uno de esos aspectos esté en la posición correcta, es decir A[m]
. Si tenemos cuidado, podemos usar esa ubicación de "hogar" para almacenar información sobre si todavía se han impreso o no duplicados.
En la versión de caf, a medida que avanzábamos por la matriz, A[i] != i
implicaba que A[i]
era un duplicado. En mi versión, confío en un invariante ligeramente diferente: eso A[i] != i && A[A[i]] == A[i]
implica que A[i]
es un duplicado que no hemos visto antes . (Si deja caer la parte "que no hemos visto antes", el resto puede verse implicado por la verdad de invariante de caf, y la garantía de que todos los duplicados tienen alguna copia en la ubicación de una casa). Esta propiedad se mantiene en al principio (después de que finaliza el 1er bucle de caf) y muestro a continuación que se mantiene después de cada paso.
A medida que avanzamos por la matriz, el éxito por A[i] != i
parte de la prueba implica que A[i]
podría ser un duplicado que no se haya visto antes. Si no lo hemos visto antes, entonces esperamos que A[i]
la ubicación de la casa se señale a sí misma, eso es lo que se prueba en la segunda mitad de la if
condición. Si ese es el caso, lo imprimimos y modificamos la ubicación de inicio para volver a este primer duplicado encontrado, creando un "ciclo" de 2 pasos.
Para ver que esta operación no altera nuestra invariante, supongamos m = A[i]
que una posición particular es i
satisfactoria A[i] != i && A[A[i]] == A[i]
. Es obvio que el cambio que hacemos ( A[A[i]] = i
) funcionará para evitar que se produzcan m
duplicados otros sucesos ajenos al hogar if
al hacer que falle la segunda mitad de sus condiciones, pero ¿funcionará cuando i
llegue a la ubicación del hogar m
? Sí, porque ahora, a pesar de que en este nuevo i
nos encontramos con que la primera mitad de la if
condición A[i] != i
es verdadera, la segunda mitad prueba si la ubicación a la que apunta es una ubicación de origen y descubre que no lo es. En esta situación, ya no sabemos si el valor duplicado fue m
o no A[m]
, pero sabemos que de cualquier manera,ya se ha informado , porque se garantiza que estos 2 ciclos no aparecerán en el resultado del primer bucle de caf. (Tenga en cuenta que si m != A[m]
entonces exactamente uno de m
y A[m]
ocurre más de una vez, y el otro no ocurre en absoluto).
a[a[i]]
, y la restricción de espacio O (1) sugiere que laswap()
operación es clave.