Skip to content

Commit df205e7

Browse files
Add Dijkstra's shortest path algorithm (#153)
1 parent b0f8ccf commit df205e7

File tree

2 files changed

+259
-0
lines changed

2 files changed

+259
-0
lines changed

DIRECTORY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
* [Lasso](https://github.com/TheAlgorithms/R/blob/HEAD/data_preprocessing/lasso.r)
3636

3737
## Graph Algorithms
38+
* [Dijkstra Shortest Path](https://github.com/TheAlgorithms/R/blob/HEAD/graph_algorithms/dijkstra_shortest_path.r)
3839
* [Breadth First Search](https://github.com/TheAlgorithms/R/blob/HEAD/graph_algorithms/breadth_first_search.r)
3940
* [Depth First Search](https://github.com/TheAlgorithms/R/blob/HEAD/graph_algorithms/depth_first_search.r)
4041

Lines changed: 258 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,258 @@
1+
# Dijkstra's Shortest Path Algorithm
2+
#
3+
# Dijkstra's algorithm finds the shortest path between a source vertex and all other vertices
4+
# in a weighted graph with non-negative edge weights. It uses a greedy approach with a priority queue.
5+
#
6+
# Time Complexity: O((V + E) log V) with binary heap, O(V^2) with simple array
7+
# Space Complexity: O(V) for distance and visited arrays
8+
#
9+
# Input: A weighted graph represented as adjacency list with weights, and a source vertex
10+
# Output: Shortest distances from source to all vertices and the paths
11+
12+
# Priority queue implementation using simple vector (for educational purposes)
13+
# In production, use more efficient data structures
14+
create_priority_queue <- function() {
15+
list(
16+
elements = data.frame(vertex = integer(0), distance = numeric(0)),
17+
size = 0
18+
)
19+
}
20+
21+
# Insert element into priority queue
22+
pq_insert <- function(pq, vertex, distance) {
23+
pq$elements <- rbind(pq$elements, data.frame(vertex = vertex, distance = distance))
24+
pq$size <- pq$size + 1
25+
return(pq)
26+
}
27+
28+
# Extract minimum element from priority queue
29+
pq_extract_min <- function(pq) {
30+
if (pq$size == 0) {
31+
return(list(pq = pq, min_element = NULL))
32+
}
33+
34+
min_idx <- which.min(pq$elements$distance)
35+
min_element <- pq$elements[min_idx, ]
36+
pq$elements <- pq$elements[-min_idx, ]
37+
pq$size <- pq$size - 1
38+
39+
return(list(pq = pq, min_element = min_element))
40+
}
41+
42+
# Check if priority queue is empty
43+
pq_is_empty <- function(pq) {
44+
return(pq$size == 0)
45+
}
46+
47+
# Main Dijkstra's algorithm implementation
48+
dijkstra_shortest_path <- function(graph, source) {
49+
# Get all vertices in the graph
50+
all_vertices <- unique(c(names(graph), unlist(lapply(graph, function(x) x$vertex))))
51+
num_vertices <- max(all_vertices)
52+
53+
# Initialize distances and previous vertices
54+
distances <- rep(Inf, num_vertices)
55+
previous <- rep(-1, num_vertices)
56+
visited <- rep(FALSE, num_vertices)
57+
58+
# Set source distance to 0
59+
distances[source] <- 0
60+
61+
# Create priority queue and add source
62+
pq <- create_priority_queue()
63+
pq <- pq_insert(pq, source, 0)
64+
65+
while (!pq_is_empty(pq)) {
66+
# Extract vertex with minimum distance
67+
result <- pq_extract_min(pq)
68+
pq <- result$pq
69+
current <- result$min_element
70+
71+
if (is.null(current)) break
72+
73+
u <- current$vertex
74+
75+
# Skip if already visited
76+
if (visited[u]) next
77+
78+
# Mark as visited
79+
visited[u] <- TRUE
80+
81+
# Update distances to neighbors
82+
if (as.character(u) %in% names(graph)) {
83+
for (edge in graph[[as.character(u)]]) {
84+
v <- edge$vertex
85+
weight <- edge$weight
86+
87+
# Relaxation step
88+
if (!visited[v] && distances[u] + weight < distances[v]) {
89+
distances[v] <- distances[u] + weight
90+
previous[v] <- u
91+
pq <- pq_insert(pq, v, distances[v])
92+
}
93+
}
94+
}
95+
}
96+
97+
return(list(
98+
distances = distances,
99+
previous = previous
100+
))
101+
}
102+
103+
# Reconstruct shortest path from source to target
104+
get_shortest_path <- function(dijkstra_result, source, target) {
105+
previous <- dijkstra_result$previous
106+
distances <- dijkstra_result$distances
107+
108+
# Check if target is reachable
109+
if (distances[target] == Inf) {
110+
return(list(
111+
path = NULL,
112+
distance = Inf
113+
))
114+
}
115+
116+
# Reconstruct path by backtracking
117+
path <- c()
118+
current <- target
119+
120+
while (current != -1) {
121+
path <- c(current, path)
122+
current <- previous[current]
123+
}
124+
125+
return(list(
126+
path = path,
127+
distance = distances[target]
128+
))
129+
}
130+
131+
# Find shortest paths to all vertices
132+
get_all_shortest_paths <- function(dijkstra_result, source) {
133+
distances <- dijkstra_result$distances
134+
previous <- dijkstra_result$previous
135+
paths <- list()
136+
137+
for (target in 1:length(distances)) {
138+
if (distances[target] != Inf) {
139+
path_result <- get_shortest_path(dijkstra_result, source, target)
140+
paths[[as.character(target)]] <- path_result
141+
}
142+
}
143+
144+
return(paths)
145+
}
146+
147+
# Example usage and testing
148+
cat("=== Dijkstra's Shortest Path Algorithm ===\n")
149+
150+
# Create a weighted graph as adjacency list
151+
# Graph structure with weights:
152+
# 1
153+
# / \
154+
# 4/ \2
155+
# / \
156+
# 2 3
157+
# |3 /1
158+
# | /
159+
# 4-----5
160+
# 2
161+
weighted_graph <- list(
162+
"1" = list(
163+
list(vertex = 2, weight = 4),
164+
list(vertex = 3, weight = 2)
165+
),
166+
"2" = list(
167+
list(vertex = 4, weight = 3)
168+
),
169+
"3" = list(
170+
list(vertex = 5, weight = 1)
171+
),
172+
"4" = list(
173+
list(vertex = 5, weight = 2)
174+
),
175+
"5" = list()
176+
)
177+
178+
cat("Weighted graph structure:\n")
179+
for (vertex in names(weighted_graph)) {
180+
edges <- weighted_graph[[vertex]]
181+
if (length(edges) > 0) {
182+
edge_strs <- sapply(edges, function(e) paste0(e$vertex, "(", e$weight, ")"))
183+
cat("Vertex", vertex, "-> [", paste(edge_strs, collapse = ", "), "]\n")
184+
} else {
185+
cat("Vertex", vertex, "-> []\n")
186+
}
187+
}
188+
189+
# Run Dijkstra's algorithm from vertex 1
190+
cat("\nRunning Dijkstra's algorithm from vertex 1:\n")
191+
result <- dijkstra_shortest_path(weighted_graph, 1)
192+
193+
# Display shortest distances
194+
cat("Shortest distances from vertex 1:\n")
195+
for (i in 1:length(result$distances)) {
196+
if (result$distances[i] != Inf) {
197+
cat("To vertex", i, ": distance =", result$distances[i], "\n")
198+
}
199+
}
200+
201+
# Get shortest path to specific vertex
202+
cat("\nShortest path from 1 to 5:\n")
203+
path_to_5 <- get_shortest_path(result, 1, 5)
204+
if (!is.null(path_to_5$path)) {
205+
cat("Path:", paste(path_to_5$path, collapse = " -> "), "\n")
206+
cat("Distance:", path_to_5$distance, "\n")
207+
}
208+
209+
# Get all shortest paths
210+
cat("\nAll shortest paths from vertex 1:\n")
211+
all_paths <- get_all_shortest_paths(result, 1)
212+
for (target in names(all_paths)) {
213+
path_info <- all_paths[[target]]
214+
cat("To vertex", target, ": ", paste(path_info$path, collapse = " -> "),
215+
" (distance:", path_info$distance, ")\n")
216+
}
217+
218+
# Example with a more complex graph
219+
cat("\n=== More Complex Weighted Graph Example ===\n")
220+
complex_weighted_graph <- list(
221+
"1" = list(
222+
list(vertex = 2, weight = 7),
223+
list(vertex = 3, weight = 9),
224+
list(vertex = 6, weight = 14)
225+
),
226+
"2" = list(
227+
list(vertex = 3, weight = 10),
228+
list(vertex = 4, weight = 15)
229+
),
230+
"3" = list(
231+
list(vertex = 4, weight = 11),
232+
list(vertex = 6, weight = 2)
233+
),
234+
"4" = list(
235+
list(vertex = 5, weight = 6)
236+
),
237+
"5" = list(),
238+
"6" = list(
239+
list(vertex = 5, weight = 9)
240+
)
241+
)
242+
243+
cat("Complex weighted graph from vertex 1:\n")
244+
complex_result <- dijkstra_shortest_path(complex_weighted_graph, 1)
245+
246+
cat("Shortest distances:\n")
247+
for (i in 1:length(complex_result$distances)) {
248+
if (complex_result$distances[i] != Inf) {
249+
cat("To vertex", i, ": distance =", complex_result$distances[i], "\n")
250+
}
251+
}
252+
253+
# Shortest path to vertex 5
254+
path_to_5_complex <- get_shortest_path(complex_result, 1, 5)
255+
if (!is.null(path_to_5_complex$path)) {
256+
cat("Shortest path from 1 to 5:", paste(path_to_5_complex$path, collapse = " -> "), "\n")
257+
cat("Distance:", path_to_5_complex$distance, "\n")
258+
}

0 commit comments

Comments
 (0)