Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
3 changes: 3 additions & 0 deletions DIRECTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@
* [K Folds](https://github.com/TheAlgorithms/R/blob/HEAD/data_preprocessing/k_folds.r)
* [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)

## Mathematics
* [Amicable Numbers](https://github.com/TheAlgorithms/R/blob/HEAD/mathematics/amicable_numbers.r)
* [Armstrong Number](https://github.com/TheAlgorithms/R/blob/HEAD/mathematics/armstrong_number.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")
}