Notación Big-O: ejemplos prácticos y explicados para principiantes

Introducción: La notación Big-O mide el crecimiento del tiempo de ejecución en función del tamaño de la entrada n. A continuación se muestran ejemplos en Go con razonamiento paso a paso para las clases más comunes de complejidad, seguidos de consejos prácticos.

O(1) - Tiempo constante. Ejemplo: obtener el primer elemento de una slice

// O(1) - acceder al primer elemento func getFirst(nums []int) int { if len(nums) == 0 { return -1 } return nums[0] }

Razonamiento: El número de operaciones es fijo y no depende de n. Comprobación de longitud, acceso por índice y retorno son operaciones O(1).

O(n) - Tiempo lineal. Ejemplo: búsqueda lineal en un array

// Búsqueda lineal: O(n) func linearSearch(nums []int, target int) int { for i := 0; i < len(nums); i++ { // hasta n iteraciones if nums[i] == target { return i } } return -1 }

Razonamiento: En el peor caso el bucle recorre los n elementos; cada iteración hace trabajo O(1). Total O(n).

O(log n) - Tiempo logarítmico. Ejemplo: búsqueda binaria en slice ordenada

// Búsqueda binaria: O(log n) func binarySearch(nums []int, target int) int { lo, hi := 0, len(nums)-1 for lo <= hi { mid := lo + (hi-lo)/2 if nums[mid] == target { return mid } else if nums[mid] < target { lo = mid + 1 } else { hi = mid - 1 } } return -1 }

Razonamiento: Cada iteración reduce el rango a la mitad. Tras k iteraciones el tamaño restante es n/2^k. Se llega a 1 cuando k es aproximadamente log2(n). Por tanto O(log n).

O(n log n) - Ejemplo típico de ordenación eficiente. Merge sort

// Merge sort: O(n log n) func mergeSort(a []int) []int { if len(a) <= 1 { return a } mid := len(a)/2 left := mergeSort(a[:mid]) right := mergeSort(a[mid:]) return merge(left, right) } func merge(left, right []int) []int { res := make([]int, 0, len(left)+len(right)) i, j := 0, 0 for i < len(left) && j < len(right) { if left[i] <= right[j] { res = append(res, left[i]) i++ } else { res = append(res, right[j]) j++ } } res = append(res, left[i:]...) res = append(res, right[j:]...) return res }

Razonamiento: Se divide en dos subproblemas de tamaño n/2 y se combinan en O(n). La recurrencia T(n) = 2T(n/2) + O(n) da O(n log n) por el teorema maestro o sumando el coste por niveles.

O(n^2) - Tiempo cuadrático. Ejemplo: recorrer todos los pares con bucles anidados

// Imprimir pares ordenados: O(n^2) func printPairs(nums []int) { n := len(nums) for i := 0; i < n; i++ { for j := 0; j < n; j++ { _ = nums[i] + nums[j] } } }

Razonamiento: El bucle externo corre n veces y por cada iteración el interno corre n veces, total n × n = n^2, por tanto O(n^2).

O(2^n) - Tiempo exponencial. Ejemplo: generar todas las subconjuntos

// Subconjuntos: O(2^n) func subsets(nums []int) [][]int { res := [][]int{} var helper func(int, []int) helper = func(i int, cur []int) { if i == len(nums) { tmp := append([]int(nil), cur...) res = append(res, tmp) return } helper(i+1, cur) helper(i+1, append(cur, nums[i])) } helper(0, []int{}) return res }

Razonamiento: Cada elemento puede incluirse o no, árbol binario de profundidad n con 2^n hojas. Se hace trabajo por cada hoja y por tanto el coste crece como c·2^n, clasificado como O(2^n).

O(n!) - Tiempo factorial. Ejemplo: generar todas las permutaciones

// Permutaciones: O(n!) func permutations(nums []int) [][]int { a := make([]int, len(nums)) copy(a, nums) res := [][]int{} var helper func(int) helper = func(k int) { if k == len(a)-1 { tmp := append([]int(nil), a...) res = append(res, tmp) return } for i := k; i < len(a); i++ { a[k], a[i] = a[i], a[k] helper(k+1) a[k], a[i] = a[i], a[k] } } helper(0) return res }

Razonamiento: El número de permutaciones de n elementos es n!. La recursión genera n! soluciones; si además se copia cada permutación el coste total puede ser O(n·n!), pero la explosión combinatoria viene de n!.

Reglas rápidas para principiantes: eliminar constantes y términos de menor orden, la base del logaritmo no importa, las reglas multiplicativa y aditiva para bucles y sumas de costes, y recordar que algoritmos que generan todas las salidas están limitados por el tamaño de la salida misma.

Sobre Q2BSTUDIO: En Q2BSTUDIO somos una empresa de desarrollo de software que crea aplicaciones a medida y software a medida para clientes que buscan soluciones escalables y seguras. Nos especializamos en inteligencia artificial aplicada a negocio, agentes IA y soluciones de ia para empresas, además de ofrecer servicios de ciberseguridad y pentesting. Si necesita una aplicación multiplataforma a medida visite desarrollo de aplicaciones y software a medida y para proyectos de inteligencia artificial empresarial conozca nuestras capacidades en inteligencia artificial para empresas.

Palabras clave integradas: aplicaciones a medida, software a medida, inteligencia artificial, ciberseguridad, servicios cloud aws y azure, servicios inteligencia de negocio, ia para empresas, agentes IA, power bi. Si desea optimizar procesos o potenciar su información con Business Intelligence y Power BI podemos ayudarle a diseñar soluciones a medida que incluyan seguridad, nube y analítica avanzada.

Conclusión: Entender Big-O ayuda a elegir algoritmos apropiados según el tamaño de los datos y los requisitos de rendimiento. Para diseño de soluciones a medida y apoyo con IA, cloud y ciberseguridad, Q2BSTUDIO ofrece servicios integrales para llevar su proyecto a producción.