Esto es lo que se me ocurrió, que no requiere el bit de signo adicional:
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
El primer bucle permuta la matriz de modo que si el elemento x
está presente al menos una vez, una de esas entradas estará en la posición A[x]
.
Tenga en cuenta que puede no parecer O (n) a primera vista, pero lo es, aunque tiene un bucle anidado, todavía se ejecuta a O(N)
tiempo. Un intercambio solo ocurre si hay un i
tal que A[i] != i
, y cada intercambio establece al menos un elemento tal que A[i] == i
, donde eso no era cierto antes. Esto significa que el número total de intercambios (y, por lo tanto, el número total de ejecuciones del while
cuerpo del bucle) es como máximo N-1
.
El segundo ciclo imprime los valores x
para los cuales A[x]
no es igual x
, ya que el primer ciclo garantiza que si x
existe al menos una vez en la matriz, una de esas instancias estará en A[x]
, esto significa que imprime aquellos valores de los x
cuales no están presentes en la matriz
(Ideone enlace para que puedas jugar con él)
a[a[i]]
, y la restricción de espacio O (1) sugiere que laswap()
operación es clave.5
no está en el rango0..N-1
(N
en este caso, estar5
).print
declaración paraprint i
convertir esto en una solución para stackoverflow.com/questions/5249985/… y (suponiendo que la "bolsa" es una matriz modificable) Qk de stackoverflow.com/questions/3492302/… .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:
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 decirA[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 queA[i]
era un duplicado. En mi versión, confío en un invariante ligeramente diferente: esoA[i] != i && A[A[i]] == A[i]
implica queA[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 queA[i]
podría ser un duplicado que no se haya visto antes. Si no lo hemos visto antes, entonces esperamos queA[i]
la ubicación de la casa se señale a sí misma, eso es lo que se prueba en la segunda mitad de laif
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 esi
satisfactoriaA[i] != i && A[A[i]] == A[i]
. Es obvio que el cambio que hacemos (A[A[i]] = i
) funcionará para evitar que se produzcanm
duplicados otros sucesos ajenos al hogarif
al hacer que falle la segunda mitad de sus condiciones, pero ¿funcionará cuandoi
llegue a la ubicación del hogarm
? Sí, porque ahora, a pesar de que en este nuevoi
nos encontramos con que la primera mitad de laif
condiciónA[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 fuem
o noA[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 sim != A[m]
entonces exactamente uno dem
yA[m]
ocurre más de una vez, y el otro no ocurre en absoluto).fuente
Aquí está el pseudocódigo
Código de muestra en C ++
fuente
-
con~
el problema cero.O(n)
un espacio oculto: losn
bits de los signos. Si la matriz se define de manera que cada elemento solo pueda contener valores entre0
yn-1
, entonces obviamente no funciona.Para N relativamente pequeño podemos usar operaciones div / mod
No C / C ++ pero de todos modos
http://ideone.com/GRZPI
fuente
No es realmente bonito, pero al menos es fácil ver las propiedades O (N) y O (1). Básicamente, escaneamos la matriz y, para cada número, vemos si la posición correspondiente se ha marcado como ya visto una vez (N) o ya visto varias veces (N + 1). Si está marcado ya visto una vez, lo imprimimos y lo marcamos ya visto varias veces. Si no está marcado, lo marcamos ya visto una vez y movemos el valor original del índice correspondiente a la posición actual (marcar es una operación destructiva).
o, mejor aún (más rápido, a pesar del doble bucle):
fuente
if (value > i) a[i--] = a[value];
funciona: sivalue <= i
ya hemos procesado el valor ena[value]
y podemos sobrescribirlo de manera segura. ¡Tampoco diría que la naturaleza O (N) es obvia! Explicándolo: el bucle principal ejecutaN
tiempos, más las vecesa[i--] = a[value];
que se ejecuta la línea. Esa línea solo puede ejecutarse sia[value] < N
, y cada vez que se ejecuta, inmediatamente después se establece un valor de matriz que aún no estabaN
configuradoN
, por lo que puede ejecutarse en la mayoría de lasN
veces, para un total de2N
iteraciones de bucle como máximo .Una solución en C es:
Es O (n) tiempo y O (1) complejidad espacial.
fuente
Supongamos que presentamos esta matriz como una estructura de datos de gráfico unidireccional: cada número es un vértice y su índice en la matriz apunta a otro vértice que forma un borde del gráfico.
Para mayor simplicidad, tenemos índices de 0 a n-1 y rango de números de 0..n-1. p.ej
0 (3) -> 3 (3) es un ciclo.
Respuesta: solo recorra la matriz basándose en índices. si a [x] = a [y] entonces es un ciclo y, por lo tanto, se duplica. Salte al siguiente índice y continúe de nuevo y así sucesivamente hasta el final de una matriz. Complejidad: O (n) tiempo y O (1) espacio.
fuente
Un pequeño código de Python para demostrar el método de caf anterior:
fuente
i
valor; tengawhile
en cuenta lo que aparece en mi respuesta.El algoritmo se puede ver fácilmente en la siguiente función C. La recuperación de la matriz original, aunque no es necesaria, será posible tomando cada módulo de entrada n .
Ideone Link para pruebas.
fuente
fuente
He creado una aplicación de juegos de muestra de forma rápida para encontrar duplicados en 0 (n) complejidad de tiempo y espacio extra constante. Por favor revise la url Encontrar duplicados
La solución IMP anterior funcionó cuando una matriz contiene elementos de 0 a n-1, y cualquiera de estos números aparece varias veces.
fuente
fuente
Si la matriz no es demasiado grande, esta solución es más simple, crea otra matriz del mismo tamaño para marcar.
1 Cree un mapa de bits / matriz del mismo tamaño que su matriz de entrada
2 escanee su matriz de entrada e incremente su recuento en la matriz anterior
3 Ahora escanee la matriz check_list e imprima el duplicado una o tantas veces como se hayan duplicado
Por supuesto, toma el doble del espacio consumido por la solución dada anteriormente, pero la eficiencia de tiempo es O (2n), que es básicamente O (n).
fuente
O(1)
espacio.