Advanced Graph Algorithms

Advanced graph algorithms including minimum spanning trees, shortest path variations, differential constraints, and layered graph techniques used in competitive programming.

Minimum Spanning Tree Algorithms

A minimum spanning tree connects all vertices with minimum total weight using exactly n-1 edges.

Kruskal's and Prim's MST Algorithms

#include <iostream>
#include <vector>
#include <algorithm>
#include <queue>
using namespace std;

// Union-Find for Kruskal's Algorithm
class UnionFind {
private:
    vector<int> parent, rank;
    
public:
    UnionFind(int n) : parent(n), rank(n, 0) {
        for (int i = 0; i < n; i++) {
            parent[i] = i;
        }
    }
    
    int find(int x) {
        if (parent[x] != x) {
            parent[x] = find(parent[x]); // Path compression
        }
        return parent[x];
    }
    
    bool unite(int x, int y) {
        int px = find(x), py = find(y);
        if (px == py) return false;
        
        // Union by rank
        if (rank[px] < rank[py]) swap(px, py);
        parent[py] = px;
        if (rank[px] == rank[py]) rank[px]++;
        
        return true;
    }
};

// Kruskal's Algorithm
struct Edge {
    int u, v, weight;
    bool operator<(const Edge& other) const {
        return weight < other.weight;
    }
};

class KruskalMST {
private:
    vector<Edge> edges;
    int n;
    
public:
    KruskalMST(int vertices) : n(vertices) {}
    
    void addEdge(int u, int v, int w) {
        edges.push_back({u, v, w});
    }
    
    pair<int, vector<Edge>> findMST() {
        sort(edges.begin(), edges.end());
        UnionFind uf(n);
        
        vector<Edge> mst;
        int totalWeight = 0;
        
        for (const Edge& e : edges) {
            if (uf.unite(e.u, e.v)) {
                mst.push_back(e);
                totalWeight += e.weight;
                if (mst.size() == n - 1) break;
            }
        }
        
        return {totalWeight, mst};
    }
};

int main() {
    cout << "Testing Kruskal's MST Algorithm:" << endl;
    
    KruskalMST kruskal(4);
    kruskal.addEdge(0, 1, 10);
    kruskal.addEdge(0, 2, 6);
    kruskal.addEdge(0, 3, 5);
    kruskal.addEdge(1, 3, 15);
    kruskal.addEdge(2, 3, 4);
    
    auto [weight, mst] = kruskal.findMST();
    
    cout << "MST Total Weight: " << weight << endl;
    cout << "MST Edges:" << endl;
    for (const Edge& e : mst) {
        cout << e.u << " - " << e.v << " (weight: " << e.weight << ")" << endl;
    }
    
    return 0;
}

Kruskal's algorithm sorts edges by weight and uses Union-Find to detect cycles, building MST incrementally.

Advanced Shortest Path Algorithms

Beyond basic Dijkstra and Floyd-Warshall, advanced techniques handle special constraints and optimizations.

Dijkstra with Path Reconstruction

#include <iostream>
#include <vector>
#include <queue>
#include <climits>
using namespace std;

class DijkstraWithPath {
private:
    vector<vector<pair<int, int>>> graph;
    int n;
    
public:
    DijkstraWithPath(int vertices) : n(vertices), graph(vertices) {}
    
    void addEdge(int u, int v, int w) {
        graph[u].push_back({v, w});
    }
    
    pair<vector<int>, vector<int>> shortestPath(int start) {
        vector<int> dist(n, INT_MAX);
        vector<int> parent(n, -1);
        priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> pq;
        
        dist[start] = 0;
        pq.push({0, start});
        
        while (!pq.empty()) {
            int d = pq.top().first;
            int u = pq.top().second;
            pq.pop();
            
            if (d > dist[u]) continue;
            
            for (auto& edge : graph[u]) {
                int v = edge.first;
                int w = edge.second;
                
                if (dist[u] + w < dist[v]) {
                    dist[v] = dist[u] + w;
                    parent[v] = u;
                    pq.push({dist[v], v});
                }
            }
        }
        
        return {dist, parent};
    }
    
    vector<int> reconstructPath(const vector<int>& parent, int start, int end) {
        vector<int> path;
        int curr = end;
        
        while (curr != -1) {
            path.push_back(curr);
            curr = parent[curr];
        }
        
        reverse(path.begin(), path.end());
        
        // Check if path exists
        if (path[0] != start) {
            return {};
        }
        
        return path;
    }
};

int main() {
    cout << "Testing Dijkstra with Path Reconstruction:" << endl;
    
    DijkstraWithPath dijkstra(5);
    dijkstra.addEdge(0, 1, 4);
    dijkstra.addEdge(0, 2, 2);
    dijkstra.addEdge(1, 2, 1);
    dijkstra.addEdge(1, 3, 5);
    dijkstra.addEdge(2, 3, 8);
    dijkstra.addEdge(2, 4, 10);
    dijkstra.addEdge(3, 4, 2);
    
    auto [dist, parent] = dijkstra.shortestPath(0);
    
    cout << "Shortest distances from vertex 0:" << endl;
    for (int i = 0; i < dist.size(); i++) {
        cout << "To " << i << ": " << dist[i] << endl;
    }
    
    cout << "\nPath from 0 to 4:" << endl;
    vector<int> path = dijkstra.reconstructPath(parent, 0, 4);
    for (int i = 0; i < path.size(); i++) {
        cout << path[i];
        if (i < path.size() - 1) cout << " -> ";
    }
    cout << endl;
    
    return 0;
}

Enhanced Dijkstra algorithm that not only finds shortest distances but also reconstructs the actual paths using parent pointers.