算法系列 — leetcode
题目描述:
Write an efficient algorithm that searches for a value in an m x n matrix. This matrix has the following properties:
- Integers in each row are sorted in ascending from left to right.
- Integers in each column are sorted in ascending from top to bottom.
If there is an element equal to the value, return True; otherwise return False.
最最理想的解法,复杂度为O(m+n),不过我并没有想到
直接从矩阵的右上角开始搜索, 用mark表示搜索位的所有信息
如果target = mark,找到返回True
如果target > mark,则向下搜索
如果target < mark,则向左搜索
直至出界,则说明没有这个元素,返回False
矩阵每行都是从左至右升序,每列都是从上到下升序,如果target在矩阵中某个位置
初始mark在整个矩阵的右上角,所以target在其左下方
每次搜索只移动一步,移动的规则也总是能够保证移动之后target仍然是在mark左下方
那么在经过有限步移动之后target必然和mark重合
mark向下最多移动m步,向左最多移动n步,因而时间复杂度为O(m+n)
def searchMatrix(matrix, target):
i = 0
j = len(matrix[0])
while i < len(matrix) and j >= 0:
if matrix[i][j] == target:
return True
if matrix[i][j] < target:
i++
else :
j--
return False
无论怎样看,这个解法都非常精简优雅,不过,我最初的目的是要练习分治算法,似乎这个解法里面并没有分治的思想
暴力枚举配合二分法
由于每行和每列都是有序的,很容易就能想到对每行(或每列)进行二分搜索
不难分析算法时间复杂度为O(N*logM)或者O(M*logN)
def searchMatrix(matrix, target):
cols = len(matrix[0])
for x in range(0, len(matrix)):
if matrix[x][0] <= target && matrix[x][cols-1] >= target && binarySearch(matrix[x], target):
return True
return False
def binarySearch(li, target):
start = 0
end = len(li) - 1
while(end > start):
mid = int((start + end) / 2)
if target == li[mid]:
return True
elif target > li[mid]:
start = mid + 1
else :
end = mid - 1
return li[end] == target
这个算法看起来就既没有技术含量也没有上面的那个算法优雅了,不过至少不是O(M*N)
分治思想配合二分法
由于给定的矩阵比较特殊,每行每列均是升序的,因而可以缩小矩阵规模以分治
最简单的就是将矩阵二等分或者四等分,但是矩阵各个部分都是不同的,因此很难通过减少重复动作来降低复杂度
只能通过剪枝来减小递归系数,而等分矩阵很难进行剪枝
从剪枝这个角度入手,自然是想将矩阵中不可能存在target的部分给抛弃掉
假设matrix[i][j] < target,那么它左上方的子矩阵便可全部抛弃
如果还有matrix[i+1][j+1] > target, 那么此元素右下方的子矩阵也可全部抛弃
因而只需要对matrix[i][j]左下方和右上方的两个子矩阵再进行查找,然后将两个结果归并起来即可
注意到对角线(i=j,在矩阵中不是很严格)上的元素也是升序的,因而可以通过二分查找来确定这个划分矩阵的关键元素
此处的复杂度分析涉及到两个变量,且关键元素位置不是确定的,因而比较困难,我再研究研究
不过估算应该是介于O(M*logN)和O(M+N)之间的; 边界条件不再累述,代码之中会有所反映
def _searchMatrix(matrix, target):
return searchMatrix(matrix, target, 0, 0, len(matrix)-1, len(matrix[0])-1)
def searchMatrix(matrix, target, si, sj, ei, ej):
if si > ei or sj > ej or target < matrix[si][sj]:
return False
if si == ei:
for j in range(sj, ej+1):
if target == matrix[si][j]:
return True
return False
elif sj == ej:
for i in range(si, ei+1):
if target == matrix[i][sj]:
return True
return False
start = 0
end = min((ei - si), (ej - sj))
while end > start + 1:
mid = int((start + end) / 2)
if target > matrix[si + mid][sj + mid]:
start = mid
elif target < matrix[si + mid][sj + mid]:
end = mid
else :
return True
if target > matrix[si+start+1][sj+start+1]:
start += 1
elif target == matrix[si+start+1][sj+start+1] \
or target == matrix[si+start][sj+start]:
return True
return searchMatrix(matrix, target, si+start+1, sj, ei, sj+start) or searchMatrix(matrix, target, si, sj+start+1, si+start, ej)
这个算法虽然没有第一个算法那么简洁并且高效,比第二个算法看上去也复杂了一点,
在这个问题上也没有收获到很大的成果,但是在这个过程中对分治的思想训练很有帮助
作为刚上路的菜鸟,慢慢学吧,总会好的