劍指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