给定一个整数矩阵 A, 它有如下特性:
相邻的整数不同
矩阵有 n 行 m 列,n和m不会小于3。
对于所有的 i < n, 都有 A[i][0] < A[i][1] && A[i][m - 2] > A[i][m - 1]
对于所有的 j < m, 都有 A[0][j] < A[1][j] && A[n - 2][j] > A[n - 1][j]
我们定义一个位置 [i,j] 是峰值, 当且仅当它满足:
A[i][j] > A[i + 1][j] && A[i][j] > A[i - 1][j] &&
A[i][j] > A[i][j + 1] && A[i][j] > A[i][j - 1]
二维二分法
根据矩阵特性 任取某一行中最大的元素不断向其四个方向中比它大的元素移动 最后一定会找到一个peak 因为这一行其它元素都比这个元素小 所以这个元素一定不会移动到这一行的另一边 在一个封闭矩形中不断移动 在边界小于内圈元素的条件下 最终移动到的位置一定就是一个peak
所以可以根据这一特性 对行和列交替进行二分 每一个二分后就在去掉了一半的区间(可能存在peak 但本题只要找到一个peak就足够了) 最终会二分为四个点 即两行两列的交汇点 判断其中为peak的点
时间复杂度计算 T(n*m) = O(n) + O(m/2) + T(n/2 * m/2) 最终时间复杂度为O(n+m)
public class Solution {
/*
* @param A: An integer matrix
* @return: The index of the peak
*/
public List<Integer> findPeakII(int[][] A) {
// write your code here
List<Integer> results = new ArrayList<>();
int n = A.length;
int m = A[0].length;
int startRow = 1;
int endRow = n - 2;
int startCol = 1;
int endCol = m - 2;
// binary search
while ((startRow + 1 < endRow) || (startCol + 1 < endCol)) {
// divide row
if (startRow + 1 < endRow) {
// divide row
int midRow = startRow + (endRow - startRow) / 2;
int indexMaxCol = findMaxInRow(A, midRow, startCol - 1, endCol + 2);
if(isPeak(A, midRow, indexMaxCol)) {
results.add(midRow);
results.add(indexMaxCol);
return results;
} else if (A[midRow - 1][indexMaxCol] > A[midRow][indexMaxCol]) {
endRow = midRow;
} else {
startRow = midRow;
}
}
if (startCol + 1 < endCol) {
// dived indexMaxCol
int midCol = startCol + (endCol - startCol) / 2;
int indexMaxRow = findMaxInCol(A, midCol, startRow - 1, endRow + 2);
if (isPeak(A, indexMaxRow, midCol)) {
results.add(indexMaxRow);
results.add(midCol);
return results;
} else if (A[indexMaxRow][midCol - 1] > A[indexMaxRow][midCol]) {
endCol = midCol;
} else {
startCol = midCol;
}
}
}
// now we have four points left
// A[startRow][startCol], A[startRow][endCol], A[endRow][startCol], A[endRow][endCol]
if (isPeak(A, startRow, startCol)) {
results.add(startRow);
results.add(startCol);
return results;
}
if (isPeak(A, startRow, endCol)) {
results.add(startRow);
results.add(endCol);
return results;
}
if (isPeak(A, endRow, startCol)) {
results.add(endRow);
results.add(startCol);
return results;
}
if (isPeak(A, endRow, endCol)) {
results.add(endRow);
results.add(endCol);
return results;
}
return results;
}
private int findMaxInRow(int[][] A, int row, int start, int end) {
int maxIndex = 0;
int max = A[row][0];
for (int i = start; i < end; i++) {
if (A[row][i] > max) {
max = A[row][i];
maxIndex = i;
}
}
return maxIndex;
}
private int findMaxInCol(int[][] A, int col, int start, int end) {
int maxIndex = 0;
int max = A[0][col];
for (int i = start; i < end; i++) {
if (A[i][col] > max) {
max = A[i][col];
maxIndex = i;
}
}
return maxIndex;
}
private boolean isPeak(int[][] A, int row, int col) {
if (row <= 0 || row >= A.length - 1) {
return false;
}
if (col <= 0 || col >= A[0].length - 1) {
return false;
}
if (A[row][col] > A[row][col-1] && A[row][col] > A[row-1][col] && A[row][col] > A[row+1][col] && A[row][col] > A[row][col+1]) {
return true;
}
return false;
}
}