返回

利用优化策略征服 LeetCode:矩阵中战斗力最弱的 K 行

前端

前言

在LeetCode的浩瀚题库中,矩阵操作类问题占据着不可忽视的一席之地。它们往往考察我们对多维数组的理解、操作和算法设计的功底。今天,我们将深入探讨一道精彩的矩阵问题——1337. 矩阵中战斗力最弱的K行。

问题

给你一个大小为m x n的二进制矩阵mat,其中1表示士兵,0表示空位。每个士兵都有一个战斗力,战斗力等于他所在行中士兵的数量。请你找出矩阵中战斗力最弱的K行,并返回它们的行列号。

示例

输入:mat =
[[1,1,0,0,0],
 [1,1,1,1,0],
 [1,0,0,0,0],
 [1,1,0,0,0],
 [1,1,1,1,1]]
K = 3
输出:[[2, 4], [3, 2], [4, 5]]
解释:
行 2 的战斗力为 4 (行中 1 的数量)
行 3 的战斗力为 2
行 4 的战斗力为 5
行 5 的战斗力为 5
因此,前三战斗力最弱的行是 [2,4][3,2][4,5]

思路分析

乍看之下,这个问题似乎需要对每个行进行统计,再对其战斗力排序。然而,我们可以采用更加巧妙的策略,避免不必要的重复计算。

我们注意到,士兵的战斗力等于他所在行中1的数量。因此,我们可以先对矩阵中的行进行排序,根据士兵数量的升序排列。排序后,前K行就是战斗力最弱的K行。

算法步骤

  1. 将矩阵中每一行的士兵数量作为权重,对行进行排序。
  2. 返回排序后前K行。

代码实现

def kWeakestRows(mat: List[List[int]], k: int) -> List[List[int]]:
    # 计算每一行的战斗力
    战斗力 = [sum(row) for row in mat]
    
    # 对战斗力进行升序排序
    sorted_战斗力, sorted_index = zip(*sorted(zip(战斗力, range(len(战斗力))), key=lambda x: x[0]))
    
    # 返回战斗力最弱的K行
    return [[i, sorted_战斗力[i]] for i in sorted_index[:k]]

优化策略

上述算法的时间复杂度为O(mn log m),其中m和n分别是矩阵的行数和列数。我们可以通过二分查找进一步优化算法的时间复杂度。

二分查找优化

  1. 计算矩阵中所有士兵数量的总和。
  2. 使用二分查找,找到战斗力为总和的k/2的行。
  3. 在战斗力为k/2的行之前和之后各搜索k/4的行。
  4. 重复步骤3,直到找到战斗力最弱的K行。

优化后的代码

def kWeakestRows(mat: List[List[int]], k: int) -> List[List[int]]:
    m, n = len(mat), len(mat[0])
    total = sum(sum(row) for row in mat)
    
    left, right = 0, total
    while left < right:
        mid = (left + right) // 2
        cnt = 0
        for row in mat:
            cnt += bisect.bisect_left(row, mid)
        if cnt < k:
            left = mid + 1
        else:
            right = mid
    
    战斗力 = []
    for i, row in enumerate(mat):
        战斗力.append((sum(row), i))
    战斗力.sort()
    return [战斗力[i][1] for i in range(k)]

时间复杂度分析

二分查找优化后的时间复杂度为O(m log n log m),比原始算法快得多,尤其是在矩阵列数n较大的情况下。

结语

通过巧妙地运用排序和二分查找技巧,我们成功征服了 LeetCode 1337. 矩阵中战斗力最弱的 K 行。这道题不仅考察了我们对矩阵操作的理解,还锻炼了我们优化算法的时间复杂度的能力。希望这篇文章能为你带来启发,助你轻松应对类似的编程挑战!