一.题目描述
在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
二.解题思路
要注意数组的有序信息
1.暴力穷举:O(M*N)
2.单折半:O(M*logN):迭代每一行,每一行是有序的,二分搜索
3.左下/右上(M+N):O(M+N) 右上是该行和该列(该元素所在列以上的元素)所有元素里最大,若target比该元素大,则只能向下移动一行,小的话则遍历当前行。每次可以剔除一行。
4.双折半:最差O(M*logN)(最差情况target在矩形中心,这时候性能和单折差不多),平均可以O(logM+logN), 和单折半类似,四个点,top,bottom,left,right,稍微复杂一点。首先上边界折半,位于终止点右边的矩形框内的元素都大于该终止点,因次大于target,可以剔除。然后下边界折半,相似的道理可以剔除左边的矩形框,然后左边界,右边界。有很多细节要考察的,特别要注意top和left更新过程中,不能出现小于原来值的情况。具体看注释吧。
5.十字分割法:将二维数组分成四个矩形,选中间的交叉点,左上矩形和右下矩形必然冲突,每次能剔除1/4的元素,然后对剩下三个区域递归,直到只剩一个元素。
三.源码
# 暴力
class Solution:
# array 二维列表
def Find(self, target, array):
# write code here
for arr in array:
if target in arr:return True
return False
#二分
class Solution:
# array 二维列表
def Find(self, target, array):
# write code here
for arr in array:
low,high=0,len(arr)-1
mid=(low+high)/2
while low<=high:
if target<arr[mid]:
high=mid-1
elif target>arr[mid]:
low=mid+1
else:return True
mid=(low+high)/2
return False
#左下/右上+折半
class Solution:
# array 二维列表
def Find(self, target, array):
# write code here
if not array[0]:return False
m=len(array[0])-1
for arr in array:
if target>arr[m]:continue
low,high=0,len(arr)-1
mid=(low+high)/2
while low<=high:
if target==arr[mid]:
return True
elif target>arr[mid]:
low=mid+1
else:high=mid-1
mid=(low+high)/2
return False
# 双折
class Solution:
# array 二维列表
def Find(self, target, array):
# write code here
if not array[0]:return False
top,bottom,left,right=0,len(array)-1,0,len(array[0])-1
cur_top,cur_bottom,cur_left,cur_right=top,bottom,left,right
while left<right or top<bottom:
cur_left,cur_right=left,right
# bin search top
while cur_left<=cur_right:
mid=(cur_left+cur_right)/2
if target==array[top][mid]:return True
elif target<array[top][mid]:cur_right=mid-1
else:cur_left=mid+1
# delete the right rectangle
right=(cur_right+cur_left)/2
top+=1
cur_left,cur_right=left,right
#bin search bottom
while cur_left<=cur_right:
mid=(cur_left+cur_right)/2
if target==array[bottom][mid]:return True
elif target<array[bottom][mid]:cur_right=mid-1
else:cur_left=mid+1
# delete the left rectangle
# Attention:, since we use <= in while statement
# this means that cur_right may less than cur_left
# this can lead to (cur_right+cur_left)/2 less left
# if we do not add this if statement, this lead to problems
# left show never be smaller in iteration
if (cur_right+cur_left)/2>left:left=(cur_right+cur_left)/2
bottom-=1
# bin search left
cur_top,cur_bottom=top,bottom
while cur_top<=cur_bottom:
mid=(cur_top+cur_bottom)/2
if target==array[mid][left]:return True
elif target<array[mid][left]:cur_bottom=mid-1
else:cur_top=mid+1
# delete the downward rectangle
bottom=(cur_top+cur_bottom)/2
left+=1
# bin search right
cur_top,cur_bottom=top,bottom
while cur_top<=cur_bottom:
mid=(cur_top+cur_bottom)/2
if target==array[mid][right]:return True
elif target<array[mid][right]:cur_bottom=mid-1
else:cur_top=mid+1
# delete the upward rectangle
if (cur_top+cur_bottom)/2>top:top=(cur_top+cur_bottom)/2
right-=1
# Attention, in the outer while statement, we use <
# not <=, thus we need to add one more judgement as
# there may be the case that after the last modification in
# right-=1, array[top][right] is the target, but right<=left,
# then we can judge this case in inner while statement.
return True if array[top][right]==target else False