LeetCode 3567 - Minimum Absolute Difference in Sliding Submatrix
The problem requires computing a specific statistic for every contiguous k x k submatrix of a given m x n integer matrix grid. For each submatrix, we must find the minimum absolute difference between any two distinct elements.
Difficulty: 🟡 Medium
Topics: Array, Sorting, Matrix
Solution
Problem Understanding
The problem requires computing a specific statistic for every contiguous k x k submatrix of a given m x n integer matrix grid. For each submatrix, we must find the minimum absolute difference between any two distinct elements. The final result is a 2D array ans of size (m - k + 1) x (n - k + 1) where each entry corresponds to a top-left corner of a submatrix.
In simpler terms, you slide a k x k window over the matrix, and for each window, you identify all distinct numbers, compute all pairwise differences, and return the smallest. If all numbers in the submatrix are identical, the minimum difference is 0.
The constraints tell us the matrix is relatively small (m, n ≤ 30) and element values range between -10^5 and 10^5. The k parameter can vary between 1 and the smaller dimension of the matrix. Edge cases include single-element submatrices (k = 1) and submatrices where all elements are the same, which require careful handling to avoid errors in computing differences.
Approaches
Brute Force Approach
A straightforward solution is to iterate over all possible k x k submatrices. For each submatrix, extract all elements, deduplicate them, sort the list, and compute the minimum difference between consecutive elements. Sorting is necessary because the minimum difference will always occur between two consecutive numbers in a sorted list. This method guarantees correctness but is inefficient because we are repeatedly processing overlapping submatrices, which can be costly when k is large.
Optimal Approach
The key observation for optimization is that the submatrix size is small (k ≤ 30) and the total number of submatrices is bounded by (m - k + 1) * (n - k + 1) ≤ 900. This allows a simple brute-force-like solution to work within constraints, but we can avoid sorting by using a SortedList or similar data structure that maintains order while inserting elements, allowing us to calculate minimum differences efficiently. This reduces the repeated sorting overhead and simplifies finding consecutive differences.
| Approach | Time Complexity | Space Complexity | Notes |
|---|---|---|---|
| Brute Force | O((m-k+1)*(n-k+1)_k^2_log(k^2)) | O(k^2) | Extract submatrix, sort, compute min difference |
| Optimal | O((m-k+1)*(n-k+1)*k^2) | O(k^2) | Use a sorted data structure to maintain elements for min difference computation |
Algorithm Walkthrough
- Initialize an empty 2D array
ansof size(m - k + 1) x (n - k + 1)to store the results. - Iterate over all valid top-left coordinates
(i, j)fork x ksubmatrices. - For each submatrix, extract all
k^2elements into a list. - Convert the list to a
setto remove duplicates, since we only care about distinct values. - If the submatrix has fewer than 2 distinct elements, set the corresponding
ans[i][j]to 0. - Otherwise, sort the distinct elements to easily find consecutive differences.
- Iterate through the sorted list and compute the absolute differences between consecutive elements.
- Set
ans[i][j]to the minimum of these differences. - Return
ansafter processing all submatrices.
Why it works: Sorting guarantees that the smallest absolute difference between distinct values is always between two consecutive elements. Using a set ensures duplicates do not affect the calculation. Iterating through all valid positions covers every submatrix.
The problem asks us to analyze every possible contiguous k x k submatrix inside a given m x n integer grid and compute a specific statistic for each of those submatrices. For each such window, we must find the minimum absolute difference between any two distinct values inside that window, and place that result into a corresponding position in the output matrix.
In simpler terms, we slide a square window of size k x k over the grid. For each position of the window, we look at all values inside it, and we want the smallest difference between any two different numbers in that region. If all values in the window are identical, then the answer is 0.
The output is a reduced matrix of size (m - k + 1) x (n - k + 1), where each cell corresponds to the top-left corner of a valid k x k submatrix in the original grid.
The constraints are small, with m, n <= 30, meaning at most 900 cells total, and at most 900 submatrices. Each submatrix contains at most k^2 <= 900 elements. This strongly suggests that an O(k^2 log k^2) per-window solution is acceptable.
Important edge cases include when k = 1, where every submatrix contains only one element and the answer is always 0, and when all values in a submatrix are identical, which also forces the answer to be 0. Negative values are also allowed, so sorting or absolute difference logic must handle full integer ranges correctly.
Approaches
The brute-force idea is straightforward. For each k x k submatrix, we extract all values into a list, sort it, and then compute the minimum difference between consecutive elements in the sorted order. This works because the smallest absolute difference between any two numbers must occur between adjacent values in sorted order. However, repeating this extraction and sorting for every window can still be expensive, though it remains feasible under constraints.
The key observation is that once values are sorted, we do not need to compare all pairs. Only adjacent differences matter. This reduces the problem inside each window from quadratic comparisons to linear scanning after sorting.
A more advanced optimization would maintain a balanced structure while sliding the window, but given the small constraints, recomputing per window is sufficiently optimal and simpler to implement correctly.
Complexity Comparison
| Approach | Time Complexity | Space Complexity | Notes |
|---|---|---|---|
| Brute Force | O((m-k+1)(n-k+1) · k² log k²) | O(k²) | Extract and sort each window independently |
| Optimal (sort per window + scan) | O((m-k+1)(n-k+1) · k² log k) | O(k²) | Sort each window and compute adjacent differences |
Algorithm Walkthrough
The optimal approach processes each valid top-left corner of a k x k submatrix systematically.
- We iterate over every possible starting row
ifrom0tom - k, and every starting columnjfrom0ton - k. Each(i, j)defines a uniquek x kwindow. - For each window, we collect all
k^2values into a temporary list. This step is necessary because we need a flat structure to sort and compare values efficiently. - We sort the collected values in non-decreasing order. Sorting ensures that any pair with minimal absolute difference must appear adjacent in this ordering.
- We initialize a variable
bestwith infinity. We then scan through the sorted list from left to right, computing differences between consecutive elements. - If we find a difference of
0, we immediately return0for that window because this is the smallest possible value. - Otherwise, we keep tracking the minimum difference found during the scan.
- We store this result in the output matrix at position
(i, j).
Why it works
The correctness relies on a standard ordering property: for any set of numbers, the minimum absolute difference must occur between two consecutive elements in sorted order. Any non-adjacent pair must have at least one intermediate value, which cannot produce a smaller gap than its adjacent neighbors. This guarantees that scanning only adjacent pairs after sorting is sufficient to find the correct answer for each submatrix.
Python Solution
from typing import List
class Solution:
def minAbsDiff(self, grid: List[List[int]], k: int) -> List[List[int]]:
m, n = len(grid), len(grid[0])
ans = [[0] * (n - k + 1) for _ in range(m - k + 1)]
for i in range(m - k + 1):
for j in range(n - k + 1):
elements = []
for x in range(i, i + k):
for y in range(j, j + k):
elements.append(grid[x][y])
distinct_elements = sorted(set(elements))
if len(distinct_elements) < 2:
ans[i][j] = 0
else:
min_diff = float('inf')
for a, b in zip(distinct_elements, distinct_elements[1:]):
min_diff = min(min_diff, b - a)
ans[i][j] = min_diff
return ans
Implementation Explanation: The outer loops iterate over the starting points of each k x k submatrix. Inner loops collect elements. Converting to a set removes duplicates, and sorting ensures we can compute consecutive differences efficiently. The zip function pairs consecutive elements, and we track the minimum difference. The result array is filled progressively.
res_rows = m - k + 1
res_cols = n - k + 1
ans = [[0] * res_cols for _ in range(res_rows)]
for i in range(res_rows):
for j in range(res_cols):
values = []
for r in range(i, i + k):
for c in range(j, j + k):
values.append(grid[r][c])
values.sort()
best = float("inf")
for t in range(1, len(values)):
diff = values[t] - values[t - 1]
if diff == 0:
best = 0
break
if diff < best:
best = diff
ans[i][j] = best
return ans
The implementation directly follows the algorithm structure. We iterate over all valid windows, flatten each `k x k` region into a list, sort it, and then compute adjacent differences. The early exit on zero improves efficiency in duplicate-heavy cases. The result matrix is filled incrementally.
## Go Solution
```go
import (
"math"
"sort"
)
func minAbsDiff(grid [][]int, k int) [][]int {
m, n := len(grid), len(grid[0])
ans := make([][]int, m-k+1)
for i := range ans {
ans[i] = make([]int, n-k+1)
}
for i := 0; i <= m-k; i++ {
for j := 0; j <= n-k; j++ {
elementsMap := make(map[int]struct{})
for x := i; x < i+k; x++ {
for y := j; y < j+k; y++ {
elementsMap[grid[x][y]] = struct{}{}
}
}
distinctElements := make([]int, 0, len(elementsMap))
for val := range elementsMap {
distinctElements = append(distinctElements, val)
}
if len(distinctElements) < 2 {
ans[i][j] = 0
} else {
sort.Ints(distinctElements)
minDiff := math.MaxInt32
for idx := 1; idx < len(distinctElements); idx++ {
diff := distinctElements[idx] - distinctElements[idx-1]
if diff < minDiff {
minDiff = diff
}
}
ans[i][j] = minDiff
}
}
}
return ans
}
Go-specific Notes: We use a map to deduplicate elements since Go does not have a built-in set. Sorting and min difference calculation is the same conceptually. Slices are dynamically allocated for results and intermediate storage.
Worked Examples
Example 1: grid = [[1,8],[3,-2]], k = 2
Extract submatrix [[1,8],[3,-2]] → distinct [1,8,3,-2] → sort [-2,1,3,8] → differences [3,2,5] → min is 2 → result [[2]].
Example 2: grid = [[3,-1]], k = 1
Each submatrix has a single element → distinct length < 2 → result [[0,0]].
Example 3: grid = [[1,-2,3],[2,3,5]], k = 2
Submatrix (0,0) [[1,-2],[2,3]] → distinct [-2,1,2,3] → min diff 1.
Submatrix (0,1) [[-2,3],[3,5]] → distinct [-2,3,5] → min diff 2.
Final result [[1,2]].
func minAbsDiff(grid [][]int, k int) [][]int {
m := len(grid)
n := len(grid[0])
resRows := m - k + 1
resCols := n - k + 1
ans := make([][]int, resRows)
for i := range ans {
ans[i] = make([]int, resCols)
}
values := make([]int, 0, k*k)
for i := 0; i < resRows; i++ {
for j := 0; j < resCols; j++ {
values = values[:0]
for r := i; r < i+k; r++ {
for c := j; c < j+k; c++ {
values = append(values, grid[r][c])
}
}
quickSort(values)
best := int(^uint(0) >> 1)
for t := 1; t < len(values); t++ {
diff := values[t] - values[t-1]
if diff == 0 {
best = 0
break
}
if diff < best {
best = diff
}
}
ans[i][j] = best
}
}
return ans
}
func quickSort(arr []int) { if len(arr) <= 1 { return } pivot := arr[len(arr)/2] left := 0 right := len(arr) - 1
i, j := 0, right
for i <= j {
for arr[i] < pivot {
i++
}
for arr[j] > pivot {
j--
}
if i <= j {
arr[i], arr[j] = arr[j], arr[i]
i++
j--
}
}
if j > 0 {
quickSort(arr[:j+1])
}
if i < len(arr) {
quickSort(arr[i:])
}
}
In Go, we explicitly reuse a preallocated slice `values` to avoid repeated allocations. The sorting is implemented via a custom quicksort to remain self-contained, though in production code `sort.Ints` would be preferred for correctness and simplicity. Integer initialization uses `int(^uint(0) >> 1)` to represent infinity.
## Worked Examples
### Example 1
Input grid:
`[[1,8],[3,-2]], k = 2`
For the only window `(0,0)`, we collect `[1, 8, 3, -2]`.
After sorting: `[-2, 1, 3, 8]`
Differences:
- `1 - (-2) = 3`
- `3 - 1 = 2`
- `8 - 3 = 5`
Minimum is `2`, so output is `[[2]]`.
### Example 2
Input grid:
`[[3,-1]], k = 1`
Each window has a single element.
For `(0,0)`: `[3]` → no pair, so `0`
For `(0,1)`: `[-1]` → no pair, so `0`
Output is `[[0, 0]]`.
### Example 3
Input grid:
`[[1,-2,3],[2,3,5]], k = 2`
Window `(0,0)`:
Values: `[1, -2, 2, 3]`
Sorted: `[-2, 1, 2, 3]`
Differences: `3, 1, 1` → result `1`
Window `(0,1)`:
Values: `[-2, 3, 3, 5]`
Sorted: `[-2, 3, 3, 5]`
Differences: `5, 0, 2` → result `0`
Note that duplicates produce a zero minimum difference, which is immediately optimal.
Final output: `[[1, 0]]`
## Complexity Analysis
| Measure | Complexity | Explanation |
| --- | --- | --- |
| Time | O((m-k+1)*(n-k+1)_k^2_log(k^2)) | For each submatrix, deduplicate and sort `k^2` elements |
| Space | O(k^2) | Storage for submatrix elements and distinct elements set |
Although the worst-case complexity involves sorting, `k` is at most 30, making this feasible within the constraints.
| Time | O((m-k+1)(n-k+1) · k² log k) | Each window collects k² elements and sorts them |
| Space | O(k²) | Temporary storage for values in one window |
The time complexity is acceptable because both dimensions are bounded by 30, leading to at most 900 windows, and each window processes at most 900 elements.
## Test Cases
Provided examples
assert Solution().minAbsDiff([[1,8],[3,-2]], 2) == [[2]] # single 2x2 submatrix assert Solution().minAbsDiff([[3,-1]], 1) == [[0,0]] # single-element submatrices assert Solution().minAbsDiff([[1,-2,3],[2,3,5]], 2) == [[1,2]] # multiple submatrices
Edge cases
assert Solution().minAbsDiff([[5]], 1) == [[0]] # 1x1 matrix assert Solution().minAbsDiff([[1,1,1],[1,1,1],[1,1,1]], 2) == [[0,0],[0,0]] # all identical assert Solution().minAbsDiff([[1,2,3],[4,5,6],[7,8,9]], 3) == [[1]] # full matrix assert Solution().minAbsDiff([[1,8],[3,-2]], 2) == [[2]] # basic 2x2 case assert Solution().minAbsDiff([[3,-1]], 1) == [[0,0]] # k=1 edge case assert Solution().minAbsDiff([[1,-2,3],[2,3,5]], 2) == [[1,0]] # multiple windows assert Solution().minAbsDiff([[5]], 1) == [[0]] # single cell grid assert Solution().minAbsDiff([[1,1],[1,1]], 2) == [[0]] # all duplicates assert Solution().minAbsDiff([[1,100,1000],[2,3,4]], 2) == [[1,1]] # varied values
| Test | Why |
| --- | --- |
| Single 2x2 submatrix | Validates calculation of min absolute difference |
| Single-element submatrices | Tests handling k=1 |
| 2x2 matrix | verifies full-window computation |
| k=1 case | ensures single element windows return 0 |
| multiple windows | validates sliding correctness |
| single cell | smallest possible input |
| all duplicates | checks zero-difference propagation |
| mixed values | ensures correct min adjacent diff logic |
## Edge Cases
One important edge case is when `k = 1`. In this situation, every submatrix contains exactly one element, so there are no pairs to compare. A naive implementation might attempt to compute differences on an empty or singleton list, but the correct behavior is always to return `0` for every position.
Another edge case is when all values in a submatrix are identical. After sorting, all elements are equal, so every adjacent difference is `0`. The implementation correctly short-circuits when a zero difference is found, ensuring optimal performance and correctness.
A third edge case involves negative numbers mixed with positive numbers. Since absolute difference is independent of sign ordering, sorting still correctly aligns values so that the minimum difference is found between adjacent elements. The algorithm does not treat negatives specially, which avoids subtle bugs in manual absolute difference comparisons.