Skip to content
Clases

Análisis de Código

Es vital entender qué tan rápido “explotan” las funciones conforme NN crece.

Orden de mejor a peor: O(1)<O(logN)<O(N)<O(N)<O(NlogN)<O(N2)<O(2N)<O(N!)O(1) < O(\log N) < O(\sqrt{N}) < O(N) < O(N \log N) < O(N^2) < O(2^N) < O(N!)


No importa si N=10N = 10 o N=109N = 10^9, tarda lo mismo.

// Acceso a array o matemáticas simples
int obtenerElemento(vector<int>& arr, int index) {
return arr[index]; // Una sola operación
}

El problema se reduce a la mitad en cada paso. Típico de Binary Search.

  • Si N=1,000,000N=1,000,000, solo toma 20≈20 pasos (220106)(220≈106).
// Dividir N sucesivamente
while (N > 0) {
N /= 2; // El espacio de búsqueda se reduce a la mitad
// Operaciones constantes aquí...
}

Común en teoría de números (Test de primalidad, descomposición en factores).

  • Si N=109N=109, toma 31,622≈31,622 pasos. Mucho mejor que O(N)O(N).
// Test de primalidad ingenuo
bool esPrimo(int n) {
if (n < 2) return false;
for (long long i = 2; i * i <= n; ++i) { // i llega hasta sqrt(n)
if (n % i == 0) return false;
}
return true;
}

Recorremos la entrada una vez. Si duplicas N, duplicas el tiempo.

// Suma de elementos o Búsqueda lineal
long long suma = 0;
for (int i = 0; i < N; ++i) {
suma += arr[i];
}

O(NlogN)O(N \log N) - Linealítmico

Section titled “O(Nlog⁡N)O(N \log N)O(NlogN) - Linealítmico”

El estándar de los ordenamientos eficientes (std::sort, Merge Sort) y estructuras de datos basadas en árboles (Maps, Sets, Segment Trees).

  • Si N=106N = 10^6, toma 2×107\approx 2 \times 10^7 operaciones. Es el límite seguro para 1 segundo con N=2105N=2 \cdot 10^5.
// La mayoría de los ordenamientos eficientes
sort(arr.begin(), arr.end()); // C++ usa Introsort (mezcla de Quick, Heap y Insertion)

Bucles anidados. Típico de fuerza bruta o algoritmos sencillos como Bubble Sort.

  • Peligroso para N>104 (10,000 elementos).
// Recorrer todos los pares posibles
for (int i = 0; i < N; ++i) {
for (int j = 0; j < N; ++j) {
// Operación con arr[i] y arr[j]
cout << i << ", " << j << endl;
}
}

Recursión ramificada. Por cada elemento, generamos dos ramas nuevas. Típico de generar Subsets (Subconjuntos) o Fibonacci recursivo sin DP.

  • Solo viable para N2025N≤20−25.
// Fibonacci recursivo ineficiente
int fib(int n) {
if (n <= 1) return n;
return fib(n-1) + fib(n-2); // Dos llamadas por cada n
}

Generar todas las permutaciones posibles de un arreglo. Crece extremadamente rápido.

  • Solo viable para N1011N≤10−11.
// Generar todas las formas de ordenar el arreglo
sort(arr.begin(), arr.end());
do {
// Procesar permutación actual
} while (next_permutation(arr.begin(), arr.end()));