Leetcod面试经典150题刷题记录 —— 矩阵篇

news2025/2/5 19:47:53

矩阵篇

    • 1. 有效的数独
    • 2. 螺旋矩阵
      • Python
    • 3. 旋转图像
      • Python
        • 额外开辟数组空间
        • 原地置换法
    • 4. 矩阵置零
    • 5. 生命游戏
      • Python

1. 有效的数独

题目链接:有效的数独 - leetcode
题目描述:
请你判断一个 9 x 9 的数独是否有效。只需要 根据以下规则 ,验证已经填入的数字是否有效即可。
数字 1-9 在每一行只能出现一次。
数字 1-9 在每一列只能出现一次。
数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。(请参考示例图)
注意:
一个有效的数独(部分已被填充)不一定是可解的。
只需要根据以上规则,验证已经填入的数字是否有效即可。
空白格用 '.' 表示。
题目归纳:

class Solution:
    def isValidSudoku(self, board: List[List[str]]) -> bool:
        # (1) board的行数为m,列数为n,m = n = 9
        # (2) 该求解算法应只遍历board一次
        # (3) 遍历过程更新hashtable的计数,并判断是否满足有效数独
        # (4) 0<=i,j<9,故对于(i,j)单元格,该单元格所在的小九宫格,其对应行数为i//3,列数为j//3,均向下取整, 0<=i//3, j//3 <9
        # (5) 创建二维数组 rows与columns,分别记录每行、每列中,每个数字的出现次数
        # (6) 创建三维数组 subboxes,记录每个小九宫格中,每个数字的出现次数
        #     其中rows[i][index]表示数独i行j列单元格所在 行,数字index+1出现的次数
        #     其中columns[j][index]表示数独i行j列单元格所在 列,数字index+1出现的次数
        #     其中subboxes[i//3][j//3][index]表示数独i行j列单元格所在 小九宫格,数字index+1出现的次数
        #     其中 1<= index+1 <= 9
        # (7) 若board[i][j]非空,其字符串值为数字n,则将
        #     rows[i][n-1] += 1
        #     columns[j][n-1] += 1
        #     subboxes[i//3][j//3][n-1] += 1
        #     若更新后的计数 >1,则不符合有效数独条件,返回false
        #     若更新后的计数 <=1,则符合有效数独条件,返回true
        rows = [[0] * 9 for _ in range(9)] 
        columns = [[0] * 9 for _ in range(9)]
        subboxes = [[[0] * 9 for _ in range(3)] for _ in range(3)]

        for i in range(9):
            for j in range(9):
                ch = board[i][j]
                if ch != '.': # 数字
                    digit = int(ch) - 1
                    rows[i][digit] += 1
                    columns[j][digit] += 1
                    subboxes[int(i/3)][int(j/3)][digit] += 1
                    if rows[i][digit] > 1 or                       columns[j][digit] > 1 or                       subboxes[int(i/3)][int(j/3)][digit] > 1:
                        return False
        return True

这里有一个巨坑,我不踩不知道,一踩吓一跳。就是在生成rowscolumnssubboxes时,我一开始采用的方式是:

rows = [ [0]*9 ]*9
columns = [ [0]*9 ]*9
subboxes = [[[0] * 9 for _ in range(3)] for _ in range(3)]

但是这样有个问题,问大模型,大模型给的回答是,[[0]*9]*9是通过重复一个已存在的子列表来创建一个新的列表,这个新列表中有 9 个相同的子列表,每个子列表都有 9 个元素,每个元素值都是 0。这意味着这个新列表中的每个子列表都是同一个对象,修改其中一个子列表会影响其他子列表。注意这句,会影响其他子列表,所以,为稳妥起见并养成良好习惯,生成列表还是用列表推导式,而不要用这种快速写法。

2. 螺旋矩阵

题目链接:螺旋矩阵 - leetcode
题目描述:
给你一个 m 行 n 列的矩阵 matrix ,请按照 顺时针螺旋顺序 ,返回矩阵中的所有元素。
spiral1.jpg
题目归纳:

解题思路:
(1) 解法: 旋转图像 - leetcode官方题解

Python

class Solution:
    def spiralOrder(self, matrix: List[List[int]]) -> List[int]:
        # 洋葱式模拟法
        # (1)将矩阵看成若干层,输出次序:最外层->次外层->最内层
        # [[1, 1, 1, 1, 1, 1, 1],
        # [1, 2, 2, 2, 2, 2, 1],
        # [1, 2, 3, 3, 3, 2, 1],
        # [1, 2, 2, 2, 2, 2, 1],
        # [1, 1, 1, 1, 1, 1, 1]]
        # (2)对于每层,顺序又是顺时针的
        # (top, left) -> (top, right)
        #      ^              |
        #      |              v
        # (bottom, left) <- (bottom, right)
        #
        if not matrix or not matrix[0]:
            return list()
        
        rows, columns = len(matrix), len(matrix[0])
        ans = list()
        left, right, top, bottom = 0, columns-1, 0, rows-1
        while left <= right and top <= bottom: # 3*3方阵最后会出现left=right, top=bottom
            
            for column in range(left, right+1): # [left,right],从左到右
                ans.append(matrix[top][column])
            
            for row in range(top+1, bottom+1): # [top+1, bottom],从上到下
                ans.append(matrix[row][right]) 
            
            if left < right and top < bottom: # 左右未交汇,上下未交汇
            
                for column in range(right-1, left, -1): # right-1, ... , left+1,从右到左
                    ans.append(matrix[bottom][column])
            
                for row in range(bottom, top, -1): # bottom, ..., top+1,从下到上
                    ans.append(matrix[row][left])
            
            left += 1
            right -= 1
            top += 1
            bottom -= 1

        return ans

3. 旋转图像

题目链接:螺旋矩阵 - leetcode
题目描述:
给定一个 n × n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。你必须在 原地 旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要 使用另一个矩阵来旋转图像。
题目归纳:

解题思路:
(1) 解法: 旋转图像 - leetcode官方题解

Python

额外开辟数组空间
class Solution:
    def rotate(self, matrix: List[List[int]]) -> None:
        """
        Do not return anything, modify matrix in-place instead.
        """
        # (1)关键性质
        # 矩阵中的第 i 行第 j 个元素
        # 旋转后会出现在
        # 倒数第 i 列第 j 个位置,即第 j 行倒数第i列,即
        # matrix[row][col] -> matrix[col][(n-1) -row]

        n = len(matrix) # 长宽相等
        matrix_new = [ [0 for j in range(n)] for i in range(n)]

        # 开始旋转
        for i in range(n):
            for j in range(n):
                matrix_new[j][(n-1)-i] = matrix[i][j]
        
        # 不能写成引用赋值:matrix = matrix_new
        matrix[::] = matrix_new
原地置换法
class Solution:
    def rotate(self, matrix: List[List[int]]) -> None:
        """
        Do not return anything, modify matrix in-place instead.
        """

        # 解法二,原地置换法,不使用额外矩阵
        # 将矩阵按行分块,有
        #  a_0
        #  a_1
        #  ...
        #  a_n-1
        # (1)将矩阵按行对称轴进行上下反转
        #  a_n-1
        #  a_n-2
        #  ...
        #  a_0
        # (2)T转置。这就是最终答案,顺时针旋转90°
        # a_n-1_T, ..., a_1_T, a_0_T。
        # 

        #      |     |
        #  --- · --- · --- : row1
        #      |     |
        #  --- · --- · --- : row2
        #      |     |
        #    col1   col2
        # 可以发现,顺时针旋转90°,即
        # row1 --> col2
        # row2 --> col1
        # col1 --> row1
        # col2 --> row2
        
        # 那么我们做如下操作,对矩阵按行反转,就像反转字符串那样
        #      |     |
        #  --- · --- · --- : row2
        #   -  |  -  |  -             行对称轴
        #  --- · --- · --- : row1
        #      |     |
        #    col1' col2'

        # 再进行矩阵转置
        #      |     |
        #  --- · --- · --- : col2'
        #   -  |  -  |  -             行对称轴
        #  --- · --- · --- : col1'
        #      |     |
        #    row2' row1'

        # (1)上下反转
        n = len(matrix)
        top, bottom = 0, n-1
        while top < bottom:
            matrix[top][:], matrix[bottom][:] = matrix[bottom][:], matrix[top][:]
            top += 1
            bottom -= 1
        
        # (2)沿主对角线进行T转置
        for i in range(0, n):
            for j in range(0, i):
                matrix[i][j], matrix[j][i] = matrix[j][i], matrix[i][j]

4. 矩阵置零

题目链接:矩阵置零 - leetcode
题目描述:
给定一个 m x n 的矩阵,如果一个元素为 0 ,则将其所在行和列的所有元素都设为 0 。请使用 原地 算法。
题目归纳:

解题思路:
解法: 矩阵置零 - leetcode官方题解
(1) 解法1,建立两个标记数组。时间复杂度 O ( m n ) O(mn) O(mn),空间复杂度 O ( m + n ) O(m+n) O(m+n)
(2) 解法2,使用两个标记变量。时间复杂度 O ( m n ) O(mn) O(mn),空间复杂度 O ( 1 ) O(1) O(1)
(3) 解法3,使用一个标记变量。时间复杂度 O ( m n ) O(mn) O(mn),空间复杂度 O ( 1 ) O(1) O(1)。该解法较难理解,且收益不高,暂未记录到本文中。

#解法1,建立两个标记数组。
class Solution:
    def setZeroes(self, matrix: List[List[int]]) -> None:
        """
        Do not return anything, modify matrix in-place instead.
        """
        # (1)建立两个标记数组,分别是行标记数组与列标记数组
        m = len(matrix)
        n = len(matrix[0])
        row = [False for _ in range(m)]
        col = [False for _ in range(n)]

        # (2)遍历1
        for i in range(m):
            for j in range(n):
                if matrix[i][j] == 0:
                    row[i] = True
                    col[j] = True

        # (2)遍历2
        for i in range(m):
            for j in range(n):
                if row[i] or col[j]:
                    matrix[i][j] = 0
#解法2,使用两个标记变量。
class Solution:
    def setZeroes(self, matrix: List[List[int]]) -> None:
        """
        Do not return anything, modify matrix in-place instead.
        """
        # 解法2,用矩阵的第一行和第一列来替代方法1的两个标记数组
        # 但这样需要使用额外的两个标记变量,分别记录第一行和第一列是否原本就包含 0。
        
        # (1)首先预处理出两个标记变量
        # (2)使用其他行与列去处理第一行与第一列
        # (3)然后反过来使用第一行与第一列去更新其他行与列
        # (4)最后使用两个标记变量更新第一行与第一列
        # 上述过程类似于变量值交换,互相做过河的石头
        
        # (1)首先预处理出两个标记变量
        m = len(matrix)
        n = len(matrix[0])
        flag_col0 = any(matrix[i][0] == 0 for i in range(m))
        flag_row0 = any(matrix[0][j] == 0 for j in range(n))
    
        # (2)使用其他行与列去处理第一行与第一列
        for i in range(1,m):
            for j in range(1,n):
                if matrix[i][j] == 0:
                    matrix[0][j] = 0 #第一行
                    matrix[i][0] = 0 #第一列
        
        # (3)反过来使用第一行与第一列去更新其他行与列
        for i in range(1,m):
            for j in range(1,n):
                if matrix[i][0] == 0 or matrix[0][j] == 0:
                    matrix[i][j] = 0
        
        # (4)最后使用两个标记变量更新第一行与第一列
        if flag_row0:
            for j in range(n):
                matrix[0][j] = 0
        if flag_col0:
            for i in range(m):
                matrix[i][0] = 0


5. 生命游戏

题目链接:生命游戏 - leetcode
题目描述:
根据 百度百科 , 生命游戏 ,简称为 生命 ,是英国数学家约翰·何顿·康威在 1970 年发明的细胞自动机。
给定一个包含 m × n 个格子的面板,每一个格子都可以看成是一个细胞。每个细胞都具有一个初始状态: 1 即为 活细胞 (live),或 0 即为 死细胞 (dead)。每个细胞与其八个相邻位置(水平,垂直,对角线)的细胞都遵循以下四条生存定律:
如果活细胞周围八个位置的活细胞数少于两个,则该位置活细胞死亡;
如果活细胞周围八个位置有两个或三个活细胞,则该位置活细胞仍然存活;
如果活细胞周围八个位置有超过三个活细胞,则该位置活细胞死亡;
如果死细胞周围正好有三个活细胞,则该位置死细胞复活;
下一个状态是通过将上述规则同时应用于当前状态下的每个细胞所形成的,其中细胞的出生和死亡是同时发生的。给你 m x n 网格面板 board 的当前状态,返回下一个状态。
题目归纳:

解题思路:
解法: 生命游戏 - leetcode官方题解
(1) 新建数组。耗费内存空间
(2) 记录复合状态:-1, 0, 1, 2,并二次遍历。

Python

class Solution:
    def gameOfLife(self, board: List[List[int]]) -> None:
        """
        Do not return anything, modify board in-place instead.
        """
        # (1)活细胞周围八个位置的活细胞数 < 2,    该位置活细胞死亡
        # (2)活细胞周围八个位置的活细胞数 =2 or 3,该位置活细胞仍然存活
        # (3)活细胞周围八个位置的活细胞数 > 3,    该位置活细胞死亡
        # (4)死细胞周围八个位置的活细胞数 = 3,    该位置死细胞复活;
        # (5)下一个状态,是通过将上述规则,同时应用于当前状态下的每个细胞,所形成的,其中细胞的出生和死亡是同时发生的。即你不能先更新某些格子,然后使用它们的更新后的值再更新其他格子,那还能原地修改吗?
        # (6)注意事项。原则上,面板是无限的,但当活细胞侵占了面板边界时会造成问题。
        # 这里不考虑非原地修改的算法,那样太浪费空间了,实际中肯定也不会那样使用


        # 使用额外的复合状态
        # -1:过去存活,现在死亡
        # 0:死亡
        # 1:存活
        # 2:过去死亡,现在存活
        # 由于复合状态隐含了过去细胞的状态,所以可以在不复制数组的情况下完成原地更新
        neighbors = [(1,0), (1,-1), (0,-1), (-1,-1), (-1,0), (-1,1), (0,1), (1,1)]
        rows = len(board)
        cols = len(board[0])

        # 一、遍历每个细胞并更新复合状态
        for row in range(rows):
            for col in range(cols):

                # (1)统计每个细胞的活邻居数
                live_neighbors = 0
                for neighbor in neighbors:

                    # 相邻位置坐标
                    r = row + neighbor[0]
                    c = col + neighbor[1]

                    # 邻居是否为活细胞
                    if (0 <= r and r < rows) and (0 <= c and c < cols) and abs(board[r][c]) == 1: # -1或1都是过去存活
                        live_neighbors += 1
                # (2)根据活邻居数应用规则
                if board[row][col] == 1 and (live_neighbors < 2 or 3 < live_neighbors):
                    board[row][col] = -1
                if board[row][col] == 0 and live_neighbors == 3:
                    board[row][col] = 2
        
        # 二、再次遍历,得到更新后的状态
        for row in range(rows):
            for col in range(cols):
                if board[row][col] > 0:
                    board[row][col] = 1
                else:
                    board[row][col] = 0

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1328141.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

融资项目——vue之双向数据绑定

上一篇文章中使用的v-bind是单向绑定方法&#xff0c;即数据改变&#xff0c;网页相应的视图发生改变&#xff0c;但是网页视图发生改变其相关联的数据不会发生改变。但是双向数据绑定不同之处在于网页视图发生改变其相关联的数据也会发生改变。Vue可以使用v-model进行双向数据…

深入浅出堆排序: 高效算法背后的原理与性能

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏: 《linux深造日志》 《高效算法》 ⛺️生活的理想&#xff0c;就是为了理想的生活! &#x1f4cb; 前言 &#x1f308;堆排序一个基于二叉堆数据结构的排序算法&#xff0c;其稳定性和排序效率在八大排序中也…

数学建模学习笔记-皮尔逊相关系数

内容&#xff1a;皮尔逊相关系数 一.概念&#xff1a;是一个和线性线关的相关性系数 1.协方差概念&#xff1a; 协方差受到量纲的影响因此需要剔除 2.相关性的误区 根据这个结论&#xff0c;我们在计算该系数之前需要确定是否为线性函数 二.相关性的计算 1.Matlab&#xff…

UE4移动端最小包优化实践

移动端对于包大小有着严苛的要求,然而UE哪怕是一个空工程打出来也有90+M,本文以一个复杂的工程为例,探索怎么把包大小降低到最小。 一、工程简介 工程包含代码、插件、资源、iOS原生库工程。 二、按官方文档进行基础优化 官方文档 1、勾选Use Pak File和Create comp…

Prompt-to-Prompt:基于 cross-attention 控制的图像编辑技术

Hertz A, Mokady R, Tenenbaum J, et al. Prompt-to-prompt image editing with cross attention control[J]. arXiv preprint arXiv:2208.01626, 2022. Prompt-to-Prompt 是 Google 提出的一种全新的图像编辑方法&#xff0c;不同于任何传统方法需要用户指定编辑区域&#xff…

【linux】用grep或者pgrep查找进程ID

一、用grep ps aux|grep 字符串|awk {print $2} 像上面这样运行&#xff0c;还会同时显示grep的进程ID。 需要再添加grep的反向查找命令&#xff0c;即查找不含有 "grep" 字段的行&#xff1a;grep -v grep。 ps aux | grep 字符串 | grep -v grep | awk {print …

058:vue组件引用外部js的方法

第058个 查看专栏目录: VUE ------ element UI 专栏目标 在vue和element UI联合技术栈的操控下&#xff0c;本专栏提供行之有效的源代码示例和信息点介绍&#xff0c;做到灵活运用。 &#xff08;1&#xff09;提供vue2的一些基本操作&#xff1a;安装、引用&#xff0c;模板使…

redis基本用法学习(C#调用NRedisStack操作redis)

redis官网文档中推荐C#中使用NRedisStack包连接并操作redis&#xff0c;本文学习C#调用NRedisStack操作redis的基本方式。   新建Winform项目&#xff0c;在Nuget包管理器中搜索并安装NRedisStack包&#xff0c;如下图所示&#xff1a; 主要调用StackExchange.Redis命名空间下…

苹果如何从iCloud恢复备份?正确方法看这里!

iCloud为所有苹果用户免费提供5G内存空间&#xff0c;用户可以将照片、短信、联系人、备忘录等重要信息备份到iCloud云端&#xff0c;这样可以方便在不同设备之间同步和共享。 同时&#xff0c;iCloud保证这些数据在所有苹果设备上及时自动更新。当遇到手机数据丢失时&#xf…

PDM产品数据管理软件,企业怎么选择PDM软件

PDM产品数据管理软件是用于管理和控制与产品相关的所有数据和信息的一套软件工具。它提供了一个中心化的平台&#xff0c;以组织、存储、共享和管理与产品相关的所有数据&#xff0c;包括设计图纸、文档、工艺流程、产品配置、材料清单&#xff08;BOM&#xff09;等。 PDM软件…

MyBatis:动态 SQL 标签

MyBatis 动态 SQL 标签if 标签where 标签trim 标签choose 、when 、otherwise 标签foreach 标签附 动态 SQL 标签 MyBatis 动态 SQL 标签&#xff0c;是一组预定义的标签&#xff0c;用于构建动态的 SQL 语句&#xff0c;允许在 SQL 语句中使用条件、循环和迭代等逻辑。通过使…

福建农林大学 html +css + JavaScript 期末复习 -- 保姆级

html css JavaScript 期末复习&#xff08;保姆级复盘&#xff09; 考试题型 1、选择题 20题 30分 2、判断题 15题 15分 3、程序题 3 题 30分 4、综合题 2 题 25分 1、网页第一代文本标签&#xff08;直接上代码&#xff0c;看保姆级注解&#xff09; <!-- doctype: docum…

基于JAVA的高校学生管理系统 开源项目

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 学生管理模块2.2 学院课程模块2.3 学生选课模块2.4 成绩管理模块 三、系统设计3.1 用例设计3.2 数据库设计3.2.1 学生表3.2.2 学院课程表3.2.3 学生选课表3.2.4 学生成绩表 四、系统展示五、核心代码5.1 查询课程5.2 新…

Golang 的内存管理

文章目录 1.内存管理角色1.常见的内存分配方法线性分配器空闲链表分配器TCMalloc 2.Go 内存管理组件mspanmcache初始化替换微分配器 mcentralmheap 3.内存分配4.内存管理思想参考文献 1.内存管理角色 内存管理一般包含三个不同的组件&#xff0c;分别是用户程序&#xff08;Mu…

高校/企业如何去做数据挖掘呢?

随着近年来人工智能及大数据、云计算进入爆发时期&#xff0c;依托三者进行的数据分析、数据挖掘服务已逐渐成为各行业进行产业升级的载体&#xff0c;缓慢渗透进我们的工作和生活&#xff0c;成为新时代升级版的智能“大案牍术”。 那么对于多数企业来说&#xff0c;如何做数据…

性能压力测试--确保企业数字化业务稳健运行

随着企业的数字化转型和依赖云计算的普及&#xff0c;软件系统的性能已经成为企业成功运营的关键因素之一。性能压力测试作为确保系统在各种条件下都能高效运行的关键步骤&#xff0c;对企业的重要性不可忽视。以下是性能压力测试对企业的几个重要方面的影响和作用&#xff1a;…

在Portainer创建Nginx容器并部署Web静态站点实现公网访问

&#x1f525;博客主页&#xff1a; 小羊失眠啦. &#x1f3a5;系列专栏&#xff1a;《C语言》 《数据结构》 《Linux》《Cpolar》 ❤️感谢大家点赞&#x1f44d;收藏⭐评论✍️ 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;…

Ubuntu 22.04 禁用(彻底移除)Snap

什么是Snaps Snaps 是 Ubuntu 的母公司 Canonical 于 2016 年 4 月发布 Ubuntu 16.04 LTS&#xff08;Long Term Support&#xff0c;长期支持版&#xff09;时引入的一种容器化的软件包格式。自 Ubuntu 16.04 LTS 起&#xff0c;Ubuntu 操作系统可以同时支持 Snap 及 Debian …

汽车制造厂设备故障预测与健康管理PHM

在现代汽车制造工业中&#xff0c;设备的可靠性和稳定性对于保证生产线的高效运行至关重要。为了提高生产效率、降低维修成本以及确保产品质量&#xff0c;汽车制造厂逐渐采用设备故障预测与健康管理&#xff08;PHM&#xff09;系统&#xff0c;以实现对设备状态的实时监测和预…

TypeScript实战——ChatGPT前端自适应手机端,PC端

前言 「作者主页」&#xff1a;雪碧有白泡泡 「个人网站」&#xff1a;雪碧的个人网站 可以在线体验哦&#xff1a;体验地址 文章目录 前言引言先看效果PC端手机端 实现原理解释 包的架构目录 引言 ChatGPT是由OpenAI开发的一种基于语言模型的对话系统。它是GPT&#xff08;…