From 7dc8f1eb5b565d35c4cd661e6658f706e1edf096 Mon Sep 17 00:00:00 2001 From: landerrosette <57791410+landerrosette@users.noreply.github.com> Date: Sun, 20 Oct 2024 21:26:45 +0800 Subject: [PATCH] 4.11 shortest paths (Bellman-Ford) --- BellmanFordSP.cpp | 34 ++++++++++++++++++++++++++++++++++ BellmanFordSP.h | 31 +++++++++++++++++++++++++++++++ CMakeLists.txt | 2 ++ DijkstraSP.cpp | 2 +- DijkstraSP.h | 2 +- DirectedCycle.h | 31 ++++++++++++++++++++----------- KruskalMST.cpp | 4 ---- KruskalMST.h | 2 +- MergeBU.h | 2 +- README.md | 1 + SP.cpp | 3 ++- SP.h | 4 +++- data/tinyEWDn.txt | 17 +++++++++++++++++ main.cpp | 9 ++++++++- 14 files changed, 122 insertions(+), 22 deletions(-) create mode 100644 BellmanFordSP.cpp create mode 100644 BellmanFordSP.h create mode 100644 data/tinyEWDn.txt diff --git a/BellmanFordSP.cpp b/BellmanFordSP.cpp new file mode 100644 index 0000000..ab133a8 --- /dev/null +++ b/BellmanFordSP.cpp @@ -0,0 +1,34 @@ +#include "BellmanFordSP.h" +#include "DirectedCycle.h" + +void BellmanFordSP::onRelaxationSuccess(const EdgeWeightedDigraph &G, int v, const DirectedEdge &e, int w) { + if (!onQ[w]) { + queue.push_back(w); + onQ[w] = true; + } +} + +void BellmanFordSP::afterRelaxation(const EdgeWeightedDigraph &G, int v, const DirectedEdge &e, int w) { + if (cost++ % G.V() == 0) findNegativeCycle(); +} + +void BellmanFordSP::findNegativeCycle() { + int V = edgeTo.size(); + EdgeWeightedDigraph spt(V); + for (int v = 0; v < V; ++v) { + if (edgeTo[v]) spt.addEdge(edgeTo[v]); + } + DirectedCycle cf(spt); + cycle = cf.cycle(); +} + +BellmanFordSP::BellmanFordSP(const EdgeWeightedDigraph &G, int s) : SP(G, s), onQ(G.V()) { + queue.push_back(s); + onQ[s] = true; + while (!queue.empty() && !hasNegativeCycle()) { + int v = queue.front(); + queue.pop_front(); + onQ[v] = false; + relax(G, v); + } +} diff --git a/BellmanFordSP.h b/BellmanFordSP.h new file mode 100644 index 0000000..a6476af --- /dev/null +++ b/BellmanFordSP.h @@ -0,0 +1,31 @@ +#ifndef BELLMANFORDSP_H +#define BELLMANFORDSP_H + + +#include "SP.h" +#include +#include + +class BellmanFordSP : public SP { +private: + std::vector onQ; // 顶点是否存在与队列中 + std::list queue; // 正在被放松的顶点 + int cost = 0; // relax()的调用次数 + std::list cycle; // edgeTo[]表示的子图是否含有负权重环 + + void onRelaxationSuccess(const EdgeWeightedDigraph &G, int v, const DirectedEdge &e, int w) override; + + void afterRelaxation(const EdgeWeightedDigraph &G, int v, const DirectedEdge &e, int w) override; + + void findNegativeCycle(); + +public: + BellmanFordSP(const EdgeWeightedDigraph &G, int s); + + bool hasNegativeCycle() const { return !cycle.empty(); } + + std::list negativeCycle() const { return cycle; } +}; + + +#endif //BELLMANFORDSP_H diff --git a/CMakeLists.txt b/CMakeLists.txt index d15aa2d..71458ee 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -83,4 +83,6 @@ add_executable(algs4 DijkstraSP.h AcyclicSP.cpp AcyclicSP.h + BellmanFordSP.cpp + BellmanFordSP.h ) diff --git a/DijkstraSP.cpp b/DijkstraSP.cpp index 65f6d14..eaa2a2c 100644 --- a/DijkstraSP.cpp +++ b/DijkstraSP.cpp @@ -1,6 +1,6 @@ #include "DijkstraSP.h" -void DijkstraSP::onRelax(const DirectedEdge &e, int w) { +void DijkstraSP::onRelaxationSuccess(const EdgeWeightedDigraph &G, int v, const DirectedEdge &e, int w) { if (pq.contains(w)) pq.change(w, distTo_[w]); else pq.insert(w, distTo_[w]); } diff --git a/DijkstraSP.h b/DijkstraSP.h index 70a4e0a..4c417f1 100644 --- a/DijkstraSP.h +++ b/DijkstraSP.h @@ -9,7 +9,7 @@ class DijkstraSP : public SP { private: IndexMinPQ pq; - void onRelax(const DirectedEdge &e, int w) override; + void onRelaxationSuccess(const EdgeWeightedDigraph &G, int v, const DirectedEdge &e, int w) override; public: DijkstraSP(const EdgeWeightedDigraph &G, int s); diff --git a/DirectedCycle.h b/DirectedCycle.h index 50d0d82..60749e9 100644 --- a/DirectedCycle.h +++ b/DirectedCycle.h @@ -8,27 +8,26 @@ #include "DirectedEdge.h" #include +template class DirectedCycle { private: std::vector marked; - std::vector edgeTo; - std::list cycle_; + std::vector edgeTo; + std::list cycle_; std::vector onStack; // 栈上的所有顶点 - template void dfs(const GraphBase &G, int v); public: - template DirectedCycle(const GraphBase &G); bool hasCycle() const { return !cycle_.empty(); } - std::list cycle() const { return cycle_; } + std::list cycle() const { return cycle_; } }; template -void DirectedCycle::dfs(const GraphBase &G, int v) { +void DirectedCycle::dfs(const GraphBase &G, int v) { onStack[v] = true; marked[v] = true; for (const auto &e: G.adj(v)) { @@ -38,19 +37,29 @@ void DirectedCycle::dfs(const GraphBase &G, int v) { if (hasCycle()) { return; } else if (!marked[w]) { - edgeTo[w] = v; + edgeTo[w] = e; dfs(G, w); } else if (onStack[w]) { - for (int x = v; x != w; x = edgeTo[x]) cycle_.push_front(x); - cycle_.push_front(w); - cycle_.push_front(v); + if constexpr (std::is_same_v, DirectedEdge>) { + auto x = e; + for (; x.from() != w; x = edgeTo[x.from()]) { + cycle_.push_front(x); + } + cycle_.push_front(x); + } else { + for (int x = v; x != w; x = edgeTo[x]) { + cycle_.push_front(x); + } + cycle_.push_front(w); + cycle_.push_front(v); + } } } onStack[v] = false; } template -DirectedCycle::DirectedCycle(const GraphBase &G) : marked(G.V()), edgeTo(G.V()), onStack(G.V()) { +DirectedCycle::DirectedCycle(const GraphBase &G) : marked(G.V()), edgeTo(G.V()), onStack(G.V()) { for (int v = 0; v < G.V(); ++v) { if (!marked[v]) dfs(G, v); } diff --git a/KruskalMST.cpp b/KruskalMST.cpp index aca4642..fd0dbde 100644 --- a/KruskalMST.cpp +++ b/KruskalMST.cpp @@ -14,7 +14,3 @@ KruskalMST::KruskalMST(const EdgeWeightedGraph &G) { mst.push_back(*e); } } - -std::list KruskalMST::edges() const { - return mst; -} diff --git a/KruskalMST.h b/KruskalMST.h index 21611d5..34e1088 100644 --- a/KruskalMST.h +++ b/KruskalMST.h @@ -13,7 +13,7 @@ class KruskalMST : public MST { public: KruskalMST(const EdgeWeightedGraph &G); - std::list edges() const override; + std::list edges() const override { return mst; } }; diff --git a/MergeBU.h b/MergeBU.h index 795d032..02af4e3 100644 --- a/MergeBU.h +++ b/MergeBU.h @@ -14,7 +14,7 @@ class MergeBU : public Merge { template void MergeBU::sort(std::vector &a) { int N = a.size(); - aux = std::vector(a.size()); + aux = std::vector(N); for (int sz = 1; sz < N; sz *= 2) { // 子数组大小sz for (int lo = 0; lo < N - sz; lo += 2 * sz) { diff --git a/README.md b/README.md index 2031f59..7d8bbf4 100644 --- a/README.md +++ b/README.md @@ -44,6 +44,7 @@ incorporating modern C++ practices. - **4.8** Minimum spanning tree (Kruskal): [KruskalMST.h](KruskalMST.h) | [KruskalMST.cpp](KruskalMST.cpp) - **4.9** Shortest paths (Dijkstra): [DijkstraSP.h](DijkstraSP.h) | [DijkstraSP.cpp](DijkstraSP.cpp) - **4.10** Shortest paths in DAGs: [AcyclicSP.h](AcyclicSP.h) | [AcyclicSP.cpp](AcyclicSP.cpp) +- **4.11** Shortest paths (Bellman-Ford): [BellmanFord.h](BellmanFordSP.h) | [BellmanFord.cpp](BellmanFordSP.cpp) - ... ## Build and Run diff --git a/SP.cpp b/SP.cpp index 1f9a90c..775d856 100644 --- a/SP.cpp +++ b/SP.cpp @@ -6,8 +6,9 @@ void SP::relax(const EdgeWeightedDigraph &G, int v) { if (distTo_[w] > distTo_[v] + e.weight()) { distTo_[w] = distTo_[v] + e.weight(); edgeTo[w] = e; - onRelax(e, w); + onRelaxationSuccess(G, v, e, w); } + afterRelaxation(G, v, e, w); } } diff --git a/SP.h b/SP.h index bfb6c20..b5ac1e5 100644 --- a/SP.h +++ b/SP.h @@ -15,7 +15,9 @@ class SP { void relax(const EdgeWeightedDigraph &G, int v); - virtual void onRelax(const DirectedEdge &e, int w) {} + virtual void onRelaxationSuccess(const EdgeWeightedDigraph &G, int v, const DirectedEdge &e, int w) {} + + virtual void afterRelaxation(const EdgeWeightedDigraph &G, int v, const DirectedEdge &e, int w) {} public: SP(const EdgeWeightedDigraph &G, int s); diff --git a/data/tinyEWDn.txt b/data/tinyEWDn.txt new file mode 100644 index 0000000..65eef6a --- /dev/null +++ b/data/tinyEWDn.txt @@ -0,0 +1,17 @@ +8 +15 +4 5 0.35 +5 4 0.35 +4 7 0.37 +5 7 0.28 +7 5 0.28 +5 1 0.32 +0 4 0.38 +0 2 0.26 +7 3 0.39 +1 3 0.29 +2 7 0.34 +6 2 -1.20 +3 6 0.52 +6 0 -1.40 +6 4 -1.25 diff --git a/main.cpp b/main.cpp index 88e841c..a4aba09 100644 --- a/main.cpp +++ b/main.cpp @@ -33,6 +33,7 @@ #include "KruskalMST.h" #include "DijkstraSP.h" #include "AcyclicSP.h" +#include "BellmanFordSP.h" #include "tests/testUF.h" #include "tests/testSort.h" #include "tests/testPQ.h" @@ -185,10 +186,12 @@ int main(int argc, char *argv[]) { std::cout << "================================================" << "\n"; testMST(G, KruskalMST(G)); std::cout << "================================================" << "\n"; - } else if (dataFilePath.filename() == "tinyEWD.txt" || dataFilePath.filename() == "tinyEWDAG.txt") { + } else if (dataFilePath.filename() == "tinyEWD.txt" || dataFilePath.filename() == "tinyEWDAG.txt" || dataFilePath. + filename() == "tinyEWDn.txt") { // 测试加权有向图相关算法 // Example: ./algs4 ../data/tinyEWD.txt 0 // Example: ./algs4 ../data/tinyEWDAG.txt 5 + // Example: ./algs4 ../data/tinyEWDn.txt 0 EdgeWeightedDigraph G([&dataFilePath] { std::cout << "Reading graph from file" << "\n"; return std::ifstream(dataFilePath); @@ -203,6 +206,10 @@ int main(int argc, char *argv[]) { std::cout << "Testing 4.10 shortest paths in DAGs" << "\n"; std::cout << "================================================" << "\n"; testSP(G, s, AcyclicSP(G, s)); + std::cout << "================================================" << "\n" << "\n"; + std::cout << "Testing 4.11 shortest paths (Bellman-Ford)" << "\n"; + std::cout << "================================================" << "\n"; + testSP(G, s, BellmanFordSP(G, s)); std::cout << "================================================" << "\n"; } return 0;