Skip to content

Commit cceeac7

Browse files
authored
Add rotting oranges (#7542)
* Add Rotting Oranges BFS solution * Add reference URL to RottingOranges documentation * style: apply clang-format to RottingOranges.java * style: format 2D array initializers in RottingOrangesTest.java
1 parent ad1de6b commit cceeac7

2 files changed

Lines changed: 248 additions & 0 deletions

File tree

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
package com.thealgorithms.datastructures.graphs;
2+
3+
import java.util.LinkedList;
4+
import java.util.Queue;
5+
6+
/**
7+
* Multi-source Breadth-First Search (BFS) implementation for the Rotting Oranges problem.
8+
*
9+
* <p>Algorithm explanation:
10+
* https://en.wikipedia.org/wiki/Breadth-first_search
11+
*
12+
* <p>Problem reference:
13+
* https://leetcode.com/problems/rotting-oranges/
14+
*
15+
* <p>Given a grid where:
16+
* <ul>
17+
* <li>0 represents an empty cell</li>
18+
* <li>1 represents a fresh orange</li>
19+
* <li>2 represents a rotten orange</li>
20+
* </ul>
21+
*
22+
* <p>Returns the minimum number of minutes required for all fresh oranges
23+
* to become rotten. Returns {@code -1} if it is impossible.
24+
*
25+
* <p>Time Complexity: O(m × n)
26+
* <br>Space Complexity: O(m × n)
27+
*/
28+
public class RottingOranges {
29+
30+
private static final int[] DEL_ROW = {-1, 0, 1, 0};
31+
private static final int[] DEL_COL = {0, 1, 0, -1};
32+
33+
private static final class Cell {
34+
private final int row;
35+
private final int col;
36+
private final int minute;
37+
38+
Cell(int row, int col, int minute) {
39+
this.row = row;
40+
this.col = col;
41+
this.minute = minute;
42+
}
43+
}
44+
45+
/**
46+
* Executes the Rotting Oranges algorithm.
47+
*
48+
* @param grid the input grid
49+
* @return minimum minutes required to rot all fresh oranges,
50+
* or -1 if impossible
51+
*/
52+
public int run(int[][] grid) {
53+
54+
if (grid == null || grid.length == 0 || grid[0].length == 0) {
55+
return 0;
56+
}
57+
58+
int rows = grid.length;
59+
int cols = grid[0].length;
60+
61+
// Create a copy so original input is not modified
62+
int[][] copy = new int[rows][cols];
63+
64+
for (int i = 0; i < rows; i++) {
65+
copy[i] = grid[i].clone();
66+
}
67+
68+
Queue<Cell> queue = new LinkedList<>();
69+
int freshOranges = 0;
70+
71+
// Find all rotten oranges and count fresh oranges
72+
for (int row = 0; row < rows; row++) {
73+
for (int col = 0; col < cols; col++) {
74+
75+
if (copy[row][col] == 2) {
76+
queue.offer(new Cell(row, col, 0));
77+
} else if (copy[row][col] == 1) {
78+
freshOranges++;
79+
}
80+
}
81+
}
82+
83+
if (freshOranges == 0) {
84+
return 0;
85+
}
86+
87+
int rottedFresh = 0;
88+
int minutes = 0;
89+
90+
// Multi-source BFS
91+
while (!queue.isEmpty()) {
92+
93+
Cell current = queue.poll();
94+
95+
minutes = Math.max(minutes, current.minute);
96+
97+
for (int i = 0; i < 4; i++) {
98+
99+
int newRow = current.row + DEL_ROW[i];
100+
int newCol = current.col + DEL_COL[i];
101+
102+
if (newRow >= 0 && newRow < rows && newCol >= 0 && newCol < cols && copy[newRow][newCol] == 1) {
103+
104+
copy[newRow][newCol] = 2;
105+
rottedFresh++;
106+
107+
queue.offer(new Cell(newRow, newCol, current.minute + 1));
108+
}
109+
}
110+
}
111+
112+
return rottedFresh == freshOranges ? minutes : -1;
113+
}
114+
}
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
package com.thealgorithms.datastructures.graphs;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
5+
import org.junit.jupiter.api.Test;
6+
7+
public class RottingOrangesTest {
8+
9+
@Test
10+
void testAllOrangesRotInSingleMinute() {
11+
RottingOranges rottingOranges = new RottingOranges();
12+
13+
int[][] grid = {{2, 1, 1}, {1, 1, 0}, {0, 1, 1}};
14+
15+
assertEquals(4, rottingOranges.run(grid));
16+
}
17+
18+
@Test
19+
void testImpossibleToRotAllOranges() {
20+
RottingOranges rottingOranges = new RottingOranges();
21+
22+
int[][] grid = {{2, 1, 1}, {0, 1, 1}, {1, 0, 1}};
23+
24+
assertEquals(-1, rottingOranges.run(grid));
25+
}
26+
27+
@Test
28+
void testNoFreshOranges() {
29+
RottingOranges rottingOranges = new RottingOranges();
30+
31+
int[][] grid = {{2, 2}, {2, 2}};
32+
33+
assertEquals(0, rottingOranges.run(grid));
34+
}
35+
36+
@Test
37+
void testNoRottenOranges() {
38+
RottingOranges rottingOranges = new RottingOranges();
39+
40+
int[][] grid = {{1, 1}, {1, 1}};
41+
42+
assertEquals(-1, rottingOranges.run(grid));
43+
}
44+
45+
@Test
46+
void testEmptyGrid() {
47+
RottingOranges rottingOranges = new RottingOranges();
48+
49+
int[][] grid = {};
50+
51+
assertEquals(0, rottingOranges.run(grid));
52+
}
53+
54+
@Test
55+
void testSingleRottenOrange() {
56+
RottingOranges rottingOranges = new RottingOranges();
57+
58+
int[][] grid = {{2}};
59+
60+
assertEquals(0, rottingOranges.run(grid));
61+
}
62+
63+
@Test
64+
void testSingleFreshOrange() {
65+
RottingOranges rottingOranges = new RottingOranges();
66+
67+
int[][] grid = {{1}};
68+
69+
assertEquals(-1, rottingOranges.run(grid));
70+
}
71+
72+
@Test
73+
void testSingleFreshOrangeNextToRottenOrange() {
74+
RottingOranges rottingOranges = new RottingOranges();
75+
76+
int[][] grid = {{2, 1}};
77+
78+
assertEquals(1, rottingOranges.run(grid));
79+
}
80+
81+
@Test
82+
void testMultipleRottenSources() {
83+
RottingOranges rottingOranges = new RottingOranges();
84+
85+
int[][] grid = {{2, 1, 0, 2}, {1, 1, 1, 1}, {0, 1, 1, 1}};
86+
87+
assertEquals(3, rottingOranges.run(grid));
88+
}
89+
90+
@Test
91+
void testFreshOrangeBlockedByEmptyCells() {
92+
RottingOranges rottingOranges = new RottingOranges();
93+
94+
int[][] grid = {{2, 0, 1}, {0, 0, 0}, {1, 0, 1}};
95+
96+
assertEquals(-1, rottingOranges.run(grid));
97+
}
98+
99+
@Test
100+
void testLinearSpread() {
101+
RottingOranges rottingOranges = new RottingOranges();
102+
103+
int[][] grid = {{2, 1, 1, 1, 1}};
104+
105+
assertEquals(4, rottingOranges.run(grid));
106+
}
107+
108+
@Test
109+
void testVerticalSpread() {
110+
RottingOranges rottingOranges = new RottingOranges();
111+
112+
int[][] grid = {{2}, {1}, {1}, {1}};
113+
114+
assertEquals(3, rottingOranges.run(grid));
115+
}
116+
117+
@Test
118+
void testGridWithOnlyEmptyCells() {
119+
RottingOranges rottingOranges = new RottingOranges();
120+
121+
int[][] grid = {{0, 0}, {0, 0}};
122+
123+
assertEquals(0, rottingOranges.run(grid));
124+
}
125+
126+
@Test
127+
void testComplexGrid() {
128+
RottingOranges rottingOranges = new RottingOranges();
129+
130+
int[][] grid = {{2, 1, 1}, {1, 1, 1}, {1, 1, 1}};
131+
132+
assertEquals(4, rottingOranges.run(grid));
133+
}
134+
}

0 commit comments

Comments
 (0)