原文地址:https://leileiluoluo.com/posts/leetcode-01matrix-problem.html
1 题目描述
给定一个元素只可为0或1的矩阵,对矩阵中每个元素,计算出该元素点与最近的元素0的距离(相邻两个元素的距离为1)。
例子1:
输入:
0 0 0
0 1 0
0 0 0
输出:
0 0 0
0 1 0
0 0 0
例子2:
输入:
0 0 0
0 1 0
1 1 1
输出:
0 0 0
0 1 0
1 2 1
注:
给定矩阵的元素个数不超过10000;
给定矩阵至少包含1个0;
相邻元素的连接方式只可为上、下、左、右。
题目出处:
https://leetcode.com/problems/01-matrix/
2 解决思路
针对图像识别的模式识别中,计算图像固定区域特征时,有积分图的概念。
解决本题时借用一下该概念,按如下步骤判断每个元素与最近的元素0的距离。
1)扫描一遍矩阵,对每个元素点计算其左上角所有元素点的数值之和,结果为一个和值矩阵,称之为sumGraph;
2)针对原始矩阵的某个元素点,计算与其最近的0元素的距离时,首先根据sumGraph计算在指定半径覆盖的方形区域内是否包含0(根据sumGraph计算出该区域元素值总和,若小于全1总和即包含0);
3)若该区域包含0,则在该区域找到合个0,并算出最近的距离;
4)若该区域不包含0,则扩大搜索半径,直至找到包含0的方形区域并按步骤3)计算出结果。
5)按如上2)-4)步骤扫描原始矩阵并计算结果,直至所有元素点计算完成,并返回结果矩阵。
注:
本文算法初试搜索半径为1,若此半径上下左右围起的方形区域不包含0,则将半径扩大一倍。
逐步扩大半径及全图扫描计算如下图所示。
3 golang实现代码
https://github.com/olzhy/leetcode/blob/master/542_01_Matrix/test.go
func updateMatrix(m [][]int) [][]int {
const MaxInt = 999999999
max := func(n1, n2 int) int {
if n1 >= n2 {
return n1
}
return n2
}
steps := func(x1, y1, x2, y2 int) int {
abs := func(a, b int) int {
c := a - b
if c < 0 {
return -c
}
return c
}
return abs(x1, x2) + abs(y1, y2)
}
containsZero := func(sumGraph [][]int, x, y, radius int) bool {
xMin, yMin, xMax, yMax := x-radius-1, y-radius-1, x+radius, y+radius
if xMax > len(sumGraph)-1 {
xMax = len(sumGraph) - 1
}
if yMax > len(sumGraph[0])-1 {
yMax = len(sumGraph[0]) - 1
}
sumAllNotZeros := MaxInt
sum := sumAllNotZeros
if xMin < 0 && yMin < 0 {
sumAllNotZeros = (xMax + 1) * (yMax + 1)
sum = sumGraph[xMax][yMax]
} else if xMin < 0 && yMin >= 0 {
sumAllNotZeros = (xMax + 1) * (yMax - yMin)
sum = sumGraph[xMax][yMax] - sumGraph[xMax][yMin]
} else if xMin >= 0 && yMin < 0 {
sumAllNotZeros = (xMax - xMin) * (yMax + 1)
sum = sumGraph[xMax][yMax] - sumGraph[xMin][yMax]
} else {
sumAllNotZeros = (xMax - xMin) * (yMax - yMin)
sum = sumGraph[xMax][yMax] - sumGraph[xMin][yMax] - sumGraph[xMax][yMin] + sumGraph[xMin][yMin]
}
return sum < sumAllNotZeros
}
findMinStepsAround := func(m [][]int, x, y, radius int) int {
xMin, yMin, xMax, yMax := x-radius, y-radius, x+radius, y+radius
if xMin < 0 {
xMin = 0
}
if yMin < 0 {
yMin = 0
}
if xMax > len(m)-1 {
xMax = len(m) - 1
}
if yMax > len(m[0])-1 {
yMax = len(m[0]) - 1
}
minSteps := MaxInt
for i := xMin; i <= xMax; i++ {
for j := yMin; j <= yMax; j++ {
if 0 == m[i][j] {
steps := steps(x, y, i, j)
if steps < minSteps {
minSteps = steps
}
}
}
}
return minSteps
}
findMinStepsWithScalingRadius := func(m, sumGraph [][]int, x, y int) (int, int) {
minRadius, maxRadius := 1, max(max(x, y), max(len(m)-1-x, len(m)-1-y))
minSteps := MaxInt
r := minRadius
for r = minRadius; r <= maxRadius; r *= 2 {
containsZero := containsZero(sumGraph, x, y, r)
if containsZero {
minSteps = findMinStepsAround(m, x, y, r)
if minSteps > r {
continue
}
return minSteps, r
}
}
if maxRadius != r {
minSteps = findMinStepsAround(m, x, y, maxRadius)
}
return minSteps, r
}
sumGraph := func(m [][]int) [][]int {
sumGraph := make([][]int, len(m))
for i := range m {
sumGraph[i] = make([]int, len(m[i]))
for j := range m[i] {
sumGraph[i][j] = 0
if 0 == j && 0 == i {
sumGraph[i][j] = m[i][j]
} else if 0 == j && i > 0 {
sumGraph[i][j] = sumGraph[i-1][j] + m[i][j]
} else if j > 0 && 0 == i {
sumGraph[i][j] = sumGraph[i][j-1] + m[i][j]
} else {
sumGraph[i][j] = sumGraph[i][j-1] + sumGraph[i-1][j] - sumGraph[i-1][j-1] + m[i][j]
}
}
}
return sumGraph
}
sumG := sumGraph(m)
updated := make([][]int, len(m))
for i := range m {
updated[i] = make([]int, len(m[i]))
for j := range m[i] {
minSteps, _ := findMinStepsWithScalingRadius(m, sumG, i, j)
updated[i][j] = minSteps
}
}
return updated
}