Skip to content

Commit

Permalink
4.11 shortest paths (Bellman-Ford)
Browse files Browse the repository at this point in the history
  • Loading branch information
landerrosette committed Oct 20, 2024
1 parent d10ceb0 commit 7dc8f1e
Show file tree
Hide file tree
Showing 14 changed files with 122 additions and 22 deletions.
34 changes: 34 additions & 0 deletions BellmanFordSP.cpp
Original file line number Diff line number Diff line change
@@ -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);
}
}
31 changes: 31 additions & 0 deletions BellmanFordSP.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#ifndef BELLMANFORDSP_H
#define BELLMANFORDSP_H


#include "SP.h"
#include <vector>
#include <list>

class BellmanFordSP : public SP {
private:
std::vector<bool> onQ; // 顶点是否存在与队列中
std::list<int> queue; // 正在被放松的顶点
int cost = 0; // relax()的调用次数
std::list<DirectedEdge> 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<DirectedEdge> negativeCycle() const { return cycle; }
};


#endif //BELLMANFORDSP_H
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -83,4 +83,6 @@ add_executable(algs4
DijkstraSP.h
AcyclicSP.cpp
AcyclicSP.h
BellmanFordSP.cpp
BellmanFordSP.h
)
2 changes: 1 addition & 1 deletion DijkstraSP.cpp
Original file line number Diff line number Diff line change
@@ -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]);
}
Expand Down
2 changes: 1 addition & 1 deletion DijkstraSP.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ class DijkstraSP : public SP {
private:
IndexMinPQ<double> 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);
Expand Down
31 changes: 20 additions & 11 deletions DirectedCycle.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,27 +8,26 @@
#include "DirectedEdge.h"
#include <type_traits>

template<typename T>
class DirectedCycle {
private:
std::vector<bool> marked;
std::vector<int> edgeTo;
std::list<int> cycle_;
std::vector<T> edgeTo;
std::list<T> cycle_;
std::vector<bool> onStack; // 栈上的所有顶点

template<typename T>
void dfs(const GraphBase<T> &G, int v);

public:
template<typename T>
DirectedCycle(const GraphBase<T> &G);

bool hasCycle() const { return !cycle_.empty(); }

std::list<int> cycle() const { return cycle_; }
std::list<T> cycle() const { return cycle_; }
};

template<typename T>
void DirectedCycle::dfs(const GraphBase<T> &G, int v) {
void DirectedCycle<T>::dfs(const GraphBase<T> &G, int v) {
onStack[v] = true;
marked[v] = true;
for (const auto &e: G.adj(v)) {
Expand All @@ -38,19 +37,29 @@ void DirectedCycle::dfs(const GraphBase<T> &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<std::decay_t<decltype(e)>, 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<typename T>
DirectedCycle::DirectedCycle(const GraphBase<T> &G) : marked(G.V()), edgeTo(G.V()), onStack(G.V()) {
DirectedCycle<T>::DirectedCycle(const GraphBase<T> &G) : marked(G.V()), edgeTo(G.V()), onStack(G.V()) {
for (int v = 0; v < G.V(); ++v) {
if (!marked[v]) dfs(G, v);
}
Expand Down
4 changes: 0 additions & 4 deletions KruskalMST.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,3 @@ KruskalMST::KruskalMST(const EdgeWeightedGraph &G) {
mst.push_back(*e);
}
}

std::list<Edge> KruskalMST::edges() const {
return mst;
}
2 changes: 1 addition & 1 deletion KruskalMST.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class KruskalMST : public MST {
public:
KruskalMST(const EdgeWeightedGraph &G);

std::list<Edge> edges() const override;
std::list<Edge> edges() const override { return mst; }
};


Expand Down
2 changes: 1 addition & 1 deletion MergeBU.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class MergeBU : public Merge {
template<typename T>
void MergeBU::sort(std::vector<T> &a) {
int N = a.size();
aux<T> = std::vector<T>(a.size());
aux<T> = std::vector<T>(N);
for (int sz = 1; sz < N; sz *= 2) {
// 子数组大小sz
for (int lo = 0; lo < N - sz; lo += 2 * sz) {
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion SP.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}

Expand Down
4 changes: 3 additions & 1 deletion SP.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
17 changes: 17 additions & 0 deletions data/tinyEWDn.txt
Original file line number Diff line number Diff line change
@@ -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
9 changes: 8 additions & 1 deletion main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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);
Expand All @@ -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;
Expand Down

0 comments on commit 7dc8f1e

Please sign in to comment.