A peak element in a 2D grid is an element that is strictly greater than all of its adjacent neighbors to the left, right, top, and bottom.
Given a 0-indexed m x n
matrix mat
where no two adjacent cells are equal, find any peak element mat[i][j]
and return the length 2 array [i,j]
.
You may assume that the entire matrix is surrounded by an outer perimeter with the value -1
in each cell.
You must write an algorithm that runs in O(m log(n))
or O(n log(m))
time.
Example 1:
Input: mat = [[1,4],[3,2]] Output: [0,1] Explanation: Both 3 and 4 are peak elements so [1,0] and [0,1] are both acceptable answers.
Example 2:
Input: mat = [[10,20,15],[21,30,14],[7,16,32]] Output: [1,1] Explanation: Both 30 and 32 are peak elements so [1,1] and [2,2] are both acceptable answers.
Constraints:
m == mat.length
n == mat[i].length
1 <= m, n <= 500
1 <= mat[i][j] <= 105
The problem asks to find a peak element in a 2D matrix. A peak element is an element that is strictly greater than all its adjacent neighbors (left, right, top, bottom). The matrix is surrounded by a perimeter of -1. The algorithm must run in O(m log n) or O(n log m) time.
This solution uses binary search to efficiently find a peak element. The approach leverages the property that if an element is not a peak, then at least one of its neighbors is larger. This allows us to iteratively narrow down the search space.
Algorithm:
j
of the maximum element in the current row mid
.mat[mid][j]
with its bottom neighbor mat[mid + 1][j]
.
mat[mid][j] > mat[mid + 1][j]
, then a peak must exist in the upper half of the matrix (rows 0 to mid
). Update the right boundary r = mid
.mid + 1
to m-1). Update the left boundary l = mid + 1
.l == r
. At this point, the row l
contains a peak element. Find the column index of the maximum element in this row.[l, j]
of the peak element.Time Complexity: O(n log m) (or O(m log n) if we perform binary search on columns instead). The binary search takes O(log m) iterations. In each iteration, finding the maximum element in a row takes O(n) time.
Space Complexity: O(1). The algorithm uses a constant amount of extra space.
class Solution:
def findPeakGrid(self, mat: List[List[int]]) -> List[int]:
l, r = 0, len(mat) - 1
while l < r:
mid = (l + r) // 2
j = mat[mid].index(max(mat[mid])) #Find index of max in row
if mat[mid][j] > mat[mid + 1][j]:
r = mid
else:
l = mid + 1
j = mat[l].index(max(mat[l])) #Find index of max in final row
return [l, j]
Explanation of Python code:
l
and r
are the left and right boundaries for the binary search on rows.mid
is the middle row index.mat[mid].index(max(mat[mid]))
efficiently finds the column index j
of the maximum element in row mid
. The max()
function finds the maximum value and index()
finds its index.mat[mid][j] > mat[mid + 1][j]
determines which half to search in the next iteration.l
contains a peak. The code then finds the column index of the maximum value in that row.The logic remains the same across different languages. The primary differences lie in the syntax for array/list manipulation and finding the maximum element and its index. The examples below are provided without detailed comments as the algorithm is explained in detail above.
Java:
class Solution {
public int[] findPeakGrid(int[][] mat) {
int l = 0, r = mat.length - 1;
while (l < r) {
int mid = (l + r) / 2;
int j = maxPos(mat[mid]);
if (mat[mid][j] > mat[mid + 1][j]) {
r = mid;
} else {
l = mid + 1;
}
}
int j = maxPos(mat[l]);
return new int[] {l, j};
}
private int maxPos(int[] arr) {
int maxIndex = 0;
for (int i = 1; i < arr.length; ++i) {
if (arr[i] > arr[maxIndex]) {
maxIndex = i;
}
}
return maxIndex;
}
}
C++:
class Solution {
public:
vector<int> findPeakGrid(vector<vector<int>>& mat) {
int l = 0, r = mat.size() - 1;
while (l < r) {
int mid = (l + r) / 2;
int j = max_element(mat[mid].begin(), mat[mid].end()) - mat[mid].begin();
if (mat[mid][j] > mat[mid + 1][j]) {
r = mid;
} else {
l = mid + 1;
}
}
int j = max_element(mat[l].begin(), mat[l].end()) - mat[l].begin();
return {l, j};
}
};
Go:
func findPeakGrid(mat [][]int) []int {
l, r := 0, len(mat)-1
for l < r {
mid := (l + r) / 2
j := maxPos(mat[mid])
if mat[mid][j] > mat[mid+1][j] {
r = mid
} else {
l = mid + 1
}
}
return []int{l, maxPos(mat[l])}
}
func maxPos(arr []int) int {
maxIndex := 0
for i := 1; i < len(arr); i++ {
if arr[i] > arr[maxIndex] {
maxIndex = i
}
}
return maxIndex
}
Javascript:
/**
* @param {number[][]} mat
* @return {number[]}
*/
var findPeakGrid = function(mat) {
let l = 0, r = mat.length - 1;
while (l < r) {
let mid = Math.floor((l + r) / 2);
let j = mat[mid].indexOf(Math.max(...mat[mid]));
if (mat[mid][j] > mat[mid + 1][j]) {
r = mid;
} else {
l = mid + 1;
}
}
let j = mat[l].indexOf(Math.max(...mat[l]));
return [l, j];
};
TypeScript:
function findPeakGrid(mat: number[][]): number[] {
let l = 0, r = mat.length - 1;
while (l < r) {
let mid = Math.floor((l + r) / 2);
let j = mat[mid].indexOf(Math.max(...mat[mid]));
if (mat[mid][j] > mat[mid + 1][j]) {
r = mid;
} else {
l = mid + 1;
}
}
let j = mat[l].indexOf(Math.max(...mat[l]));
return [l, j];
}
Rust:
impl Solution {
pub fn find_peak_grid(mat: Vec<Vec<i32>>) -> Vec<i32> {
let mut l = 0;
let mut r = mat.len() - 1;
while l < r {
let mid = (l + r) / 2;
let j = mat[mid].iter().enumerate().max_by_key(|&(_, x)| x).unwrap().0;
if mat[mid][j] > mat[mid + 1][j] {
r = mid;
} else {
l = mid + 1;
}
}
let j = mat[l].iter().enumerate().max_by_key(|&(_, x)| x).unwrap().0;
vec![l as i32, j as i32]
}
}
These examples showcase the adaptability of the binary search approach across various programming languages, highlighting the core algorithmic efficiency. Remember to adapt the code based on the specific requirements of your coding environment and chosen language.