material-verano-2025

la STL

C++ trae varias estructuras de datos ya implementadas, para simplificarnos la vida :)

https://en.cppreference.com/w/cpp/container

Interfaz de iteradores

Algunas de estas estructuras tienen una interfaz de iteradores, que permite recorrer los elementos de la estructura.

Conceptualmente, un iterador representa una posición en la secuencia.

s = | 2 | 3 | 5 | 8 | 13 |       (5 elementos, 6 posiciones)
    ^                    ^
    begin(s)             end(s)

Notemos que en una secuencia de N elementos, hay N+1 posiciones:

De la misma manera, un iterador corresponde a una posición entre medio de dos elementos consecutivos (o el principio o final de la secuencia)

s = | 2 | 3 | 5 | 8 | 13 |
            ^
            it

entonces *it = 5

Para manipular iteradores, podemos usar los siguientes operadores:

Por ejemplo, podemos recorrer la secuencia s e imprimir cada elemento usando un bucle for.

for (auto it = begin(s); it != end(s); ++it) {
  cout << *it << " ";
}
cout << "\n";

https://en.cppreference.com/w/cpp/iterator

Charla “C++ seasoning” de Sean Parent: https://www.youtube.com/watch?v=W2tWOdzgXHA

Ejemplo

Nos dan un arreglo de N numeros distintos a[1], a[2], …, a[N]. Encontrar la minima diferencia entre dos elementos de este arreglo.

Restricciones:

Entrada:

La primera linea contiene el entero N

La siguiente linea tiene N numeros distintos a[1], a[2], …, a[N].

Salida:

La minima diferencia entre dos elementos de a.

Ejemplo

6
9000 5004 1000 8900 5001 1100
3

Solucion naive

int a[500100];
int main() {
  int n;
  cin >> n;
  forn(i, n) {
    cin >> a[i];
  }
  int ans = abs(a[1] - a[0]);
  forn(i, n) {
    forn(j, n) {
      if (i != j) {
        ans = min(ans, abs(a[i] - a[j]));
      }
    }
  }
  cout << ans << endl;
  return 0;
}

Para mejorar la solucion naive, podemos usar std::vector, que es un arreglo dinámico.

https://en.cppreference.com/w/cpp/container/vector

Usando un vector, podemos leer la entrada de la siguiente manera, sin necesidad de escribir el maximo N.

vector<int> a(n);
forn(i, n) {
  cin >> a[i];
}

El resto del codigo es el mismo.

El codigo anterior prueba cada par de elementos dos veces, una vez como (i, j) y otra como (j, i). Podemos hacer esta comparación solo una vez.

int ans = abs(a[1] - a[0]);
forn(i, n) {
  forn(j, i) {
    ans = min(ans, abs(a[i] - a[j]);
  }
}

Esto es el doble de rapido, pero no cambia el costo asintotico, sigue siendo O(N^2).


int N;
cin >> N;

vector<int> a(n);
forn(i, n) {
  cin >> a[i];
}

int ans = abs(a[1] - a[0]);
forn(i, n) {
  forn(j, n) {
    if (i != j) {
      ans = min(ans, abs(a[i] - a[j]);
    }
  }
}
// O(N^2)

Solucion con std::set

int ans = abs(a[1] - a[0]);

set<int> s;
for (int x : a) { // O(N) iteraciones
  if (auto it = s.upper_bound(x); it != end(s)) { // O(log N)
    ans = min(ans, *it - x); // O(1)
  }
  if (auto it = s.lower_bound(x); it != begin(s)) { // O(log N)
    ans = min(ans, x - *prev(it)); // O(1)
  }
}
// total: O(N log N)

Simplificar

int ans = abs(a[1] - a[0]);

set<int> s(begin(a), end(a)); // O(1)
for (int x : a) { // O(N) iteraciones
  if (auto it = s.upper_bound(x); it != end(s)) { // O(log N)
    ans = min(ans, *it - x);
  }
}
// total: O(N log N)

<algorithm>

C++ trae tambien una biblioteca de algoritmos. Entre otros, tiene:

https://en.cppreference.com/w/cpp/algorithm

int ans = a[1] - a[0];

sort(begin(a), end(a)); // O(N log N)
forn(i, n-1) ans = min(ans, a[i+1] - a[i]); // O(N)
// total: O(N log N)

Practica