Skip to content

Commit b811a36

Browse files
authored
feat : Add the Bellman-Ford Shortest Path Algorithm in R (#192)
1 parent 47ff5ed commit b811a36

File tree

2 files changed

+147
-0
lines changed

2 files changed

+147
-0
lines changed

DIRECTORY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
* [Breadth First Search](https://github.com/TheAlgorithms/R/blob/HEAD/graph_algorithms/breadth_first_search.r)
5252
* [Depth First Search](https://github.com/TheAlgorithms/R/blob/HEAD/graph_algorithms/depth_first_search.r)
5353
* [Dijkstra Shortest Path](https://github.com/TheAlgorithms/R/blob/HEAD/graph_algorithms/dijkstra_shortest_path.r)
54+
* [Bellman-Ford Shortest Path](https://github.com/TheAlgorithms/R/blob/HEAD/graph_algorithms/bellman_ford_shortest_path.r)
5455

5556
## Mathematics
5657
* [Amicable Numbers](https://github.com/TheAlgorithms/R/blob/HEAD/mathematics/amicable_numbers.r)
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
# Bellman-Ford Shortest Path Algorithm
2+
#
3+
# The Bellman-Ford algorithm computes shortest paths from a single source vertex to
4+
# all other vertices in a weighted graph. Unlike Dijkstra's algorithm, Bellman-Ford
5+
# supports graphs with negative edge weights and can detect negative-weight cycles.
6+
#
7+
# Time Complexity: O(V * E)
8+
# Space Complexity: O(V)
9+
#
10+
# Input: graph as an adjacency list where each entry is a list of edges with fields
11+
# `vertex` and `weight`, and `source` vertex index (integer)
12+
# Output: A list containing distances, predecessors, and a flag indicating whether
13+
# a negative cycle was detected
14+
15+
bellman_ford_shortest_path <- function(graph, source) {
16+
# Collect all vertices (numeric indices expected)
17+
all_vertices <- unique(c(names(graph), unlist(lapply(graph, function(x) sapply(x, function(e) e$vertex)))))
18+
# Convert to numeric vector
19+
all_vertices <- as.numeric(all_vertices)
20+
num_vertices <- max(all_vertices)
21+
22+
# Initialize distances and predecessors
23+
distances <- rep(Inf, num_vertices)
24+
predecessor <- rep(-1, num_vertices)
25+
26+
distances[source] <- 0
27+
28+
# Relax edges repeatedly (V-1 times)
29+
for (i in 1:(num_vertices - 1)) {
30+
updated <- FALSE
31+
# Iterate all edges
32+
for (u_char in names(graph)) {
33+
u <- as.numeric(u_char)
34+
for (edge in graph[[u_char]]) {
35+
v <- edge$vertex
36+
w <- edge$weight
37+
if (distances[u] != Inf && distances[u] + w < distances[v]) {
38+
distances[v] <- distances[u] + w
39+
predecessor[v] <- u
40+
updated <- TRUE
41+
}
42+
}
43+
}
44+
# If no update in this pass, we can stop early
45+
if (!updated) break
46+
}
47+
48+
# Check for negative-weight cycles: if we can still relax, there is a negative cycle
49+
negative_cycle <- FALSE
50+
for (u_char in names(graph)) {
51+
u <- as.numeric(u_char)
52+
for (edge in graph[[u_char]]) {
53+
v <- edge$vertex
54+
w <- edge$weight
55+
if (distances[u] != Inf && distances[u] + w < distances[v]) {
56+
negative_cycle <- TRUE
57+
break
58+
}
59+
}
60+
if (negative_cycle) break
61+
}
62+
63+
return(list(
64+
distances = distances,
65+
predecessor = predecessor,
66+
negative_cycle = negative_cycle
67+
))
68+
}
69+
70+
# Helper to reconstruct the shortest path from source to target
71+
get_bellman_ford_path <- function(result, source, target) {
72+
if (result$negative_cycle) {
73+
return(list(path = NULL, distance = NA, message = "Negative-weight cycle detected; shortest path undefined"))
74+
}
75+
76+
distances <- result$distances
77+
predecessor <- result$predecessor
78+
79+
if (is.infinite(distances[target])) {
80+
return(list(path = NULL, distance = Inf, message = "Target not reachable from source"))
81+
}
82+
83+
path <- c()
84+
current <- target
85+
while (current != -1) {
86+
path <- c(current, path)
87+
if (current == source) break
88+
current <- predecessor[current]
89+
}
90+
91+
return(list(path = path, distance = distances[target]))
92+
}
93+
94+
# Example usage and tests
95+
cat("=== Bellman-Ford Shortest Path Algorithm ===\n")
96+
97+
# Example graph with negative edges but no negative cycle
98+
# Graph structure:
99+
# 1 -> 2 (6), 1 -> 3 (5), 1 -> 4 (5)
100+
# 2 -> 5 (-1)
101+
# 3 -> 2 (-2), 3 -> 5 (1)
102+
# 4 -> 3 (-2), 4 -> 6 (-1)
103+
# 5 -> 6 (3)
104+
# 6 -> (none)
105+
bf_graph <- list(
106+
"1" = list(list(vertex = 2, weight = 6), list(vertex = 3, weight = 5), list(vertex = 4, weight = 5)),
107+
"2" = list(list(vertex = 5, weight = -1)),
108+
"3" = list(list(vertex = 2, weight = -2), list(vertex = 5, weight = 1)),
109+
"4" = list(list(vertex = 3, weight = -2), list(vertex = 6, weight = -1)),
110+
"5" = list(list(vertex = 6, weight = 3)),
111+
"6" = list()
112+
)
113+
114+
cat("Graph (adjacency list):\n")
115+
for (v in names(bf_graph)) {
116+
edges <- bf_graph[[v]]
117+
if (length(edges) > 0) {
118+
edge_strs <- sapply(edges, function(e) paste0(e$vertex, "(", e$weight, ")"))
119+
cat("Vertex", v, "-> [", paste(edge_strs, collapse = ", "), "]\n")
120+
} else {
121+
cat("Vertex", v, "-> []\n")
122+
}
123+
}
124+
125+
cat("\nRunning Bellman-Ford from vertex 1:\n")
126+
bf_result <- bellman_ford_shortest_path(bf_graph, 1)
127+
cat("Negative cycle detected:", bf_result$negative_cycle, "\n")
128+
129+
cat("Distances from vertex 1:\n")
130+
for (i in 1:length(bf_result$distances)) {
131+
d <- bf_result$distances[i]
132+
if (is.infinite(d)) {
133+
cat("To vertex", i, ": unreachable\n")
134+
} else {
135+
cat("To vertex", i, ": distance =", d, "\n")
136+
}
137+
}
138+
139+
cat("\nShortest path from 1 to 6:\n")
140+
path_info <- get_bellman_ford_path(bf_result, 1, 6)
141+
if (!is.null(path_info$path)) {
142+
cat("Path:", paste(path_info$path, collapse = " -> "), "\n")
143+
cat("Distance:", path_info$distance, "\n")
144+
} else {
145+
cat(path_info$message, "\n")
146+
}

0 commit comments

Comments
 (0)