2025-11-16:子矩阵的最小绝对差。用go语言,给定一个大小为 m×n 的整数矩阵 grid 和一个整数 k。
对于矩阵中每一个连续的 k×k 区域,求该区域内任意两个不同元素之间数值差的最小绝对值。把对每个左上角位于 (i, j) 的 k×k 子块得到的结果按位置放入一个尺寸为 (m - k + 1) × (n - k + 1) 的二维数组 ans,其中 ans[i][j] 就是以 (i, j) 为左上角的子块的答案。若某个子块内所有元素都相同,则它的结果记为 0。
注:这里的子矩阵(子块)是指所有满足 x1 ≤ x ≤ x2 且 y1 ≤ y ≤ y2 的单元格所组成的矩形区域。
1 <= m == grid.length <= 30。
1 <= n == grid[i].length <= 30。
-100000 <= grid[i][j] <= 100000。
1 <= k <= min(m, n)。
输入: grid = [[1,8],[3,-2]], k = 2。
输出: [[2]]。
解释:
只有一个可能的 k x k 子矩阵:[[1, 8], [3, -2]]。
子矩阵中的不同值为 [1, 8, 3, -2]。
子矩阵中的最小绝对差为 |1 - 3| = 2。因此,答案为 [[2]]。
题目来自力扣3567。
分步骤过程描述
-
初始化阶段:
- 获取输入矩阵
grid的维度m(行数)和n(列数)。 - 创建结果矩阵
ans,其尺寸为(m - k + 1) × (n - k + 1),用于存储每个k × k子矩阵的最小绝对差。初始化时,ans的所有元素默认值为 0(符合题目要求:若子矩阵元素全相同,结果记为 0)。 - 预分配一个长度为
k²的临时数组arr,用于高效存储每个子矩阵的元素,避免重复内存分配。
- 获取输入矩阵
-
遍历所有可能的子矩阵:
- 外层循环遍历行起始索引
i(范围:0到m - k),内层循环遍历列起始索引j(范围:0到n - k)。每个(i, j)对应一个以(i, j)为左上角的k × k子矩阵。 - 对于每个子矩阵:
- 提取元素:通过嵌套循环遍历该子矩阵的所有行(
i到i + k - 1)和所有列(j到j + k - 1),将元素依次存入临时数组arr的切片a中(通过arr[:0]重用内存,减少开销)。 - 排序元素:对切片
a中的k²个元素进行升序排序(使用 Go 的slices.Sort)。排序后,最小绝对差必然出现在相邻元素之间。 - 计算最小绝对差:
- 初始化
res为一个极大值(math.MaxInt)。 - 遍历排序后的
a,计算每对相邻元素的差(a[p] - a[p-1]),并更新res为这些差的最小值。 - 如果
res未被更新(即子矩阵元素全相同或仅有一个元素),则结果保持 0;否则将res赋值给ans[i][j]。
- 初始化
- 提取元素:通过嵌套循环遍历该子矩阵的所有行(
- 外层循环遍历行起始索引
-
返回结果:
- 遍历完成后,返回结果矩阵
ans,其中每个位置ans[i][j]存储了对应子矩阵的最小绝对差。
- 遍历完成后,返回结果矩阵
时间复杂度分析
- 子矩阵总数:存在
(m - k + 1) × (n - k + 1)个子矩阵,其数量级为 O(mn)。 - 单个子矩阵的处理:
- 元素提取:需要复制
k²个元素,时间复杂度为 O(k²)。 - 排序:对
k²个元素排序,基于比较的排序算法(如 Go 的slices.Sort)时间复杂度为 O(k² log k²) = O(k² log k)。 - 计算相邻差:遍历排序后的数组,时间复杂度为 O(k²)。
- 元素提取:需要复制
- 总时间复杂度:子矩阵数量 × 单个子矩阵处理时间,即 O(mn × k² log k)。
(注:由于k ≤ min(m, n)且m, n ≤ 30,该算法在给定约束下可行。)
空间复杂度分析
- 额外空间(不包括输入和输出):
- 临时数组
arr:固定大小为k²,用于存储子矩阵元素,空间复杂度为 O(k²)。 - 排序操作:递归排序算法(如快速排序)的栈空间复杂度为 O(log k²) = O(log k)。
- 临时数组
- 总额外空间复杂度:O(k²)(主导项)。
(注:结果矩阵ans是输出必要空间,通常不计入额外空间复杂度。)
关键点说明
- 优化策略:通过重用临时数组
arr避免重复内存分配,减少开销。 - 算法依据:利用排序后最小差必出现在相邻元素间的性质,确保正确性。
- 约束处理:当
k = 1时,子矩阵仅有一个元素,结果直接为 0;当k > 1时,正常计算相邻差。
此方法直接但有效,适用于题目中的小规模约束(m, n ≤ 30)。
Go完整代码如下:
package main
import (
"fmt"
"math"
"slices"
)
func minAbsDiff(grid [][]int, k int) [][]int {
m, n := len(grid), len(grid[0])
ans := make([][]int, m-k+1)
arr := make([]int, k*k)
for i := range ans {
ans[i] = make([]int, n-k+1)
for j := range ans[i] {
a := arr[:0] // 避免反复 make
for _, row := range grid[i : i+k] {
a = append(a, row[j:j+k]...)
}
slices.Sort(a)
res := math.MaxInt
for p := 1; p < len(a); p++ {
if a[p] > a[p-1] {
res = min(res, a[p]-a[p-1])
}
}
if res < math.MaxInt {
ans[i][j] = res
}
}
}
return ans
}
func main() {
grid := [][]int{{1, 8}, {3, -2}}
k := 2
result := minAbsDiff(grid, k)
fmt.Println(result)
}
Python完整代码如下:
# -*-coding:utf-8-*-
import math
def minAbsDiff(grid, k):
m, n = len(grid), len(grid[0])
ans = [[0] * (n - k + 1) for _ in range(m - k + 1)]
for i in range(len(ans)):
for j in range(len(ans[0])):
# 提取k×k子矩阵并展平
a = []
for row in grid[i:i+k]:
a.extend(row[j:j+k])
a.sort()
# 计算相邻非重复元素的最小差值
res = math.inf
for p in range(1, len(a)):
if a[p] > a[p-1]:
res = min(res, a[p] - a[p-1])
if res < math.inf:
ans[i][j] = res
return ans
def main():
grid = [[1, 8], [3, -2]]
k = 2
result = minAbsDiff(grid, k)
print(result)
if __name__ == "__main__":
main()
C++完整代码如下:
#include <iostream>
#include <vector>
#include <algorithm>
#include <climits>
#include <cmath>
using namespace std;
vector<vector<int>> minAbsDiff(vector<vector<int>>& grid, int k) {
int m = grid.size();
int n = grid[0].size();
vector<vector<int>> ans(m - k + 1, vector<int>(n - k + 1, 0));
for (int i = 0; i <= m - k; i++) {
for (int j = 0; j <= n - k; j++) {
vector<int> arr;
arr.reserve(k * k); // 预分配空间提高效率
// 提取k×k子矩阵
for (int x = i; x < i + k; x++) {
for (int y = j; y < j + k; y++) {
arr.push_back(grid[x][y]);
}
}
// 排序
sort(arr.begin(), arr.end());
// 计算相邻非重复元素的最小差值
int res = INT_MAX;
for (int p = 1; p < arr.size(); p++) {
if (arr[p] > arr[p-1]) {
res = min(res, arr[p] - arr[p-1]);
}
}
if (res < INT_MAX) {
ans[i][j] = res;
}
}
}
return ans;
}
int main() {
vector<vector<int>> grid = {{1, 8}, {3, -2}};
int k = 2;
vector<vector<int>> result = minAbsDiff(grid, k);
// 输出结果
cout << "[";
for (int i = 0; i < result.size(); i++) {
cout << "[";
for (int j = 0; j < result[i].size(); j++) {
cout << result[i][j];
if (j < result[i].size() - 1) {
cout << ",";
}
}
cout << "]";
if (i < result.size() - 1) {
cout << ",";
}
}
cout << "]" << endl;
return 0;
}