剑指offer—二位数组中的查找
问题分析
这一类题目的要求在给定的二维数组中判断给定的数是否存在,不存在就返回False。这些二维数组有一定的规律性,比如每一行和每一列都是递增的等等,所以在遇到二维数组或多维数组时,先看看数组是否具有一定的规律性。
下面,介绍几种比较常用的方法。
首先,最直接的方法,就是暴力求解,用这种方法最后的时间复杂度为O(MN)(M为行数,N为列数),效率是最慢的,所以这里不推荐。
其次,还有一种使用二分法来解题的,比如像下面的二维数组:
它要求每一行从左向右递增,而且每行的第一个元素大于上一行最后一个元素。这样的二维数组可以直接“展开”一维数组,如下:
这样的话,就相当于在一个一维数组中判断元素是否存在了,这里用二分法求解,只不过每次得将一维的坐标转换成二维的坐标。该方法的时间复杂度为O(log(MN))。
最后,介绍一种线性时间的方法(这种方法的适用范围要比第二种方法广),也是书上提供的方法,就是根据要找的数与给定位置数的相对大小,来进一步缩小数组的范围。
用上面的数组作为例子,该数组的每一行从左向右递增,每一列从上到下也是递增的,和第一个例子有些不同。
假设需要寻找的数是7,而我们最先从右上角的9开始寻找。如果给定的数小于7,那么根据数组的特点,比9小的数只会出现在当前列前面的列中;同样,如果要找的数大于9,那么只会出现在当前行的下面的行中。这样一来,就缩小了数组的尺寸,在新的数组中还是从最右上角的位置开始找起。
这一题,由于7<9,所以,下一步需要的找的点为9(第二行,第三列),由于7<9,所以仍是在左边查找,下一个位置为4:
而7>4,所以接下来从下面开始找:
这时候,正好找到的数与目标数匹配,所以返回True。如果一直没有找到,那么数组就会一直找到第0列或第M行,这是不允许的,直接返回False。
这种方法时间复杂度为O(M+N)。但是要注意,开始的点不能随便乱选,在这里我选的是最右上角,也可以选择左下角。选择右上角是因为,小于该位置的数全部在其左边,而大于此数的数全部在其下面,这个特别的地方,决定了它可以作为开始的位置,同样,左下角也可以。
但如果左上角,那么它的右边和下面的数全部大于它,所以就没有区分度,不便于缩小查找的范围,右下角也是如此。
本题的思想就是减小查找的范围,所以无论是多么复杂的结构,只要能够找到某种手段,能够将问题的范围减小,那么做起来就方便很多。
源码
class Solution(object):
def searchMatrix(self, matrix, target):
"""
:type matrix: List[List[int]]
:type target: int
:rtype: bool
"""
# 二分法
# if matrix:
# m = len(matrix)
# n = len(matrix[0])
# print(m, n)
# left = 0
# right = m * n - 1
# while left <= right:
# print(left, right)
# mid = (left + right) // 2
# print(mid // n)
# if matrix[mid // n][mid % n] < target:
# left = mid + 1
# elif matrix[mid//n][mid%n] > target:
# right = mid - 1
# else:
# return True
# return False
# 根据数组的特点
if matrix:
m = len(matrix)
n = len(matrix[0])
col = 0
row = n-1
while col < m and row >= 0:
if matrix[col][row] == target:
return True
elif matrix[col][row] > target:
row -= 1
else:
col += 1
return False
由于剑指offer上面的很多题目都能在leetcode上找到原型,所以这里代码用的例子也是leetcode里的,下面是原题,大家可以使用这里的题目来练习。
leetcode第74题. 搜索二维矩阵
第240题.搜索二维矩阵 II