前言
从矩阵中搜索一个元素,暴力搜索O(mn)。如果行列有序,则可二分 + 剪枝来进行搜索。除此之外,利用行列同时有序,在搜索的过程中将可搜索空间压缩,直至找到target,为Z字搜索O(m + n)。
一、搜索二维矩阵
二、二分 & Z字压缩
1、idea
a.暴力,通过行列遍历,寻找target,O(mn);
b.保持有序对二分的紧密联系,每行通过二分搜索,再利用列的有序性进行剪枝。
c.Z字压缩,同时利用行列有序,对可搜索空间进行逐行逐列进行压缩,寻找target。
2、二分 + 剪枝
func searchMatrix(matrix [][]int, target int) bool {
// 每行进行搜索
for i := 0;i < len(matrix);i++ {
// 剪枝,当最小值都大于target,就没必要搜索了。
if matrix[i][0] > target {
return false
}
// 二分查找
if binarySearch(matrix[i],target) {
return true
}
}
return false
}
// 二分搜索
func binarySearch(nums []int,target int) bool {
low,high := 0,len(nums) - 1
for low <= high {
mid := low + (high - low) >> 1
midVal := nums[mid]
if target == midVal {
return true
}
if target < midVal {
high = mid - 1
}else {
low = mid + 1
}
}
return false
}
// 搜索矩阵中是否有元素值为target
// 行列升序,必不可能暴力搜索。
// 针对每行,进行二分查找,可降低时间复杂度至mlogn
3、Z字压缩
// 方法二:Z字搜索
// 充分利用行列有序,对搜索空间逐渐压缩,O(m + n)。
// 根据右上角的值val情况,当val > target,说明最后一列的所有值都大于target,则压缩一列;反之,第一列都小于target,则压缩一行。
func searchMatrix(matrix [][]int, target int) bool {
x,y := 0,len(matrix[0]) - 1
bx,by := len(matrix) - 1,0
for ; x <= bx && y >= by; {
// 找到target
if matrix[x][y] == target {
return true
}
// val > target,压缩一列。
if matrix[x][y] > target {
y--
}else { // 压缩一行
x++
}
}
return false
}
总结
1)从不利用规律的暴力法,到利用规律的二分 + 剪枝法,再到更充分利用规律进行问题转换的Z字搜索法,挖掘规律,问题/逻辑转换,不断降低时空复杂度。
2)保持有序对二分的紧密联系。
参考文献
[1] LeetCode 行列有序矩阵搜索
[2] LeetCode 官方解答 Z字搜索