Get Biggest Three Rhombus Sums in a Grid
MedPrompt
You are given an m x n integer matrix grid.
A rhombus sum is the sum of the elements that form the border of a regular rhombus shape in grid. The rhombus must have the shape of a square rotated 45 degrees with each of the corners centered in a grid cell. Below is an image of four valid rhombus shapes with the corresponding colored cells that should be included in each rhombus sum:
Note that the rhombus can have an area of 0, which is depicted by the purple rhombus in the bottom right corner.
Return the biggest three distinct rhombus sums in the grid in descending order. If there are less than three distinct values, return all of them.
Example 1:
Input: grid = [[3,4,5,1,3],[3,3,4,2,3],[20,30,200,40,10],[1,5,5,4,1],[4,3,2,2,5]]
Output: [228,216,211]
Explanation: The rhombus shapes for the three biggest distinct rhombus sums are depicted above.
- Blue: 20 + 3 + 200 + 5 = 228
- Red: 200 + 2 + 10 + 4 = 216
- Green: 5 + 200 + 4 + 2 = 211Example 2:
Input: grid = [[1,2,3],[4,5,6],[7,8,9]]
Output: [20,9,8]
Explanation: The rhombus shapes for the three biggest distinct rhombus sums are depicted above.
- Blue: 4 + 2 + 6 + 8 = 20
- Red: 9 (area 0 rhombus in the bottom right corner)
- Green: 8 (area 0 rhombus in the bottom middle)Example 3:
Input: grid = [[7,7,7]]
Output: [7]
Explanation: All three possible rhombus sums are the same, so return [7].
Constraints:
m == grid.lengthn == grid[i].length1 <= m, n <= 501 <= grid[i][j] <= 105
Approaches
2 approaches with complexity analysis and trade-offs.
This approach directly simulates the process of finding every possible rhombus in the grid. We iterate through each cell as a potential center of a rhombus. For each center, we expand outwards to form rhombuses of increasing sizes, as long as they stay within the grid boundaries. For each valid rhombus, we calculate its sum by traversing its border and adding up the cell values.
Algorithm
- Initialize a
TreeSetcalledsumsto store distinct rhombus sums in sorted order. - Iterate through each cell
(r, c)from(0, 0)to(m-1, n-1)to use as a rhombus center. - For each center
(r, c):- Add
grid[r][c]tosums(this is a rhombus of size 0). - Start a loop for rhombus size
kfrom 1. - Check if a rhombus of size
kcentered at(r, c)is fully contained within the grid. If not, break the loop fork. - If it is contained, calculate the sum of its border elements by iterating along its four sides.
- Add the calculated sum to
sums.
- Add
- After all iterations,
sumswill contain all distinct rhombus sums. - Create a result array of size
min(3, sums.size()). - Pop the largest elements from
sumsand fill the result array. - Return the result array.
Walkthrough
We use a TreeSet to store the distinct rhombus sums. A TreeSet is useful because it automatically keeps the sums sorted and handles duplicates.
We iterate through every cell (r, c) of the grid. This cell will serve as the center of a potential rhombus.
For each center (r, c), we first consider a rhombus of size 0. The sum is simply the value of the cell grid[r][c]. We add this to our set.
Then, we start a loop for the rhombus size k, starting from k=1. We expand the rhombus as long as its four corners (r-k, c), (r, c+k), (r+k, c), and (r, c-k) are all within the grid's boundaries.
For each valid size k, we calculate the sum of the elements on its border. This is done by simulating a walk along the four sides of the rhombus, taking care not to double-count the corner elements.
The calculated sum is added to the TreeSet.
After checking all possible centers and sizes, the TreeSet contains all distinct rhombus sums in ascending order. We extract the top three largest sums (or fewer if there aren't that many) and return them in descending order.
import java.util.*; class Solution { public int[] getBiggestThree(int[][] grid) { int m = grid.length; int n = grid[0].length; // Use a TreeSet to keep sums sorted and unique TreeSet<Integer> sums = new TreeSet<>(); for (int r = 0; r < m; r++) { for (int c = 0; c < n; c++) { // Size 0 rhombus sums.add(grid[r][c]); // Size k > 0 rhombuses for (int k = 1; ; k++) { // Check if the rhombus is within bounds if (r - k < 0 || r + k >= m || c - k < 0 || c + k >= n) { break; } // Calculate sum for rhombus of size k int currentSum = 0; // Top to right (excluding right corner) for (int i = 0; i < k; i++) { currentSum += grid[r - k + i][c + i]; } // Right to bottom (excluding bottom corner) for (int i = 0; i < k; i++) { currentSum += grid[r + i][c + k - i]; } // Bottom to left (excluding left corner) for (int i = 0; i < k; i++) { currentSum += grid[r + k - i][c - i]; } // Left to top (excluding top corner) for (int i = 0; i < k; i++) { currentSum += grid[r - i][c - k + i]; } sums.add(currentSum); } } } // Extract the top 3 sums int count = Math.min(3, sums.size()); int[] result = new int[count]; for (int i = 0; i < count; i++) { result[i] = sums.pollLast(); } return result; }}Complexity
Time
O(m * n * min(m, n)^2). We iterate through `m * n` possible centers. For each center, we iterate through possible rhombus sizes `k`, where the maximum `k` is `O(min(m, n))`. For each size `k`, calculating the sum takes `O(k)` time. For a square grid of size `N`, this is `O(N^4)`.
Space
O(m * n * min(m, n)). In the worst case, every rhombus could have a unique sum. The number of possible rhombuses is O(m * n * min(m, n)). The `TreeSet` would store all these sums.
Trade-offs
Pros
Conceptually simple and follows the problem definition directly.
Relatively easy to implement without complex data structures.
Cons
Highly inefficient due to redundant calculations. The sum for each rhombus is computed from scratch.
May be too slow for larger grid sizes, although it passes for the given constraints (
m, n <= 50).
This approach optimizes the sum calculation by pre-computing prefix sums along the two diagonal directions. A rhombus's border is composed of four diagonal segments. By using pre-computed sums, we can find the sum of any diagonal segment in O(1) time. This reduces the time to calculate each rhombus sum from O(k) to O(1), significantly improving the overall performance.
Algorithm
- Create two
m x nprefix sum arrays,diag1anddiag2. - Populate
diag1[i][j]with the sum of elements on the main diagonal (top-left to bottom-right) ending at(i, j). - Populate
diag2[i][j]with the sum of elements on the anti-diagonal (top-right to bottom-left) ending at(i, j). - Initialize a
TreeSetcalledsums. - Iterate through each cell
(r, c)as a potential rhombus center. - Add
grid[r][c]tosums(for size 0 rhombus). - Loop for size
kfrom 1, as long as the rhombus is in-bounds. - For each
k, identify the four corners:T(r-k, c),R(r, c+k),B(r+k, c),L(r, c-k). - Calculate the sum of the border using the pre-computed
diag1anddiag2arrays inO(1)time. - Add the sum to the
TreeSet. - After all iterations, extract the top 3 largest elements from the
TreeSetand return them in descending order.
Walkthrough
The key insight is that the four sides of a rhombus lie on two main diagonals and two anti-diagonals. We can pre-calculate prefix sums for all such diagonals in the grid.
We create two auxiliary 2D arrays, diag1 and diag2, of the same size as the grid.
diag1[i][j]will store the sum of elements on the main diagonal (top-left to bottom-right) ending at(i, j). It's calculated asdiag1[i][j] = grid[i][j] + (i > 0 && j > 0 ? diag1[i-1][j-1] : 0).diag2[i][j]will store the sum of elements on the anti-diagonal (top-right to bottom-left) ending at(i, j). It's calculated asdiag2[i][j] = grid[i][j] + (i > 0 && j < n-1 ? diag2[i-1][j+1] : 0).
These two prefix sum arrays can be computed in O(m * n) time.
After pre-computation, we iterate through all possible rhombus centers (r, c) and sizes k. The sum for a rhombus of size k > 0 can now be calculated in O(1) time using the prefix sum arrays. The sum is the total of four diagonal segments. A segment sum from point A to B on a diagonal can be calculated as prefix[B] - prefix[A] + grid[A]. Summing the four segments of the rhombus border and simplifying gives a constant time formula.
import java.util.*; class Solution { public int[] getBiggestThree(int[][] grid) { int m = grid.length; int n = grid[0].length; // diag1: prefix sums for top-left to bottom-right diagonals int[][] diag1 = new int[m][n]; // diag2: prefix sums for top-right to bottom-left diagonals int[][] diag2 = new int[m][n]; for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) { diag1[i][j] = grid[i][j] + (i > 0 && j > 0 ? diag1[i - 1][j - 1] : 0); } for (int j = n - 1; j >= 0; j--) { diag2[i][j] = grid[i][j] + (i > 0 && j < n - 1 ? diag2[i - 1][j + 1] : 0); } } TreeSet<Integer> sums = new TreeSet<>(); for (int r = 0; r < m; r++) { for (int c = 0; c < n; c++) { sums.add(grid[r][c]); // Size 0 rhombus // Iterate through possible rhombus sizes k > 0 for (int k = 1; r - k >= 0 && r + k < m && c - k >= 0 && c + k < n; k++) { // Corners int topR = r - k, topC = c; int rightR = r, rightC = c + k; int bottomR = r + k, bottomC = c; int leftR = r, leftC = c - k; // Sum of segments: (T->R) + (R->B) + (B->L) + (L->T) int sumTR = diag2[rightR][rightC] - diag2[topR][topC]; int sumRB = diag1[bottomR][bottomC] - diag1[rightR][rightC]; int sumBL = diag2[bottomR][bottomC] - diag2[leftR][leftC]; int sumLT = diag1[leftR][leftC] - diag1[topR][topC]; // The above subtractions exclude the top and bottom corners. // Add them back to get the full border sum. int totalSum = sumTR + sumRB + sumBL + sumLT + grid[topR][topC] + grid[bottomR][bottomC]; sums.add(totalSum); } } } int count = Math.min(3, sums.size()); int[] result = new int[count]; for (int i = 0; i < count; i++) { result[i] = sums.pollLast(); } return result; }}Complexity
Time
O(m * n * min(m, n)). Pre-computation of prefix sums takes `O(m * n)`. Then we iterate through all `m * n` centers, and for each center, iterate through `O(min(m, n))` sizes. Each sum calculation is `O(1)`. For a square grid of size `N`, this is `O(N^3)`.
Space
O(m * n). We need `O(m * n)` space for the two prefix sum arrays `diag1` and `diag2`. The `TreeSet` also uses space, but the dominant factor for auxiliary space is the prefix sum arrays.
Trade-offs
Pros
Much more efficient than the brute-force approach.
Reduces the complexity of the innermost loop from O(k) to O(1).
Cons
More complex to implement, especially the prefix sum calculation and the formula to get the rhombus sum.
Requires extra space for the prefix sum arrays.
Solutions
Solution
class Solution {public int[] getBiggestThree(int[][] grid) { int m = grid.length, n = grid[0].length; int[][] s1 = new int[m + 1][n + 2]; int[][] s2 = new int[m + 1][n + 2]; for (int i = 1; i <= m; ++i) { for (int j = 1; j <= n; ++j) { s1[i][j] = s1[i - 1][j - 1] + grid[i - 1][j - 1]; s2[i][j] = s2[i - 1][j + 1] + grid[i - 1][j - 1]; } } TreeSet<Integer> ss = new TreeSet<>(); for (int i = 1; i <= m; ++i) { for (int j = 1; j <= n; ++j) { int l = Math.min(Math.min(i - 1, m - i), Math.min(j - 1, n - j)); ss.add(grid[i - 1][j - 1]); for (int k = 1; k <= l; ++k) { int a = s1[i + k][j] - s1[i][j - k]; int b = s1[i][j + k] - s1[i - k][j]; int c = s2[i][j - k] - s2[i - k][j]; int d = s2[i + k][j] - s2[i][j + k]; ss.add(a + b + c + d - grid[i + k - 1][j - 1] + grid[i - k - 1][j - 1]); } while (ss.size() > 3) { ss.pollFirst(); } } } int[] ans = new int[ss.size()]; for (int i = 0; i < ans.length; ++i) { ans[i] = ss.pollLast(); } return ans; }}class Solution {public: vector<int> getBiggestThree(vector<vector<int>> &grid) { int m = grid.size(), n = grid[0].size(); vector<vector<int>> s1(m + 1, vector<int>(n + 2)); vector<vector<int>> s2(m + 1, vector<int>(n + 2)); for (int i = 1; i <= m; ++i) { for (int j = 1; j <= n; ++j) { s1[i][j] = s1[i - 1][j - 1] + grid[i - 1][j - 1]; s2[i][j] = s2[i - 1][j + 1] + grid[i - 1][j - 1]; } } set<int> ss; for (int i = 1; i <= m; ++i) { for (int j = 1; j <= n; ++j) { int l = min({i - 1, m - i, j - 1, n - j}); ss.insert(grid[i - 1][j - 1]); for (int k = 1; k <= l; ++k) { int a = s1[i + k][j] - s1[i][j - k]; int b = s1[i][j + k] - s1[i - k][j]; int c = s2[i][j - k] - s2[i - k][j]; int d = s2[i + k][j] - s2[i][j + k]; ss.insert(a + b + c + d - grid[i + k - 1][j - 1] + grid[i - k - 1][j - 1]); } while (ss.size() > 3) { ss.erase(ss.begin()); } } } return vector<int>(ss.rbegin(), ss.rend()); }};from sortedcontainers import SortedSet class Solution : def getBiggestThree ( self , grid : List [ List [ int ]]) -> List [ int ]: m , n = len ( grid ), len ( grid [ 0 ]) s1 = [[ 0 ] * ( n + 2 ) for _ in range ( m + 1 )] s2 = [[ 0 ] * ( n + 2 ) for _ in range ( m + 1 )] for i , row in enumerate ( grid , 1 ): for j , x in enumerate ( row , 1 ): s1 [ i ][ j ] = s1 [ i - 1 ][ j - 1 ] + x s2 [ i ][ j ] = s2 [ i - 1 ][ j + 1 ] + x ss = SortedSet () for i , row in enumerate ( grid , 1 ): for j , x in enumerate ( row , 1 ): l = min ( i - 1 , m - i , j - 1 , n - j ) ss . add ( x ) for k in range ( 1 , l + 1 ): a = s1 [ i + k ][ j ] - s1 [ i ][ j - k ] b = s1 [ i ][ j + k ] - s1 [ i - k ][ j ] c = s2 [ i ][ j - k ] - s2 [ i - k ][ j ] d = s2 [ i + k ][ j ] - s2 [ i ][ j + k ] ss . add ( a + b + c + d - grid [ i + k - 1 ][ j - 1 ] + grid [ i - k - 1 ][ j - 1 ] ) while len ( ss ) > 3 : ss . remove ( ss [ 0 ]) return list ( ss )[:: - 1 ]Video walkthrough
Newsletter
One sharp idea, every week
System design and interview prep — short enough to finish.
No spam. Unsubscribe anytime.
Practice
Same difficulty — related problems to reinforce the pattern.