Algoritmos Kruskal y Prim para Árboles de Expansión Mínima

Algoritmo de Kruskal

Este método ordena todas las aristas por peso asecndente y las añade al árbol siempre que no generen ciclos, utilizando una estructura de Union-Find para gestionar componentes conexas. La complejidad temporal es O(m log m) debido a la ordenación.

struct Arista {
    int origen, destino, costo;
} aristas[10000];

bool ordenar_por_costo(const Arista &a, const Arista &b) {
    return a.costo < b.costo;
}

int raiz[10000];
int buscar_raiz(int nodo) {
    if (raiz[nodo] == nodo) return nodo;
    return raiz[nodo] = buscar_raiz(raiz[nodo]);
}

int kruskal() {
    int costo_total = 0;
    for (int i = 1; i <= n; ++i) raiz[i] = i;
    sort(aristas + 1, aristas + m + 1, ordenar_por_costo);
    for (int i = 1; i <= m; ++i) {
        int raiz_origen = buscar_raiz(aristas[i].origen);
        int raiz_destino = buscar_raiz(aristas[i].destino);
        if (raiz_origen == raiz_destino) continue;
        raiz[raiz_origen] = raiz_destino;
        costo_total += aristas[i].costo;
    }
    return costo_total;
}

Algoritmo de Prim

Prim construye el árbol progresivamente desde un vértice inicial, seleccionando en cada paso la arista más barata que conecta un vértice dentro del árbol a uno externo. Utiliza un montículo mínimo para eficiencia, logrando una complejidad de O(m log n).

typedef pair<int int=""> Par;
priority_queue<par vector="">, greater<par>> cola_prioridad;

int prim() {
    int distancia[10000];
    bool visitado[10000] = {false};
    for (int i = 1; i <= n; ++i) distancia[i] = 1e9;
    distancia[1] = 0;
    cola_prioridad.push({0, 1});
    int vertices_agregados = 0, costo_total = 0;

    while (!cola_prioridad.empty()) {
        auto [costo_actual, vertice] = cola_prioridad.top();
        cola_prioridad.pop();
        if (visitado[vertice]) continue;
        visitado[vertice] = true;
        costo_total += costo_actual;
        vertices_agregados++;
        if (vertices_agregados == n) break;

        for (int i = cabeza[vertice]; i != -1; i = siguiente[i]) {
            int vecino = destino[i];
            if (!visitado[vecino] && peso[i] < distancia[vecino]) {
                distancia[vecino] = peso[i];
                cola_prioridad.push({peso[i], vecino});
            }
        }
    }
    return (vertices_agregados < n) ? -1 : costo_total;
}</par></par></int>

Implementación de la lista de adyacencia

Para Prim, se usa una lista de adyacencia enlazada estática para acceder rápidamente a los vecinos.

int contador_aristas = 0;
int cabeza[10000], siguiente[20000], destino[20000], peso[20000];

void agregar_arista(int u, int v, int w) {
    destino[contador_aristas] = v;
    peso[contador_aristas] = w;
    siguiente[contador_aristas] = cabeza[u];
    cabeza[u] = contador_aristas++;
}

Kruskal es más sencillo de implementar con ordenación de aristas, mientras que Prim requiere manejar distancias y un montículo, pero ambos son efectivos para grafos densos y dispersos respectivamente.

Etiquetas: Kruskal Prim minimum-spanning-tree union-find graph-algorithms

Publicado el 7-1 23:21