Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions DIRECTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
* [Lasso](https://github.com/TheAlgorithms/R/blob/HEAD/data_preprocessing/lasso.r)

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

Expand Down
258 changes: 258 additions & 0 deletions graph_algorithms/dijkstra_shortest_path.r
Original file line number Diff line number Diff line change
@@ -0,0 +1,258 @@
# Dijkstra's Shortest Path Algorithm
#
# Dijkstra's algorithm finds the shortest path between a source vertex and all other vertices
# in a weighted graph with non-negative edge weights. It uses a greedy approach with a priority queue.
#
# Time Complexity: O((V + E) log V) with binary heap, O(V^2) with simple array
# Space Complexity: O(V) for distance and visited arrays
#
# Input: A weighted graph represented as adjacency list with weights, and a source vertex
# Output: Shortest distances from source to all vertices and the paths

# Priority queue implementation using simple vector (for educational purposes)
# In production, use more efficient data structures
create_priority_queue <- function() {
list(
elements = data.frame(vertex = integer(0), distance = numeric(0)),
size = 0
)
}

# Insert element into priority queue
pq_insert <- function(pq, vertex, distance) {
pq$elements <- rbind(pq$elements, data.frame(vertex = vertex, distance = distance))
pq$size <- pq$size + 1
return(pq)
}

# Extract minimum element from priority queue
pq_extract_min <- function(pq) {
if (pq$size == 0) {
return(list(pq = pq, min_element = NULL))
}

min_idx <- which.min(pq$elements$distance)
min_element <- pq$elements[min_idx, ]
pq$elements <- pq$elements[-min_idx, ]
pq$size <- pq$size - 1

return(list(pq = pq, min_element = min_element))
}

# Check if priority queue is empty
pq_is_empty <- function(pq) {
return(pq$size == 0)
}

# Main Dijkstra's algorithm implementation
dijkstra_shortest_path <- function(graph, source) {
# Get all vertices in the graph
all_vertices <- unique(c(names(graph), unlist(lapply(graph, function(x) x$vertex))))
num_vertices <- max(all_vertices)

# Initialize distances and previous vertices
distances <- rep(Inf, num_vertices)
previous <- rep(-1, num_vertices)
visited <- rep(FALSE, num_vertices)

# Set source distance to 0
distances[source] <- 0

# Create priority queue and add source
pq <- create_priority_queue()
pq <- pq_insert(pq, source, 0)

while (!pq_is_empty(pq)) {
# Extract vertex with minimum distance
result <- pq_extract_min(pq)
pq <- result$pq
current <- result$min_element

if (is.null(current)) break

u <- current$vertex

# Skip if already visited
if (visited[u]) next

# Mark as visited
visited[u] <- TRUE

# Update distances to neighbors
if (as.character(u) %in% names(graph)) {
for (edge in graph[[as.character(u)]]) {
v <- edge$vertex
weight <- edge$weight

# Relaxation step
if (!visited[v] && distances[u] + weight < distances[v]) {
distances[v] <- distances[u] + weight
previous[v] <- u
pq <- pq_insert(pq, v, distances[v])
}
}
}
}

return(list(
distances = distances,
previous = previous
))
}

# Reconstruct shortest path from source to target
get_shortest_path <- function(dijkstra_result, source, target) {
previous <- dijkstra_result$previous
distances <- dijkstra_result$distances

# Check if target is reachable
if (distances[target] == Inf) {
return(list(
path = NULL,
distance = Inf
))
}

# Reconstruct path by backtracking
path <- c()
current <- target

while (current != -1) {
path <- c(current, path)
current <- previous[current]
}

return(list(
path = path,
distance = distances[target]
))
}

# Find shortest paths to all vertices
get_all_shortest_paths <- function(dijkstra_result, source) {
distances <- dijkstra_result$distances
previous <- dijkstra_result$previous
paths <- list()

for (target in 1:length(distances)) {
if (distances[target] != Inf) {
path_result <- get_shortest_path(dijkstra_result, source, target)
paths[[as.character(target)]] <- path_result
}
}

return(paths)
}

# Example usage and testing
cat("=== Dijkstra's Shortest Path Algorithm ===\n")

# Create a weighted graph as adjacency list
# Graph structure with weights:
# 1
# / \
# 4/ \2
# / \
# 2 3
# |3 /1
# | /
# 4-----5
# 2
weighted_graph <- list(
"1" = list(
list(vertex = 2, weight = 4),
list(vertex = 3, weight = 2)
),
"2" = list(
list(vertex = 4, weight = 3)
),
"3" = list(
list(vertex = 5, weight = 1)
),
"4" = list(
list(vertex = 5, weight = 2)
),
"5" = list()
)

cat("Weighted graph structure:\n")
for (vertex in names(weighted_graph)) {
edges <- weighted_graph[[vertex]]
if (length(edges) > 0) {
edge_strs <- sapply(edges, function(e) paste0(e$vertex, "(", e$weight, ")"))
cat("Vertex", vertex, "-> [", paste(edge_strs, collapse = ", "), "]\n")
} else {
cat("Vertex", vertex, "-> []\n")
}
}

# Run Dijkstra's algorithm from vertex 1
cat("\nRunning Dijkstra's algorithm from vertex 1:\n")
result <- dijkstra_shortest_path(weighted_graph, 1)

# Display shortest distances
cat("Shortest distances from vertex 1:\n")
for (i in 1:length(result$distances)) {
if (result$distances[i] != Inf) {
cat("To vertex", i, ": distance =", result$distances[i], "\n")
}
}

# Get shortest path to specific vertex
cat("\nShortest path from 1 to 5:\n")
path_to_5 <- get_shortest_path(result, 1, 5)
if (!is.null(path_to_5$path)) {
cat("Path:", paste(path_to_5$path, collapse = " -> "), "\n")
cat("Distance:", path_to_5$distance, "\n")
}

# Get all shortest paths
cat("\nAll shortest paths from vertex 1:\n")
all_paths <- get_all_shortest_paths(result, 1)
for (target in names(all_paths)) {
path_info <- all_paths[[target]]
cat("To vertex", target, ": ", paste(path_info$path, collapse = " -> "),
" (distance:", path_info$distance, ")\n")
}

# Example with a more complex graph
cat("\n=== More Complex Weighted Graph Example ===\n")
complex_weighted_graph <- list(
"1" = list(
list(vertex = 2, weight = 7),
list(vertex = 3, weight = 9),
list(vertex = 6, weight = 14)
),
"2" = list(
list(vertex = 3, weight = 10),
list(vertex = 4, weight = 15)
),
"3" = list(
list(vertex = 4, weight = 11),
list(vertex = 6, weight = 2)
),
"4" = list(
list(vertex = 5, weight = 6)
),
"5" = list(),
"6" = list(
list(vertex = 5, weight = 9)
)
)

cat("Complex weighted graph from vertex 1:\n")
complex_result <- dijkstra_shortest_path(complex_weighted_graph, 1)

cat("Shortest distances:\n")
for (i in 1:length(complex_result$distances)) {
if (complex_result$distances[i] != Inf) {
cat("To vertex", i, ": distance =", complex_result$distances[i], "\n")
}
}

# Shortest path to vertex 5
path_to_5_complex <- get_shortest_path(complex_result, 1, 5)
if (!is.null(path_to_5_complex$path)) {
cat("Shortest path from 1 to 5:", paste(path_to_5_complex$path, collapse = " -> "), "\n")
cat("Distance:", path_to_5_complex$distance, "\n")
}