leetcode - 240.Search a 2D Matrix II

算法系列 — 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)

这个算法虽然没有第一个算法那么简洁并且高效,比第二个算法看上去也复杂了一点,
在这个问题上也没有收获到很大的成果,但是在这个过程中对分治的思想训练很有帮助
作为刚上路的菜鸟,慢慢学吧,总会好的

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值