Skip to content

Commit f3a5e03

Browse files
committed
feat: Add Kadane's 2D algorithm for maximum sum rectangle
1 parent b312567 commit f3a5e03

File tree

2 files changed

+275
-0
lines changed

2 files changed

+275
-0
lines changed
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
package com.thealgorithms.matrix;
2+
3+
4+
/**
5+
* Kadane's Algorithm for 2D arrays (Maximum Sum Rectangle in a 2D Matrix)
6+
*
7+
* This algorithm finds the maximum sum of elements in a rectangular submatrix
8+
* of a given 2D matrix. It uses Kadane's algorithm for 1D arrays extended to 2D.
9+
*
10+
* Time Complexity: O(n^2 * m) where n is the number of columns and m is the number of rows
11+
* Space Complexity: O(m) for the temporary array
12+
*
13+
* @author Adarsh Singh
14+
*/
15+
public final class Kadane2D {
16+
17+
18+
19+
/**
20+
* Finds the maximum sum rectangle in a 2D matrix
21+
*
22+
* @param matrix The input 2D matrix
23+
* @return The maximum sum found in any rectangular submatrix
24+
* @throws IllegalArgumentException if matrix is null or empty
25+
*/
26+
public static int maxSumRectangle(int[][] matrix) {
27+
if (matrix == null || matrix.length == 0 || matrix[0].length == 0) {
28+
throw new IllegalArgumentException("Matrix cannot be null or empty");
29+
}
30+
31+
int rows = matrix.length;
32+
int cols = matrix[0].length;
33+
int maxSum = Integer.MIN_VALUE;
34+
35+
// Fix the left column
36+
for (int left = 0; left < cols; left++) {
37+
int[] temp = new int[rows];
38+
39+
// Calculate sum between left and right columns
40+
for (int right = left; right < cols; right++) {
41+
// Add elements of current column to temp array
42+
for (int i = 0; i < rows; i++) {
43+
temp[i] += matrix[i][right];
44+
}
45+
46+
// Apply Kadane's 1D algorithm on temp array
47+
int currentMax = kadane1D(temp);
48+
maxSum = Math.max(maxSum, currentMax);
49+
}
50+
}
51+
52+
return maxSum;
53+
}
54+
55+
/**
56+
* Kadane's algorithm for 1D array
57+
* Finds maximum sum of contiguous subarray
58+
*
59+
* @param arr The input array
60+
* @return Maximum sum of contiguous subarray
61+
*/
62+
private static int kadane1D(int[] arr) {
63+
int maxSoFar = arr[0];
64+
int maxEndingHere = arr[0];
65+
66+
for (int i = 1; i < arr.length; i++) {
67+
maxEndingHere = Math.max(arr[i], maxEndingHere + arr[i]);
68+
maxSoFar = Math.max(maxSoFar, maxEndingHere);
69+
}
70+
71+
return maxSoFar;
72+
}
73+
74+
/**
75+
* Result class to store coordinates along with maximum sum
76+
*/
77+
public static class Result {
78+
public final int maxSum;
79+
public final int topRow;
80+
public final int leftCol;
81+
public final int bottomRow;
82+
public final int rightCol;
83+
84+
public Result(int maxSum, int topRow, int leftCol, int bottomRow, int rightCol) {
85+
this.maxSum = maxSum;
86+
this.topRow = topRow;
87+
this.leftCol = leftCol;
88+
this.bottomRow = bottomRow;
89+
this.rightCol = rightCol;
90+
}
91+
92+
@Override
93+
public String toString() {
94+
return String.format("Max Sum: %d, Top-Left: (%d, %d), Bottom-Right: (%d, %d)",
95+
maxSum, topRow, leftCol, bottomRow, rightCol);
96+
}
97+
}
98+
99+
/**
100+
* Finds the maximum sum rectangle along with its coordinates
101+
*
102+
* @param matrix The input 2D matrix
103+
* @return Result object containing maximum sum and rectangle coordinates
104+
* @throws IllegalArgumentException if matrix is null or empty
105+
*/
106+
public static Result maxSumRectangleWithCoordinates(int[][] matrix) {
107+
if (matrix == null || matrix.length == 0 || matrix[0].length == 0) {
108+
throw new IllegalArgumentException("Matrix cannot be null or empty");
109+
}
110+
111+
int rows = matrix.length;
112+
int cols = matrix[0].length;
113+
int maxSum = Integer.MIN_VALUE;
114+
int finalLeft = 0, finalRight = 0, finalTop = 0, finalBottom = 0;
115+
116+
for (int left = 0; left < cols; left++) {
117+
int[] temp = new int[rows];
118+
119+
for (int right = left; right < cols; right++) {
120+
for (int i = 0; i < rows; i++) {
121+
temp[i] += matrix[i][right];
122+
}
123+
124+
// Modified Kadane's to track row positions
125+
int currentSum = temp[0];
126+
int maxTemp = temp[0];
127+
int start = 0, end = 0, s = 0;
128+
129+
for (int i = 1; i < rows; i++) {
130+
if (currentSum < 0) {
131+
currentSum = temp[i];
132+
s = i;
133+
} else {
134+
currentSum += temp[i];
135+
}
136+
137+
if (currentSum > maxTemp) {
138+
maxTemp = currentSum;
139+
start = s;
140+
end = i;
141+
}
142+
}
143+
144+
if (maxTemp > maxSum) {
145+
maxSum = maxTemp;
146+
finalLeft = left;
147+
finalRight = right;
148+
finalTop = start;
149+
finalBottom = end;
150+
}
151+
}
152+
}
153+
154+
return new Result(maxSum, finalTop, finalLeft, finalBottom, finalRight);
155+
}
156+
}
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
package com.thealgorithms.matrix;
2+
3+
4+
5+
import org.junit.jupiter.api.Test;
6+
import static org.junit.jupiter.api.Assertions.*;
7+
8+
/**
9+
* Test cases for Kadane2D algorithm
10+
*/
11+
class Kadane2DTest {
12+
13+
@Test
14+
void testBasicMatrix() {
15+
int[][] matrix = {
16+
{1, 2, -1, -4, -20},
17+
{-8, -3, 4, 2, 1},
18+
{3, 8, 10, 1, 3},
19+
{-4, -1, 1, 7, -6}
20+
};
21+
22+
assertEquals(29, Kadane2D.maxSumRectangle(matrix));
23+
}
24+
25+
@Test
26+
void testAllPositive() {
27+
int[][] matrix = {
28+
{1, 2, 3},
29+
{4, 5, 6},
30+
{7, 8, 9}
31+
};
32+
33+
// Sum of all elements
34+
assertEquals(45, Kadane2D.maxSumRectangle(matrix));
35+
}
36+
37+
@Test
38+
void testAllNegative() {
39+
int[][] matrix = {
40+
{-1, -2, -3},
41+
{-4, -5, -6},
42+
{-7, -8, -9}
43+
};
44+
45+
// Maximum single element (least negative)
46+
assertEquals(-1, Kadane2D.maxSumRectangle(matrix));
47+
}
48+
49+
@Test
50+
void testSingleElement() {
51+
int[][] matrix = {{5}};
52+
assertEquals(5, Kadane2D.maxSumRectangle(matrix));
53+
}
54+
55+
@Test
56+
void testSingleRow() {
57+
int[][] matrix = {{-2, 1, -3, 4, -1, 2, 1, -5, 4}};
58+
assertEquals(6, Kadane2D.maxSumRectangle(matrix));
59+
}
60+
61+
@Test
62+
void testSingleColumn() {
63+
int[][] matrix = {{-2}, {1}, {-3}, {4}, {-1}, {2}, {1}, {-5}, {4}};
64+
assertEquals(6, Kadane2D.maxSumRectangle(matrix));
65+
}
66+
67+
@Test
68+
void testMixedValues() {
69+
int[][] matrix = {
70+
{2, 1, -3, -4},
71+
{-5, -1, 4, 0},
72+
{1, 2, 3, -2}
73+
};
74+
75+
assertEquals(9, Kadane2D.maxSumRectangle(matrix));
76+
}
77+
78+
@Test
79+
void testWithCoordinates() {
80+
int[][] matrix = {
81+
{1, 2, -1, -4, -20},
82+
{-8, -3, 4, 2, 1},
83+
{3, 8, 10, 1, 3},
84+
{-4, -1, 1, 7, -6}
85+
};
86+
87+
Kadane2D.Result result = Kadane2D.maxSumRectangleWithCoordinates(matrix);
88+
assertEquals(29, result.maxSum);
89+
assertEquals(1, result.topRow);
90+
assertEquals(1, result.leftCol);
91+
assertEquals(2, result.bottomRow);
92+
assertEquals(3, result.rightCol);
93+
}
94+
95+
@Test
96+
void testNullMatrix() {
97+
assertThrows(IllegalArgumentException.class, () -> {
98+
Kadane2D.maxSumRectangle(null);
99+
});
100+
}
101+
102+
@Test
103+
void testEmptyMatrix() {
104+
assertThrows(IllegalArgumentException.class, () -> {
105+
Kadane2D.maxSumRectangle(new int[0][0]);
106+
});
107+
}
108+
109+
@Test
110+
void testZeroMatrix() {
111+
int[][] matrix = {
112+
{0, 0, 0},
113+
{0, 0, 0},
114+
{0, 0, 0}
115+
};
116+
117+
assertEquals(0, Kadane2D.maxSumRectangle(matrix));
118+
}
119+
}

0 commit comments

Comments
 (0)