algorithms · go · data-structures · algorithms-data-structures
Cómo invertir arrays in-place de forma eficiente en Go
Si se está preparando para entrevistas técnicas, tarde o temprano alguien va a pedir invertir un array. A primera vista parece trivial, pero en el fondo pone a prueba qué tan bien se entienden los índices, los límites y la memoria.
En Go, este problema se convierte en una gran oportunidad: el lenguaje ofrece una sintaxis limpia y asignación múltiple, lo que permite escribir una solución in-place que es a la vez eficiente y fácil de leer.
En este post vamos a ver mi enfoque para invertir un slice in-place usando índices de inicio y fin, sin memoria extra. Es un patrón reutilizable en muchos otros algoritmos, no solo un truco puntual para entrevistas.
Empecemos por formalizar el problema que se quiere resolver.
Planteamiento del problema
Dado un array de enteros, un índice de inicio y un índice de fin, escribir una función que invierta los enteros in-place sin usar memoria extra.
Más formalmente:
-
Entrada: un array de enteros, un entero para el índice de inicio y otro entero para el índice de fin.
-
Salida: el mismo array modificado in-place, con los elementos entre start y end invertidos.
Ahora que se sabe exactamente qué entra y qué debe salir, se puede definir un algoritmo simple y seguro para llegar ahí.
Algoritmo
Antes de tocar el array, hay que asegurarse de que los índices sean seguros. Si start o end están fuera de rango, o si start no es estrictamente menor que end, no hay nada que hacer y simplemente se puede devolver el array original.
if start >= end || start < 0 || end >= len(arr) {
return arr
}
Una vez que la entrada es válida, la idea es simple: intercambiar elementos desde los extremos del rango hacia el centro. Solo hace falta visitar la mitad de los elementos entre start y end. Para cada posición i desde la izquierda, se intercambia con su contraparte desde la derecha:
-
índice izquierdo:
start + i -
índice derecho:
end - i
Por eso el loop se ejecuta hasta la mitad de la longitud del rango:
for i := 0; i < (end-start+1)/2; i++ {}
La expresión (end - start + 1) es la cantidad de elementos en el rango [start, end]. Al dividirla entre 2 se obtiene la cantidad de swaps que hay que realizar.
Para ver esto en acción, tomemos el array [1, 2, 3] e invirtamos todo el rango:
-
Primera iteración (
i = 0): swap entrearr[0]yarr[2]→[3, 2, 1]. -
No hay una segunda iteración, porque ya se llegó al medio.
Gracias a la asignación múltiple de Go, el swap se mantiene legible y la complejidad espacial se mantiene en O(1). Cuando el loop termina, el slice original quedó modificado in-place, usando la misma memoria subyacente.
arr[start+i], arr[end-i] = arr[end-i], arr[start+i]
Cuando el loop termina, el array original queda modificado dentro del mismo rango de memoria. Para comprobar que esto es cierto, vamos a escribir una prueba sencilla.
Siendo mi propio QA
Ahora que el algoritmo está listo, toca demostrar que realmente hace lo que dice. Vamos a empezar con un caso de prueba simple y dejar que el paquete testing de Go haga de QA.
En este ejemplo, se invierte el array completo [1, 2, 3, 4] usando start = 0 y end = 3:
func TestReverseArray(t *testing.T) {
arr := []int{1, 2, 3, 4}
start := 0
end := 3
result := ReverseInPlace(arr, start, end)
expected := []int{4, 3, 2, 1}
for i := range expected {
if result[i] != expected[i] {
t.Errorf("Expected %v, but got %v", expected[i], result[i])
break
}
}
if &arr[0] != &result[0] {
t.Error("Expected array to be modified in place, but got a different array reference")
}
}
Esta prueba comprueba dos cosas:
-
Que los valores sean correctos:
[1, 2, 3, 4]se convierte en[4, 3, 2, 1]. -
Que la memoria sea la misma: comparar
&arr[0]y&result[0]garantiza que no se creó un array nuevo por detrás, sino que se modificó el original in-place.
Se pueden agregar más casos de prueba para cubrir escenarios límite, por ejemplo:
-
Un subrango en el medio del array.
-
Un rango de longitud 1.
-
Rangos inválidos, como
start > endoend out of bounds, donde la función simplemente debería devolver el array original.
Reflexiones finales
Invertir un array in-place no es solo una pregunta clásica de entrevista. Es una buena forma de practicar cosas que se usan todo el tiempo: índices, límites y cómo funciona la memoria en Go.
El objetivo no es memorizar esta solución exacta, sino quedarse con el patrón: moverse desde los extremos hacia el centro, tocar solo la mitad del rango, y mantenerse en O(1) de espacio. A partir de ahí se puede jugar con variaciones como subrangos, rotaciones o distintos tipos de datos.
Si se buscan más ejercicios como este, se puede revisar mi repo de algoritmos: github.com/RubenOAlvarado/algorithms
Elegir un problema, intentar una solución propia y después compararla con la mía. Ese tipo de práctica es la que realmente suma con el tiempo.
¡A programar, y nos vemos en la próxima!