Python算法题集_矩阵置零
- 题73:矩阵置零
- 1. 示例说明
- 2. 题目解析
- - 题意分解
- - 优化思路
- - 测量工具
 
- 3. 代码展开
- 1) 标准求解【三层循环】
- 2) 改进版一【纵横计数器】
- 3) 改进版二【原地算法】
 
- 4. 最优算法
本文为Python算法题集之一的代码示例
题73:矩阵置零
1. 示例说明
 给定一个 m x n 的矩阵,如果一个元素为 0 ,则将其所在行和列的所有元素都设为 0 。请使用 原地 算法**。**
示例 1:

输入:matrix = [[1,1,1],[1,0,1],[1,1,1]]
输出:[[1,0,1],[0,0,0],[1,0,1]]
示例 2:

输入:matrix = [[0,1,2,0],[3,4,5,2],[1,3,1,5]]
输出:[[0,0,0,0],[0,4,5,0],[0,3,1,0]]
提示:
- m == matrix.length
- n == matrix[0].length
- 1 <= m, n <= 200
- -231 <= matrix[i][j] <= 231 - 1
进阶:
- 一个直观的解决方案是使用 O(m*n)的额外空间,但这并不是一个好的解决方案。
- 一个简单的改进方案是使用 O(m+n)的额外空间,但这仍然不是最好的解决方案。
- 你能想出一个仅使用常量空间的解决方案吗
2. 题目解析
- 题意分解
- 原地算法是一个使用辅助的数据结构对输入进行转换的算法。它允许有少量额外的存储空间来储存辅助变量。当算法运行时,输入通常会被输出覆盖。原地算法仅通过替换或交换元素来更新输入序列。不是原地算法有时候称为非原地(not-in-place)或者不得其所(out-of-place)
- 本题为将矩阵中的零进行行列填充
- 本题的主要计算有2处,1是元素遍历,2是行列填充
- 基本的解法是三层循环,读取到任何一个元素为零均进行一次行填充、一次列填充,所以基本的时间算法复杂度为O(n^3)
- 优化思路
-  通常优化:减少循环层次 
-  通常优化:增加分支,减少计算集数量 
-  通常优化:采用内置算法来提升计算速度 
-  分析题目特点,分析最优解 -  必须对行列进行全扫描以确定所有0,任何一个行/列只要出现一个0就不需要再扫,可以用调度用的数据结构优化 
-  调度用的数据可以存在输入的矩阵中,实现原地算法【空间复杂度O(1)】 
 
-  
- 测量工具
- 本地化测试说明:LeetCode网站测试运行时数据波动很大,因此需要本地化测试解决这个问题
- CheckFuncPerf(本地化函数用时和内存占用测试模块)已上传到CSDN,地址:Python算法题集_检测函数用时和内存占用的模块
- 本题很难超时,本地化超时测试用例自己生成,详见【最优算法章节】
3. 代码展开
1) 标准求解【三层循环】
三层循环,超过22%
丧心病狂的三层循环,可谓可算尽算,不漏过任何角落,依旧没有超时;看起来超时测试用例还是不给力
import CheckFuncPerf as cfp
def setZeroes_base(matrix):
 import copy
 matrixcopy = copy.deepcopy(matrix)
 ilenrow, ilencol = len(matrix), len(matrix[0])
 for iIdx in range(ilenrow):
     for jIdx in range(ilencol):
         if matrixcopy[iIdx][jIdx] == 0:
             for kIdx in range(ilenrow):
                 matrix[kIdx][jIdx] = 0
             for kIdx in range(ilencol):
                 matrix[iIdx][kIdx] = 0
import random
matrix = []
for iIdx in range(1000):
 matrix.append([random.randint(0,1) for x in range(1000)])
result = cfp.getTimeMemoryStr(setZeroes_base, matrix)
print(result['msg'], '执行结果 = {}'.format(result['result']))
# 运行结果
函数 setZeroes_base 的运行时间为 62147.93 ms;内存使用量为 336.00 KB 执行结果 = None
2) 改进版一【纵横计数器】
一个横向数组、一个纵向数组,检测需要置零的行列,算法相当于O(n^2) 君临天下,九九归一【超越99%】
import CheckFuncPerf as cfp
def setZeroes_ext1(matrix):
 ilenrow, ilencol = len(matrix), len(matrix[0])
 icmdrow, icmdcol = [0] * ilenrow, [0] * ilencol
 for iIdx in range(ilenrow):
     for jIdx in range(ilencol):
         if matrix[iIdx][jIdx] == 0:
             icmdrow[iIdx] = 1
             icmdcol[jIdx] = 1
 for iIdx in range(ilenrow):
     if icmdrow[iIdx] > 0:
         for jIdx in range(ilencol):
             matrix[iIdx][jIdx] = 0
 for iIdx in range(ilencol):
     if icmdcol[iIdx] > 0:
         for jIdx in range(ilenrow):
             matrix[jIdx][iIdx] = 0
import random
matrix = []
for iIdx in range(1000):
 matrix.append([random.randint(0,1) for x in range(1000)])
result = cfp.getTimeMemoryStr(setZeroes_ext1, matrix)
print(result['msg'], '执行结果 = {}'.format(result['result']))
# 运行结果
函数 setZeroes_ext1 的运行时间为 152.05 ms;内存使用量为 8.00 KB 执行结果 = None
3) 改进版二【原地算法】
在传入的矩阵中保存横向数组、纵向数组,因此空间复杂度为O(1) 表现优异,超过90%
import CheckFuncPerf as cfp
def setZeroes_ext2(matrix):
 ilenrow, ilencol = len(matrix), len(matrix[0])
 icmdrow, icmdcol = -1, -1
 bNotfind = True
 for iIdx in range(ilenrow):
     for jIdx in range(ilencol):
         if matrix[iIdx][jIdx] == 0:
             if bNotfind:
                 icmdrow = iIdx
                 icmdcol = jIdx
                 bNotfind = False
             matrix[icmdrow][jIdx] = 0
             matrix[iIdx][icmdcol] = 0
 if bNotfind:
     return
 for iIdx in range(ilenrow):
     if iIdx != icmdrow:
         if matrix[iIdx][icmdcol] == 0:
             for jIdx in range(ilencol):
                 if jIdx != icmdcol:
                     matrix[iIdx][jIdx] = 0
 for iIdx in range(ilencol):
     if iIdx != icmdcol:
         if matrix[icmdrow][iIdx] == 0:
             for jIdx in range(ilenrow):
                 if jIdx != icmdrow:
                    matrix[jIdx][iIdx] = 0
 for iIdx in range(ilenrow):
     matrix[iIdx][icmdcol] = 0
 for iIdx in range(ilencol):
     matrix[icmdrow][iIdx] = 0
import random
matrix = []
for iIdx in range(1000):
 matrix.append([random.randint(0,1) for x in range(1000)])
result = cfp.getTimeMemoryStr(setZeroes_ext2, matrix)
print(result['msg'], '执行结果 = {}'.format(result['result']))
# 运行结果
函数 setZeroes_ext2 的运行时间为 508.10 ms;内存使用量为 0.00 KB 执行结果 = None
4. 最优算法
根据本地日志分析,最优算法为第2种setZeroes_ext1 
import random
matrix = []
for iIdx in range(1000):
    matrix.append([random.randint(0,1) for x in range(1000)])
# 算法本地速度实测比较
函数 setZeroes_base 的运行时间为 62147.93 ms;内存使用量为 336.00 KB 执行结果 = None
函数 setZeroes_ext1 的运行时间为 152.05 ms;内存使用量为 8.00 KB 执行结果 = None
函数 setZeroes_ext2 的运行时间为 508.10 ms;内存使用量为 0.00 KB 执行结果 = None
一日练,一日功,一日不练十日空
may the odds be ever in your favor ~



















