From 262883c274a0a99283449445cb20ba626400832b Mon Sep 17 00:00:00 2001 From: Srishti Soni <92056170+shimmer12@users.noreply.github.com> Date: Sun, 12 Oct 2025 01:02:05 +0530 Subject: [PATCH] =?UTF-8?q?Implement=20D'Esopo=E2=80=93Pape=20algorithm=20?= =?UTF-8?q?for=20shortest=20paths?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This implementation includes the D'Esopo–Pape algorithm for finding single-source shortest paths in a graph with non-negative edge weights, utilizing a deque for efficient processing. It also contains unit tests to verify the correctness of the algorithm with example graphs. --- ...23Pape_(Pape)_Algorithm_shortest_path.cpp" | 148 ++++++++++++++++++ 1 file changed, 148 insertions(+) create mode 100644 "graph/D'Esopo\342\200\223Pape_(Pape)_Algorithm_shortest_path.cpp" diff --git "a/graph/D'Esopo\342\200\223Pape_(Pape)_Algorithm_shortest_path.cpp" "b/graph/D'Esopo\342\200\223Pape_(Pape)_Algorithm_shortest_path.cpp" new file mode 100644 index 0000000000..e8d76a6682 --- /dev/null +++ "b/graph/D'Esopo\342\200\223Pape_(Pape)_Algorithm_shortest_path.cpp" @@ -0,0 +1,148 @@ +/** + * @file + * @brief D'Esopo–Pape (Pape) Algorithm for Single-Source Shortest Paths + * + * @details + * Computes shortest path distances from a source in a graph with + * non-negative edge weights. Often performs very well in practice on sparse + * graphs. Works for directed or undirected graphs (pass edges accordingly). + * + * - Typical complexity: close to O(E) on many inputs (amortized). + * - Worst-case complexity: may degrade (can be exponential on contrived graphs). + * - Space: O(V + E) for adjacency + O(V) for bookkeeping. + * + * References: + * - Original papers and common descriptions of the Pape algorithm + * - Compare with Dijkstra (binary heap) and SPFA variants + */ + +#include +#include +#include +#include +#include +#include + +namespace graph { + +/** + * @brief Adjacency list type: for each vertex, list of (neighbor, weight). + */ +using Adj = std::vector>>; + +/** + * @brief Build adjacency list from an edge list. + * @param v number of vertices + * @param edges edges as {u, v, w} + * @param undirected if true, inserts both (u->v) and (v->u) + * @return adjacency list + */ +Adj make_adj(int v, const std::vector>& edges, bool undirected) { + Adj g(v); + g.reserve(v); + for (const auto& e : edges) { + int a = e[0], b = e[1], w = e[2]; + g[a].push_back({b, w}); + if (undirected) g[b].push_back({a, w}); + } + return g; +} + +/** + * @brief D'Esopo–Pape SSSP using a deque. + * + * @param g adjacency list + * @param src source vertex (0-based) + * @return dist vector where dist[i] is the shortest distance from src to i + * + * @note Assumes all weights are non-negative. + */ +std::vector pape_shortest_paths(const Adj& g, int src) { + const int n = static_cast(g.size()); + const int INF = std::numeric_limits::max(); + + std::vector dist(n, INF); + std::vector in_queue(n, 0); // 0: not in deque, 1: in deque + std::vector seen(n, 0); // whether vertex has ever been enqueued + + std::deque dq; + dist[src] = 0; + dq.push_back(src); + in_queue[src] = 1; + seen[src] = 1; + + while (!dq.empty()) { + int u = dq.front(); + dq.pop_front(); + in_queue[u] = 0; + + for (const auto& [v, w] : g[u]) { + if (dist[u] != INF && dist[v] > dist[u] + w) { + dist[v] = dist[u] + w; + + if (!in_queue[v]) { + in_queue[v] = 1; + if (seen[v]) { + // If we've seen v before, prioritize it + dq.push_front(v); + } else { + // First time seen -> push to back + dq.push_back(v); + seen[v] = 1; + } + } + } + } + } + return dist; +} + +} // namespace graph + +/* -------------------------- Unit Tests & Demo -------------------------- */ + +static void test_examples() { + using graph::make_adj; + using graph::pape_shortest_paths; + + // Example 1 + { + int v = 5, src = 0; + std::vector> edges = { + {0, 1, 5}, {1, 2, 1}, {1, 3, 2}, {2, 4, 1}, {4, 3, 1} + }; + auto g = make_adj(v, edges, /*undirected=*/true); + auto dist = pape_shortest_paths(g, src); + std::vector expected = {0, 5, 6, 7, 7}; + assert(dist == expected); + } + + // Example 2 + { + int v = 5, src = 0; + std::vector> edges = { + {0, 1, 4}, {0, 2, 8}, {1, 4, 6}, {2, 3, 2}, {3, 4, 10} + }; + auto g = make_adj(v, edges, /*undirected=*/true); + auto dist = graph::pape_shortest_paths(g, src); + std::vector expected = {0, 4, 8, 10, 10}; + assert(dist == expected); + } +} + +int main() { + test_examples(); + + // Print one example to STDOUT (matching the article's output style) + int v = 5, src = 0; + std::vector> edges = { + {0, 1, 5}, {1, 2, 1}, {1, 3, 2}, {2, 4, 1}, {4, 3, 1} + }; + auto g = graph::make_adj(v, edges, /*undirected=*/true); + auto dist = graph::pape_shortest_paths(g, src); + + for (int i = 0; i < v; ++i) { + std::cout << dist[i] << (i + 1 == v ? '\n' : ' '); + } + return 0; +}