一、题目描述
编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target 。该矩阵具有以下特性:
每行的元素从左到右升序排列。
每列的元素从上到下升序排列。
示例 1:
输入:matrix = [[1,4,7,11,15],[2,5,8,12,19],[3,6,9,16,22],[10,13,14,17,24],[18,21,23,26,30]], target = 5
输出:true
示例 2:
输入:matrix = [[1,4,7,11,15],[2,5,8,12,19],[3,6,9,16,22],[10,13,14,17,24],[18,21,23,26,30]], target = 20
输出:false
二、代码实现
方法一:直接查找(循环遍历)
解题思路
遍历整个矩阵 matrix,判断 target 是否出现。
代码实现
func searchMatrix(matrix [][]int, target int) bool {
for _, arr := range matrix{
for _, num := range arr {
if num == target {
return true
}
}
}
return false
}
复杂度分析
- 时间复杂度:O(m x n),遍历一行时间复杂度为 O(n),最多需要遍历 m 行。
- 空间复杂度:O(1)。
方法二:遍历 + 二分查找
解题思路
矩阵 matrix 中每一行的元素都是升序排列的,使用二分查找遍历每一行元素,判断 target 是否在该行中,从而判断 target 是否出现。
代码实现
func searchMatrix(matrix [][]int, target int) bool {
for _, arr := range matrix{
if binarySearch(arr, target) {
return true
}
}
return false
}
// 二分查找
func binarySearch(numsArr []int, target int) bool {
left, right := 0, len(numsArr) - 1
for left <= right {
mid := (right + left) / 2
num := numsArr[mid]
if num < target {
left = mid + 1
} else if num > target {
right = mid - 1
} else {
return true
}
}
return false
}
复杂度分析
- 时间复杂度:O(m x logn),二分查找遍历一行时间复杂度为 O(logn),最多需要二分查找 m 行。
- 空间复杂度:O(1)。
方法三:双指针(二叉搜索树)
解题思路
从矩阵 matrix 的右上角 (0,n−1) 进行搜索。对于右上角的元素,其左边列的元素必定比它小,其下面行的元素必定比它大,所以从右上角开始搜索,遇到比target大的元素,则往左边列搜索,遇到比target小的元素,则往下一行搜索,如果搜索到左下角,还没有搜索到,则不存在target元素。
我们将矩阵逆时针旋转 45° ,并将其转化为图形式,发现其类似于二叉搜索树,即对于每个元素,其左分支元素更小、右分支元素更大。因此,通过从 “根节点” 开始搜索,遇到比 target 大的元素就向左,反之向右,即可找到目标值 target 。
代码实现
func searchMatrix(matrix [][]int, target int) bool {
i, j := 0, len(matrix[0]) - 1
for i < len(matrix) && j >= 0 {
if matrix[i][j] > target {
j--
} else if matrix[i][j] < target {
i++
} else {
return true
}
}
return false
}
复杂度分析
-
时间复杂度:O(m+n)。 j 最多被减少 n 次,i 最多被增加 m 次,总搜索次数为 m+n。
-
空间复杂度:O(n)。