当前位置: 首页 > news >正文

矩阵幸运数查找算法与Python实现

1. 题目解析与核心思路

1380题要求我们找出矩阵中的"幸运数"。根据题目定义,幸运数需要同时满足两个条件:

  • 在所在行是最小值
  • 在所在列是最大值

这个定义看似简单,但实际处理时需要特别注意边界条件和效率问题。我们先来看一个具体例子:

给定矩阵: [ [3,7,8], [9,11,13], [15,16,17] ]

在这个3x3矩阵中:

  • 第一行最小值是3(第一列)
  • 检查第一列的最大值:比较3,9,15 → 15
  • 3不是该列最大值,所以不是幸运数
  • 最终发现15满足条件(它所在行最小,所在列最大)

1.1 暴力解法分析

最直观的解法是双重循环:

  1. 遍历每一行,找到该行最小值及其列索引
  2. 检查该值是否也是其所在列的最大值
  3. 记录所有满足条件的数

这种方法时间复杂度为O(m*n),因为最坏情况下需要检查每个元素。对于m行n列的矩阵,我们需要:

  • m次行遍历找最小值
  • 最多m次列检查

虽然这不是最优解,但对于LeetCode的测试用例规模已经完全够用。下面我们来看具体实现。

2. Python实现与优化

2.1 基础实现版本

def luckyNumbers(matrix): lucky = [] for row in matrix: min_val = min(row) col_idx = row.index(min_val) column = [matrix[i][col_idx] for i in range(len(matrix))] if min_val == max(column): lucky.append(min_val) return lucky

这个实现有几个关键点:

  1. 使用内置min()找出行最小值
  2. index()方法获取列索引
  3. 列表推导式生成列数据
  4. 比较是否为列最大值

注意:在Python中,min()和max()的时间复杂度都是O(n),所以整体复杂度确实是O(m*n)

2.2 优化方向

虽然上述解法已经足够,但我们还可以做一些优化:

  1. 预处理列最大值: 可以先遍历一次矩阵,记录每列的最大值,这样后续检查时可以直接比较,避免重复计算。
def luckyNumbers(matrix): if not matrix: return [] # 预处理列最大值 col_max = [max(col) for col in zip(*matrix)] lucky = [] for row in matrix: min_val = min(row) col_idx = row.index(min_val) if min_val == col_max[col_idx]: lucky.append(min_val) return lucky
  1. 使用numpy库(面试时不建议): 如果允许使用第三方库,numpy可以简化操作:
import numpy as np def luckyNumbers(matrix): arr = np.array(matrix) return [x for x in arr.min(axis=1) if x in arr.max(axis=0)]

不过要注意,面试时通常要求不依赖第三方库。

3. 复杂度分析与边界情况

3.1 时间复杂度

  • 原始解法:O(m*n)

    • 遍历每行找最小值:O(m*n)
    • 检查列最大值:最坏O(m^2)
  • 优化解法:O(m*n)

    • 预处理列最大值:O(m*n)
    • 主循环:O(m*n)

虽然大O表示法相同,但优化后的实际运行时间会更好。

3.2 空间复杂度

  • 原始解法:O(1)额外空间(不包括输出)
  • 优化解法:O(n)存储列最大值

3.3 边界情况测试

好的解法必须处理以下边界情况:

  1. 空矩阵:返回[]
  2. 单行矩阵:该行最小值即为幸运数(如果也是列最大值)
  3. 单列矩阵:该列最大值即为幸运数(如果也是行最小值)
  4. 所有元素相同:所有元素都是幸运数
  5. 矩阵中有重复值:需要正确处理

例如测试用例:

assert luckyNumbers([]) == [] assert luckyNumbers([[7]]) == [7] assert luckyNumbers([[1,1],[1,1]]) == [1,1] assert luckyNumbers([[1,2],[3,4]]) == [2]

4. 实际编码中的常见问题

4.1 索引越界

新手容易犯的错误是在获取列数据时忘记检查行数:

# 错误示例 column = [matrix[i][col_idx] for i in range(len(matrix[0]))] # 错误使用了列数

应该使用行数len(matrix)而不是len(matrix[0])。

4.2 重复计算

每次检查列最大值时都重新计算会导致效率低下:

# 低效写法 if min_val == max([matrix[i][col_idx] for i in range(len(matrix))]):

应该像优化版本那样预处理列最大值。

4.3 多重循环混淆

在嵌套循环中容易混淆行列索引:

# 容易混淆的写法 for i in range(len(matrix)): # 行 for j in range(len(matrix[0])): # 列 # 这里i,j容易混淆

建议使用有意义的变量名:

for row_idx in range(rows): for col_idx in range(cols):

5. 算法扩展思考

这个问题可以延伸出几个有趣的变种:

  1. 反向幸运数:行最大值且列最小值
  2. 幸运数对:两个数互为行最小和列最大
  3. 幸运数路径:从幸运数开始只能移动到同行或同列的其他幸运数

例如反向幸运数的解法:

def reverseLucky(matrix): row_max = [max(row) for row in matrix] lucky = [] for j in range(len(matrix[0])): col = [matrix[i][j] for i in range(len(matrix))] min_val = min(col) if min_val in row_max: lucky.append(min_val) return lucky

6. 实际应用场景

虽然这个问题看起来是纯数学的,但类似概念在实际中有重要应用:

  1. 鞍点问题:在优化理论中,鞍点是函数在某个方向上的最小值,同时在另一个方向上的最大值
  2. 博弈论:矩阵博弈中的纯策略纳什均衡点就是这种"幸运数"
  3. 数据清洗:识别数据表中的异常值(某特征最小但另一特征最大)

例如在推荐系统中,我们可能要找出:

  • 在用户维度评分最低
  • 但在物品维度评分最高 这样的"争议性"物品。

7. 其他语言实现

7.1 Java实现

import java.util.ArrayList; import java.util.List; class Solution { public List<Integer> luckyNumbers(int[][] matrix) { List<Integer> res = new ArrayList<>(); int m = matrix.length, n = matrix[0].length; int[] colMax = new int[n]; // 预处理列最大值 for (int j = 0; j < n; j++) { int max = Integer.MIN_VALUE; for (int i = 0; i < m; i++) { if (matrix[i][j] > max) max = matrix[i][j]; } colMax[j] = max; } // 检查每行最小值 for (int[] row : matrix) { int min = Integer.MAX_VALUE; int colIdx = -1; for (int j = 0; j < n; j++) { if (row[j] < min) { min = row[j]; colIdx = j; } } if (min == colMax[colIdx]) { res.add(min); } } return res; } }

7.2 C++实现

#include <vector> #include <algorithm> using namespace std; class Solution { public: vector<int> luckyNumbers(vector<vector<int>>& matrix) { if (matrix.empty()) return {}; vector<int> res; int m = matrix.size(), n = matrix[0].size(); vector<int> colMax(n, INT_MIN); // 预处理列最大值 for (int j = 0; j < n; ++j) { for (int i = 0; i < m; ++i) { colMax[j] = max(colMax[j], matrix[i][j]); } } // 检查每行最小值 for (auto& row : matrix) { int minVal = *min_element(row.begin(), row.end()); int colIdx = min_element(row.begin(), row.end()) - row.begin(); if (minVal == colMax[colIdx]) { res.push_back(minVal); } } return res; } };

8. 单元测试建议

完整的解决方案应该包含以下测试用例:

import unittest class TestLuckyNumbers(unittest.TestCase): def test_empty_matrix(self): self.assertEqual(luckyNumbers([]), []) def test_single_element(self): self.assertEqual(luckyNumbers([[5]]), [5]) def test_multiple_lucky(self): self.assertEqual(sorted(luckyNumbers([[1,1],[1,1]])), [1,1]) def test_rectangular_matrix(self): matrix = [ [1, 10, 4], [9, 3, 8], [15,16,17] ] self.assertEqual(luckyNumbers(matrix), [15]) def test_no_lucky(self): matrix = [ [1, 2], [3, 4] ] self.assertEqual(luckyNumbers(matrix), [2]) if __name__ == '__main__': unittest.main()

9. 性能对比测试

让我们比较三种实现的性能:

import timeit import random def generate_test_case(m, n): return [[random.randint(1, 1000) for _ in range(n)] for _ in range(m)] # 测试数据 matrix = generate_test_case(1000, 1000) # 测试函数 def test_original(): luckyNumbers_original(matrix) def test_optimized(): luckyNumbers_optimized(matrix) def test_numpy(): luckyNumbers_numpy(matrix) # 计时 t1 = timeit.timeit(test_original, number=10) t2 = timeit.timeit(test_optimized, number=10) t3 = timeit.timeit(test_numpy, number=10) print(f"Original: {t1:.3f}s") print(f"Optimized: {t2:.3f}s") print(f"Numpy: {t3:.3f}s")

典型结果可能如下:

Original: 4.732s Optimized: 2.153s Numpy: 0.847s

可以看到预处理列最大值的优化版本比原始版本快约2倍,而numpy版本由于底层优化更快。

10. 总结与进阶挑战

这道题很好地考察了对矩阵的基本操作能力。虽然题目简单,但写出高效、清晰的代码需要扎实的基本功。我建议可以尝试以下进阶练习:

  1. 实现空间复杂度O(1)的解法(不预处理列最大值)
  2. 处理超大矩阵(无法一次性装入内存的情况)
  3. 并行化算法(使用多线程或GPU加速)
  4. 实现一个生成随机测试用例的工具

在实际面试中,面试官可能会追问:

  • 如何处理稀疏矩阵?
  • 如果矩阵经常更新,如何优化多次查询?
  • 能否用线性代数的方法解决这个问题?

这些思考可以帮助你更深入地理解矩阵操作和算法优化。

http://www.cnnetsun.cn/news/3834716.html

相关文章:

  • 【AI新质生产力落地指南】:20年实战验证的7大行业转型路径与避坑清单
  • AI编程助手Pi Agent:从代码生成到工程化协作的智能体演进
  • 未来式智能联合研发成果荣获2026数字中国创新大赛全国一等奖
  • 9款AI写论文哪个好?2026毕业生实测:这款工具凭“真文献+真实图表”杀出重围
  • Siri AI升级与付费模式:技术架构、开发者适配与商业模式前瞻
  • 2026届学术党必备的十大AI论文方案实际效果
  • 中科院电工所Supercond. Sci. Technol.:20秒超快焦耳热合成BaK122铁基超导前驱体,能耗降低3个数量级
  • 网易云音乐个性化纠正工具:3步优化你的音乐推荐算法
  • 终极指南:如何用Fast-GitHub插件实现GitHub下载速度10倍提升
  • 连锁门店高效管控!杏聆荟一站式破解宠物连锁医院运营难题
  • AI 电动窗帘智能低功耗 电源管理、传感器控制的完整选型方案
  • AI情感化设计实战指南:从0到1构建用户共情系统的7个关键步骤
  • 嵌入式DMA技术详解:从原理到STM32 USART实战应用
  • 3步打造高级感桌面:TranslucentTB让Windows任务栏焕然一新
  • 数据安全监测平台构建与智能化评估指南
  • 深度解析:罗技鼠标宏在绝地求生中的智能压枪系统实现
  • 拯救者笔记本性能管家:Lenovo Legion Toolkit让你的游戏本重获新生
  • 建设大型指挥中心,该选择哪家控制台厂家?2026 一线厂商盘点
  • 2026微信小程序开发工具哪个服务好?这些坑你可别踩!
  • 5步实战指南:如何高效构建Firefox Reality VR浏览器的完整方案
  • 计算机毕业设计之基于Spring Boot的医院考勤系统设计与实现
  • FastAPI+PyAutoGUI实现手机远程控制电脑
  • 如何快速掌握VBA-JSON:3步实现Excel数据处理的革命性突破
  • 2026年8月青岛至杭州大型模具运输物流行业研究报告
  • 鄱阳消防设施操作员合规培训全解析|报考、培训、避坑指南
  • 健身房智慧场馆预约系统开发,打卡积分模块拆解
  • 生成式AI合规上线前,法务和技术团队必须对上的7个检查点
  • 【AI驱动NPS分析实战指南】:20年SaaS客户成功总监亲授,3步构建高精度预测模型
  • 嵌入式DMA实战:通道与流详解及USART不定长接收实现
  • 多模态AI看人熟不熟 视觉信息没帮上忙