*python3__leecode/0240. 搜索二维矩阵 II

一、刷题内容

原题链接

https://leetcode-cn.com/problems/search-a-2d-matrix-ii/

内容描述

编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target 。该矩阵具有以下特性:

每行的元素从左到右升序排列。
每列的元素从上到下升序排列。

示例 1:
在这里插入图片描述
输入:matrix = [[1,4,7,11,15],[2,5,8,12,19],[3,6,9,16,22],[10,13,14,17,24],[18,21,23,26,30]], target = 5
输出:true

示例 2:
在这里插入图片描述
输入:matrix = [[1,4,7,11,15],[2,5,8,12,19],[3,6,9,16,22],[10,13,14,17,24],[18,21,23,26,30]], target = 20
输出:false

提示:
m == matrix.length
n == matrix[i].length
1 <= n, m <= 300
-10^9 <= matix[i][j] <= 10^9
每行的所有元素从左到右升序排列
每列的所有元素从上到下升序排列
-10^9 <= target <= 10^9

二、解题方法

1.方法一:暴力法

对于每一行我们可以像搜索未排序的一维数组——通过检查每个元素来判断是否有目标值。

这个算法并没有做到聪明的事情。我们循环数组,依次检查每个元素。如果,我们找到了,我们返回 true。否则,对于搜索到末尾都没有返回的循环,我们返回 false。此算法在所有情况下都是正确的答案,因为我们耗尽了整个搜索空间。

class Solution:
    def searchMatrix(self, matrix, target):
        for row in matrix:
            if target in row:
                return True
        
        return False

时间复杂度:O(mn)。因为我们在 n×m 矩阵上操作,总的时间复杂度为矩阵的大小
空间复杂度:O(1),暴力法分配的额外空间不超过少量指针,因此内存占用是恒定的。

2.方法二:二分法搜索

矩阵已经排过序,就需要使用二分法搜索以加快我们的算法。

算法:
首先,我们确保矩阵不为空。那么,如果我们迭代矩阵对角线,从当前元素对列和行搜索,我们可以保持从当前 (row,col) 对开始的行和列为已排序。 因此,我们总是可以二分搜索这些行和列切片。我们以如下逻辑的方式进行 : 在对角线上迭代,二分搜索行和列,直到对角线的迭代元素用完为止(意味着我们可以返回 false )或者找到目标(意味着我们可以返回 true )。binary search 函数的工作原理和普通的二分搜索一样,但需要同时搜索二维数组的行和列。

class Solution:
    def binary_search(self, matrix, target, start, vertical):
        lo = start
        hi = len(matrix[0])-1 if vertical else len(matrix)-1

        while hi >= lo:
            mid = (lo + hi)//2
            if vertical: # searching a column
                if matrix[start][mid] < target:
                    lo = mid + 1
                elif matrix[start][mid] > target:
                    hi = mid - 1
                else:
                    return True
            else: # searching a row
                if matrix[mid][start] < target:
                    lo = mid + 1
                elif matrix[mid][start] > target:
                    hi = mid - 1
                else:
                    return True
        
        return False

    def searchMatrix(self, matrix, target):
        # an empty matrix obviously does not contain `target`
        if not matrix:
            return False

        # iterate over matrix diagonals starting in bottom left.
        for i in range(min(len(matrix), len(matrix[0]))):
            vertical_found = self.binary_search(matrix, target, i, True)
            horizontal_found = self.binary_search(matrix, target, i, False)
            if vertical_found or horizontal_found:
                return True
        
        return False

3.方法三:搜索空间的缩减

我们可以将已排序的二维矩阵划分为四个子矩阵,其中两个可能包含目标,其中两个肯定不包含。

算法:
由于该算法是递归操作的,因此可以通过它的基本情况和递归情况的正确性来判断它的正确性。

基本情况 :
对于已排序的二维数组,有两种方法可以确定一个任意元素目标是否可以用常数时间判断。第一,如果数组的区域为零,则它不包含元素,因此不能包含目标。其次,如果目标小于数组的最小值或大于数组的最大值,那么矩阵肯定不包含目标值。

class Solution:
    def searchMatrix(self, matrix, target):
        # an empty matrix obviously does not contain `target`
        if not matrix:
            return False

        def search_rec(left, up, right, down):
            # this submatrix has no height or no width.
            if left > right or up > down:
                return False
            # `target` is already larger than the largest element or smaller
            # than the smallest element in this submatrix.
            elif target < matrix[up][left] or target > matrix[down][right]:
                return False

            mid = left + (right-left)//2

            # Locate `row` such that matrix[row-1][mid] < target < matrix[row][mid]
            row = up
            while row <= down and matrix[row][mid] <= target:
                if matrix[row][mid] == target:
                    return True
                row += 1
            
            return search_rec(left, row, mid-1, down) or search_rec(mid+1, up, right, row-1)

        return search_rec(0, 0, len(matrix[0])-1, len(matrix)-1)

4.方法四:

因为矩阵的行和列是排序的(分别从左到右和从上到下),所以在查看任何特定值时,我们可以修剪O(m)O(m)或O(n)O(n)元素。

算法:
首先,我们初始化一个指向矩阵左下角的 (row,col) 指针。然后,直到找到目标并返回 true(或者指针指向矩阵维度之外的 (row,col) 为止,我们执行以下操作:如果当前指向的值大于目标值,则可以 “向上” 移动一行。 否则,如果当前指向的值小于目标值,则可以移动一列。不难理解为什么这样做永远不会删减正确的答案;因为行是从左到右排序的,所以我们知道当前值右侧的每个值都较大。 因此,如果当前值已经大于目标值,我们知道它右边的每个值会比较大。也可以对列进行非常类似的论证,因此这种搜索方式将始终在矩阵中找到目标(如果存在)。

在下面的动画中查看算法的一些示例运行:

class Solution:
    def searchMatrix(self, matrix, target):
        # an empty matrix obviously does not contain `target` (make this check
        # because we want to cache `width` for efficiency's sake)
        if len(matrix) == 0 or len(matrix[0]) == 0:
            return False

        # cache these, as they won't change.
        height = len(matrix)
        width = len(matrix[0])

        # start our "pointer" in the bottom-left
        row = height-1
        col = 0

        while col < width and row >= 0:
            if matrix[row][col] > target:
                row -= 1
            elif matrix[row][col] < target:
                col += 1
            else: # found it
                return True
        
        return False

时间复杂度:O(n+m)。
时间复杂度分析的关键是注意到在每次迭代(我们不返回 true)时,行或列都会精确地递减/递增一次。由于行只能减少 m 次,而列只能增加 n 次,因此在导致 while 循环终止之前,循环不能运行超过 n+m 次。因为所有其他的工作都是常数,所以总的时间复杂度在矩阵维数之和中是线性的。
空间复杂度:O(1),因为这种方法只处理几个指针,所以它的内存占用是恒定的。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
这段代码中使用了 `np.arange()` 函数生成一个数组,该函数的作用是返回一个有规律的一维数组。具体来说,`np.arange(start, stop, step)` 可以生成一个从 `start` 开始,以 `step` 为步长,不包括 `stop` 的一维数组。例如,`np.arange(0, np.pi/2, 0.1)` 会生成一个从 0 开始,以 0.1 为步长,不包括 $\pi/2$ 的一维数组。 接着,代码中用到了 `x_pop.dot()` 函数,该函数用于计算矩阵的点积(即矩阵乘法)。注意,这里的 `x_pop` 是一个二维矩阵,而 `2**np.arange(gene_SIZE*n*A_W_P/3)[::-1]` 则是一个一维数组。这里使用了 `[::-1]` 的切片方法对一维数组进行了翻转,即倒序排列。这一步的作用是,将一维数组从低位到高位按位赋值给二维矩阵中每个元素的二进制表示的最低位开始的连续段,从而实现一个十进制数到二进制数的转换。 最后,将矩阵点积的结果赋值给 `x`。该操作可以理解为用一维数组的二进制表示的最低位开始的连续段组成的整数,替换了原来一维数组的每个元素,并重新构成一个新的一维数组。 范例: ```python import numpy as np # 生成一个从0开始,以0.1为步长,不包括π/2的一维数组 x = np.arange(0, np.pi/2, 0.1) # 计算y的值 y = np.sin(x) # 定义一个函数F def F(x, y): return 21.5 + x * np.sin(4 * np.pi * x) + y * np.sin(20 * np.pi * y) # 定义 x_pop 和 gene_SIZE 等变量 x_pop = np.random.randint(0, 2, size=(4, 6)) gene_SIZE = 22 n = 3 A_W_P = 10 # 计算 x x = x_pop.dot(2**np.arange(gene_SIZE*n*A_W_P//3)[::-1]) print(x) ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

百里 Jess

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值