Skip to content
Open
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package com.thealgorithms.datastructures.graphs;

import java.util.LinkedList;
import java.util.Queue;

/**
* Multi-source Breadth-First Search (BFS) implementation for the Rotting Oranges problem.
*
* <p>Algorithm explanation:
* https://en.wikipedia.org/wiki/Breadth-first_search
*
* <p>Problem reference:
* https://leetcode.com/problems/rotting-oranges/
*
* <p>Given a grid where:
* <ul>
* <li>0 represents an empty cell</li>
* <li>1 represents a fresh orange</li>
* <li>2 represents a rotten orange</li>
* </ul>
*
* <p>Returns the minimum number of minutes required for all fresh oranges
* to become rotten. Returns {@code -1} if it is impossible.
*
* <p>Time Complexity: O(m × n)
* <br>Space Complexity: O(m × n)
*/
public class RottingOranges {

private static final int[] DEL_ROW = {-1, 0, 1, 0};
private static final int[] DEL_COL = {0, 1, 0, -1};

private static final class Cell {
private final int row;
private final int col;
private final int minute;

Cell(int row, int col, int minute) {
this.row = row;
this.col = col;
this.minute = minute;
}
}

/**
* Executes the Rotting Oranges algorithm.
*
* @param grid the input grid
* @return minimum minutes required to rot all fresh oranges,
* or -1 if impossible
*/
public int run(int[][] grid) {

if (grid == null || grid.length == 0 || grid[0].length == 0) {
return 0;
}

int rows = grid.length;
int cols = grid[0].length;

// Create a copy so original input is not modified
int[][] copy = new int[rows][cols];

for (int i = 0; i < rows; i++) {
copy[i] = grid[i].clone();
}

Queue<Cell> queue = new LinkedList<>();
int freshOranges = 0;

// Find all rotten oranges and count fresh oranges
for (int row = 0; row < rows; row++) {
for (int col = 0; col < cols; col++) {

if (copy[row][col] == 2) {
queue.offer(new Cell(row, col, 0));
} else if (copy[row][col] == 1) {
freshOranges++;
}
}
}

if (freshOranges == 0) {
return 0;
}

int rottedFresh = 0;
int minutes = 0;

// Multi-source BFS
while (!queue.isEmpty()) {

Cell current = queue.poll();

minutes = Math.max(minutes, current.minute);

for (int i = 0; i < 4; i++) {

int newRow = current.row + DEL_ROW[i];
int newCol = current.col + DEL_COL[i];

if (newRow >= 0 && newRow < rows && newCol >= 0 && newCol < cols && copy[newRow][newCol] == 1) {

copy[newRow][newCol] = 2;
rottedFresh++;

queue.offer(new Cell(newRow, newCol, current.minute + 1));
}
}
}

return rottedFresh == freshOranges ? minutes : -1;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
package com.thealgorithms.datastructures.graphs;

import static org.junit.jupiter.api.Assertions.assertEquals;

import org.junit.jupiter.api.Test;

public class RottingOrangesTest {

@Test
void testAllOrangesRotInSingleMinute() {
RottingOranges rottingOranges = new RottingOranges();

int[][] grid = {{2, 1, 1}, {1, 1, 0}, {0, 1, 1}};

assertEquals(4, rottingOranges.run(grid));
}

@Test
void testImpossibleToRotAllOranges() {
RottingOranges rottingOranges = new RottingOranges();

int[][] grid = {{2, 1, 1}, {0, 1, 1}, {1, 0, 1}};

assertEquals(-1, rottingOranges.run(grid));
}

@Test
void testNoFreshOranges() {
RottingOranges rottingOranges = new RottingOranges();

int[][] grid = {{2, 2}, {2, 2}};

assertEquals(0, rottingOranges.run(grid));
}

@Test
void testNoRottenOranges() {
RottingOranges rottingOranges = new RottingOranges();

int[][] grid = {{1, 1}, {1, 1}};

assertEquals(-1, rottingOranges.run(grid));
}

@Test
void testEmptyGrid() {
RottingOranges rottingOranges = new RottingOranges();

int[][] grid = {};

assertEquals(0, rottingOranges.run(grid));
}

@Test
void testSingleRottenOrange() {
RottingOranges rottingOranges = new RottingOranges();

int[][] grid = {{2}};

assertEquals(0, rottingOranges.run(grid));
}

@Test
void testSingleFreshOrange() {
RottingOranges rottingOranges = new RottingOranges();

int[][] grid = {{1}};

assertEquals(-1, rottingOranges.run(grid));
}

@Test
void testSingleFreshOrangeNextToRottenOrange() {
RottingOranges rottingOranges = new RottingOranges();

int[][] grid = {{2, 1}};

assertEquals(1, rottingOranges.run(grid));
}

@Test
void testMultipleRottenSources() {
RottingOranges rottingOranges = new RottingOranges();

int[][] grid = {{2, 1, 0, 2}, {1, 1, 1, 1}, {0, 1, 1, 1}};

assertEquals(3, rottingOranges.run(grid));
}

@Test
void testFreshOrangeBlockedByEmptyCells() {
RottingOranges rottingOranges = new RottingOranges();

int[][] grid = {{2, 0, 1}, {0, 0, 0}, {1, 0, 1}};

assertEquals(-1, rottingOranges.run(grid));
}

@Test
void testLinearSpread() {
RottingOranges rottingOranges = new RottingOranges();

int[][] grid = {{2, 1, 1, 1, 1}};

assertEquals(4, rottingOranges.run(grid));
}

@Test
void testVerticalSpread() {
RottingOranges rottingOranges = new RottingOranges();

int[][] grid = {{2}, {1}, {1}, {1}};

assertEquals(3, rottingOranges.run(grid));
}

@Test
void testGridWithOnlyEmptyCells() {
RottingOranges rottingOranges = new RottingOranges();

int[][] grid = {{0, 0}, {0, 0}};

assertEquals(0, rottingOranges.run(grid));
}

@Test
void testComplexGrid() {
RottingOranges rottingOranges = new RottingOranges();

int[][] grid = {{2, 1, 1}, {1, 1, 1}, {1, 1, 1}};

assertEquals(4, rottingOranges.run(grid));
}
}
Loading