一个 2D 网格中的 峰值 是指那些 严格大于 其相邻格子(上、下、左、右)的元素。
给你一个 从 0 开始编号 的
m
x
n
m x n
mxn 矩阵
m
a
t
mat
mat ,其中任意两个相邻格子的值都 不相同 。找出 任意一个 峰值
m
a
t
[
i
]
[
j
]
mat[i][j]
mat[i][j] 并 返回其位置
[
i
,
j
]
[i,j]
[i,j] 。
你可以假设整个矩阵周边环绕着一圈值为 -1 的格子。
要求必须写出时间复杂度为
O
(
m
l
o
g
(
n
)
)
O(m log(n))
O(mlog(n)) 或
O
(
n
l
o
g
(
m
)
)
O(n log(m))
O(nlog(m)) 的算法
提示:
方法:二分法;
令
m
m
m 和
n
n
n 分别为
m
a
t
mat
mat 的行数和列数。首先对于一维数组,因为任意两个相邻的格子的值都不相同,所以它的最大值必定是该一维数组的峰值。基于这一点,我们可以只考虑每一行的最大值,记第
i
i
i 行的最大值为第
j
i
j_i
ji?列元素,如果
m
a
t
[
i
]
[
j
i
]
>
m
a
t
[
i
?
1
]
[
j
i
]
mat[i][j_i]>mat[i?1][j_i]
mat[i][ji?]>mat[i?1][ji?]且
m
a
t
[
i
]
[
j
i
]
>
m
a
t
[
i
+
1
]
[
j
i
]
mat[i][j_i]>mat[i+1][j_i]
mat[i][ji?]>mat[i+1][ji?](对于数组越界的情况,取
?
1
?1
?1 值),那么
(
i
,
j
i
)
(i,j_i)
(i,ji?)即为结果。
对于题目给定的条件,是否一定存在峰值呢?答案是肯定的。证明可以采用反证法:
如果不存在峰值,根据题目给定的条件,我们知道第0 行的最大值比它上面的格子(值为 ?1)大,那么初始时有 i = 0 i=0 i=0 行的最大值满足比上面格子大的条件。如果第 i i i行的最大值满足比它上面的格子大这一条件,那么根据不存在峰值的前提,我们有 m a t [ i ] [ j i ] < m a t [ i + 1 ] [ j i ] mat[i][ji]<mat[i+1][j_i] mat[i][ji]<mat[i+1][ji?],而 m a t [ i + 1 ] [ j i + 1 ] ≥ m a t [ i + 1 ] [ j i ] > m a t [ i ] [ j i ] ≥ m a t [ i ] [ j i + 1 ] mat[i+1][j_i+1]≥mat[i+1][j_i]>mat[i][j_i]≥mat[i][j_{i+1}] mat[i+1][ji?+1]≥mat[i+1][ji?]>mat[i][ji?]≥mat[i][ji+1?],从而我们有 m a t [ i + 1 ] [ j i + 1 ] > m a t [ i ] [ j i + 1 ] mat[i+1][j_i+1]>mat[i][j_{i+1}] mat[i+1][ji?+1]>mat[i][ji+1?],即 i + 1 i+1 i+1也满足条件。基于以上推导,使用数学归纳法,当 i = m ? 1 i=m?1 i=m?1时,有 m a t [ m ? 1 ] [ j m ? 1 ] > m a t [ m ? 2 ] [ j m ? 1 ] mat[m?1][j_{m?1}]>mat[m?2][j_{m?1}] mat[m?1][jm?1?]>mat[m?2][jm?1?],而下面的格子值为 ? 1 -1 ?1,显然 ( m ? 1 , j m ? 1 ) (m?1,j_{m?1}) (m?1,jm?1?) 为峰值,与不存在峰值矛盾。
根据以上证明,我们也可以得出这样一个结论,即如果 i 1 i_1 i1?行的最大值比它上面的格子大, i 2 i_2 i2?行比它下面的格子大,且 i 1 ≤ i 2 i_1 \leq i_2 i1?≤i2? ,那么 [ i 1 , i 2 ] [i_1,i_2] [i1?,i2?]之间一定存在峰值。基于这个结论,我们可以使用二分法来求解问题,初始时 l o w = 0 , h i g h = m ? 1 low=0,high=m-1 low=0,high=m?1:
使用了这样的方法,我们的时间复杂度为 O ( n l o g m ) O(nlog_m) O(nlogm?)。
class Solution:
def findPeakGrid(self, mat: List[List[int]]) -> List[int]:
m = len(mat)
#n = len(mat[0])
low = 0
high = m-1
while low <= high:
i = (low + high)//2
j = mat[i].index(max(mat[i]))
if i-1>=0 and mat[i][j] < mat[i-1][j]:
high = i-1
continue
if i+1 < m and mat[i][j] < mat[i+1][j]:
low = i + 1
continue
return [i,j]
return None