力扣hot100 | 矩阵 | 73. 矩阵置零、54. 螺旋矩阵、48. 旋转图像、240. 搜索二维矩阵 II

73. 矩阵置零

力扣题目链接
给定一个 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]]

一、用额外空间(不符题意)

  • 遍历矩阵,记录哪些行和列包含0
  • 再次遍历,将对应行列置零
def setZeroes_v1(self, matrix: List[List[int]]) -> None:if not matrix or not matrix[0]:  # []和[[]]情况(题目说m,n>=1,可以不写,但要没说就一定要写这里)returnm, n = len(matrix), len(matrix[0])zero_rows = set()zero_cols = set()# 第一次遍历,记录包含0的行和列for i in range(m):for j in range(n):if matrix[i][j] == 0:zero_rows.add(i)zero_cols.add(j)# 第二次遍历,将对应行列置零for i in range(m):for j in range(n):if i in zero_rows or j in zero_cols:matrix[i][j] = 0
  • 时间复杂度 O(m * n)
  • 空间复杂度 O(m + n)

二、原地方法【推荐】

  • 【思路】用第一行记录列、用第一列记录行
    • 利用矩阵的第一行和第一列来记录哪些行列需要置零
    • 需要特殊处理第一行和第一列本身是否包含0的情况
  • 【步骤】
    1. 检查第一行和第一列是否本身包含0,用两个标志位记录
    2. 遍历矩阵其余部分,如果发现0,则在对应的第一行(记信息)和第一列(记信息)位置标记
    3. 根据第一行和第一列的标记,将对应行列置零
    4. 最后根据标志位处理第一行和第一列
def setZeroes_v1(self, matrix: List[List[int]]) -> None:if not matrix or not matrix[0]: # []和[[]](虽然题目说m,n>=1,可以不写,但要没说就一定要写这里)returnm, n = len(matrix), len(matrix[0])  # m行,n列# 检查第一行和第一列是否包含0first_row_zero = any(matrix[0][j] == 0 for j in range(n)) # 【注意】检查行要遍历列first_col_zero = any(matrix[i][0] == 0 for i in range(m)) # 【注意】检查列要遍历行# 遍历矩阵其余部分,使用第一行和第一列作为标记for i in range(1, m):for j in range(1, n):if matrix[i][j] == 0:matrix[i][0] = 0  # 标记该行需要置零matrix[0][j] = 0  # 标记该列需要置零# 根据标记将对应行列置零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# 处理第一行if first_row_zero:for j in range(n):matrix[0][j] = 0# 处理第一列if first_col_zero:for i in range(m):matrix[i][0] = 0

54. 螺旋矩阵

力扣题目链接
给你一个 mn 列的矩阵 matrix ,请按照 顺时针螺旋顺序 ,返回矩阵中的所有元素。

示例 1:
在这里插入图片描述
输入:matrix = [[1,2,3],[4,5,6],[7,8,9]]
输出:[1,2,3,6,9,8,7,4,5]

示例 2:
在这里插入图片描述
输入:matrix = [[1,2,3,4],[5,6,7,8],[9,10,11,12]]
输出:[1,2,3,4,8,12,11,10,9,5,6,7]

一、四个边界指针【最推荐】

  • 【思路】
    • 维护四个边界:top, bottom, left, right
    • 每遍历完一条边,对应边界向内收缩
    • 注意处理单行或单列的情况
  • 【步骤】
    1. 初始化四个边界指针
    2. 按照右→下→左→上的顺序遍历上/右/下/左边界
    3. 每遍历完一条边,收缩对应边界,再开始下一条边的遍历
    4. 当边界重叠时结束遍历 while top <= bottom and left <= right:(注意top小哦)
def spiralOrder_v1(matrix):if not matrix or not matrix[0]:return []m, n = len(matrix), len(matrix[0])res = []# 四个边界指针top, bottom = 0, m - 1left, right = 0, n - 1while top <= bottom and left <= right:  # 一定记得要加=号啊!不然最中心的遍历不到!!# 1. 向右遍历上边界for j in range(left, right + 1):res.append(matrix[top][j])top += 1# 2. 向下遍历右边界for i in range(top, bottom + 1):res.append(matrix[i][right])right -= 1# 3. 向左遍历下边界(需要检查是否还有行)if top <= bottom: # 就算在本轮迭代中,上面已经top++过,所以要再检查!!!for j in range(right, left - 1, -1):res.append(matrix[bottom][j])bottom -= 1# 4. 向上遍历左边界(需要检查是否还有列)if left <= right: # 就算在本轮迭代中,上面已经right--过,所以要再检查!!!for i in range(bottom, top - 1, -1):res.append(matrix[i][left])left += 1return res
  • 时间复杂度 O(m*n)
  • 空间复杂度 O(1):不算结果数组的话
  • 【注意】因为循环条件while top <= bottom and left <= right:中有等于号,所以循环中遍历bottomleft前一定要检查if top <= bottom:和if left <= right:!!不然最后一轮循环中(开头就重叠,上面两条边再+/-)还会额外添加一个值进去!!!
  • ——>记忆: “遍历bottom前检查top,遍历left前检查right”!!!

二、方向数组

  • 使用方向数组表示四个方向的移动
  • 遇到边界或已访问元素时改变方向
  • 使用visited数组标记已访问元素
def spiralOrder_v2(matrix):if not matrix or not matrix[0]:return []m, n = len(matrix), len(matrix[0])result = []visited = [[False] * n for _ in range(m)]# 方向数组:右、下、左、上directions = [(0, 1), (1, 0), (0, -1), (-1, 0)]direction_idx = 0row, col = 0, 0for _ in range(m * n):result.append(matrix[row][col])visited[row][col] = True# 计算下一个位置next_row = row + directions[direction_idx][0]next_col = col + directions[direction_idx][1]# 检查是否需要转向if (next_row < 0 or next_row >= m or next_col < 0 or next_col >= n or visited[next_row][next_col]):# 转向direction_idx = (direction_idx + 1) % 4next_row = row + directions[direction_idx][0]next_col = col + directions[direction_idx][1]row, col = next_row, next_colreturn result
  • 时间复杂度 O(m*n)
  • 空间复杂度 O(m*n)

三、递归分治

  • 递归处理外圈和内圈
  • 每次递归处理当前矩形的一圈
def spiralOrder_v3(matrix):if not matrix or not matrix[0]:return []def spiral_helper(matrix, start_row, end_row, start_col, end_col):if start_row > end_row or start_col > end_col:return []result = []# 只有一行if start_row == end_row:for j in range(start_col, end_col + 1):result.append(matrix[start_row][j])return result# 只有一列if start_col == end_col:for i in range(start_row, end_row + 1):result.append(matrix[i][start_col])return result# 遍历外圈# 上边界for j in range(start_col, end_col):result.append(matrix[start_row][j])# 右边界for i in range(start_row, end_row):result.append(matrix[i][end_col])# 下边界for j in range(end_col, start_col, -1):result.append(matrix[end_row][j])# 左边界for i in range(end_row, start_row, -1):result.append(matrix[i][start_col])# 递归处理内圈result.extend(spiral_helper(matrix, start_row + 1, end_row - 1, start_col + 1, end_col - 1))return resultm, n = len(matrix), len(matrix[0])return spiral_helper(matrix, 0, m - 1, 0, n - 1)
  • 时间复杂度 O(m*n)
  • 空间复杂度 O(min(m,n)):递归栈深度

48. 旋转图像

力扣题目链接
给定一个 n × n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度

你必须在原地旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要使用另一个矩阵来旋转图像。

示例 1:
在这里插入图片描述
输入:matrix = [[1,2,3],[4,5,6],[7,8,9]]
输出:[[7,4,1],[8,5,2],[9,6,3]]

示例 2:
在这里插入图片描述
输入:matrix = [[5,1,9,11],[2,4,8,10],[13,3,6,7],[15,14,12,16]]
输出:[[15,13,2,5],[14,3,4,1],[12,6,8,9],[16,7,10,11]]

一、转置 + 水平翻转 = 顺时针90°旋转 【推荐】

  • 【原理】旋转90°的数学本质:(i,j) → (j, n-1-i)
  • 【思路】先转置,再沿中轴线水平翻转 matrix[i][j] → matrix[j][i] → matrix[j][n-1-i]
	原矩阵    转置后    水平翻转后[1,2,3]   [1,4,7]   [7,4,1][4,5,6] → [2,5,8] → [8,5,2][7,8,9]   [3,6,9]   [9,6,3]
  • 【算法步骤】
    1. 转置矩阵:matrix[i][j]matrix[j][i] 交换
    2. 水平翻转:每行沿中轴线左右对称交换(是镜像,也是reverse()
def rotate(self, matrix: List[List[int]]) -> None:n = len(matrix)# 第一步:转置矩阵(沿主对角线翻折)for i in range(n):for j in range(i, n):  # 只处理上三角,避免重复交换matrix[i][j], matrix[j][i] = matrix[j][i], matrix[i][j]# 第二步:水平翻转(每行左右对称交换)for i in range(n):for j in range(n // 2):matrix[i][j], matrix[i][n - 1 - j] = matrix[i][n - 1 - j], matrix[i][j]# 或者直接reverse每一行:'''for i in range(n):matrix[i].reverse()  '''
  • 时间复杂度 O(n^2)
  • 空间复杂度 O(1)

二、四元素循环法

  • 【思路】每次处理四个位置的元素循环移动:
    (i,j) → (j,n-1-i) → (n-1-i,n-1-j) → (n-1-j,i) → (i,j)
def rotate(self, matrix: List[List[int]]) -> None:n = len(matrix)# 处理每一层(环)for layer in range(n // 2):first = layerlast = n - 1 - layer# 处理当前层的每个元素for i in range(first, last):offset = i - first# 保存top元素top = matrix[first][i]# left → topmatrix[first][i] = matrix[last - offset][first]# bottom → leftmatrix[last - offset][first] = matrix[last][last - offset]# right → bottommatrix[last][last - offset] = matrix[i][last]# top → rightmatrix[i][last] = top######---------------- 优化写法 ----------------######
def rotate(self, matrix: List[List[int]]) -> None:n = len(matrix)for i in range(n // 2):for j in range(n - n // 2):# 四个位置同时交换,使用Python的多重赋值(matrix[i][j], matrix[~j][i], matrix[~i][~j], matrix[j][~i]) = (matrix[~j][i], matrix[~i][~j], matrix[j][~i], matrix[i][j])# 注意:~i 等价于 n-1-i
  • 时间复杂度 O(n^2)
  • 空间复杂度 O(1)

关键技巧:

  1. 转置时只处理上三角:避免重复交换导致还原
  2. 边界处理:注意n//2的使用,确保正确的循环边界
  3. Python多重赋值:可以优雅地实现多元素交换

拓展

逆时针90° = 垂直翻转 + 转置
旋转180° = 水平翻转 + 垂直翻转
旋转270° = 旋转90°三次,或者逆时针90°

240. 搜索二维矩阵 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

一、暴力解法

def searchMatrix(self, matrix: List[List[int]], target: int) -> bool:if not matrix or not matrix[0]:return Falsefor row in matrix:for val in row:if val == target:return Truereturn False
  • 时间复杂度 O(m * n)
  • 空间复杂度 O(1)

二、角落搜索法【推荐】

此方法参考:灵茶山艾府

  • 【思路】 右上角元素的特性:
    • 是当前行的最大值
    • 是当前列的最小值
    • 这个性质使得我们可以明确移动方向
  • 【步骤】
    1. 从右上角(0, n-1)开始
    2. 如果当前值等于target,返回True
    3. 如果当前值大于target,左移(排除当前列)
    4. 如果当前值小于target,下移(排除当前行)
    5. 重复直到找到目标或越界
  • 【举例】:
    在这里插入图片描述
class Solution:def searchMatrix(self, matrix: List[List[int]], target: int) -> bool:m, n = len(matrix), len(matrix[0])i, j = 0, n - 1               # 从右上角开始while i <= m - 1 and j >= 0:  # 或者i < m and j >= 0都可以,只是确保还有剩余元素if matrix[i][j] == target:return Trueif matrix[i][j] < target: # 该行max都小于target, 整行排除,i下移i += 1else:                     # 该列min都大于target, 整列排除,j上移j -= 1return False
  • 时间复杂度 O(m + n):每次循环排除掉一行或者一列,一共 m+n 行列,最坏情况下需要排除 m+n−1 行列才能找到答案。
  • 空间复杂度 O(1)
  • 【另外】还可以用左下角搜索(是该行的最小值,也是该列的最大值),思路类似。

三、逐行二分查找

  • 【思路】对每一行进行二分查找
def searchMatrix(self, matrix: List[List[int]], target: int) -> bool:def binary_search_row(row):"""在指定行进行二分查找"""left, right = 0, len(row) - 1while left <= right:mid = (left + right) // 2if row[mid] == target:return Trueelif row[mid] < target:left = mid + 1else:right = mid - 1return Falsefor row in matrix:# 优化:如果target小于行首或大于行尾,跳过该行if target < row[0] or target > row[-1]:continueif binary_search_row(row):return Truereturn False
  • 时间复杂度 O(m * log n)
  • 空间复杂度 O(1)

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

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

相关文章

ARC与eARC是什么?主要用在哪?

在家庭影音设备不断升级的今天&#xff0c;人们对音视频体验的要求越来越高。无论是追剧、玩游戏还是观看电影大片&#xff0c;很多用户不再满足于电视自带的扬声器&#xff0c;而是希望借助回音壁、功放或家庭影院系统&#xff0c;获得更加震撼的沉浸式声音体验。一、ARC是什么…

解锁JavaScript性能优化:从理论到实战

文章目录 前言 一、常见性能瓶颈剖析 二、实战案例与优化方案 (一)DOM 操作优化案例​ (二)事件绑定优化案例​ (三)循环与递归优化案例​ (四)内存管理优化案例​ 三、性能优化工具介绍 总结 前言 性能优化的重要性 在当今数字化时代,Web 应用已成为人们生活和工作…

结构化记忆、知识图谱与动态遗忘机制在医疗AI中的应用探析(上)

往期相关内容推荐: 基于Python的多元医疗知识图谱构建与应用研究(上)

XSS攻击:从原理入门到实战精通详解

一、XSS攻击基础概念1.1 什么是XSS攻击 XSS&#xff08;Cross-Site Scripting&#xff0c;跨站脚本攻击&#xff09;是一种将恶意脚本注入到可信网站中的攻击手段。当用户访问被注入恶意代码的页面时&#xff0c;浏览器会执行这些代码&#xff0c;导致&#xff1a;用户会话被劫…

Leetcode 14 java

今天复习一下以前做过的题目&#xff0c;感觉是忘光了。 160. 相交链表 给你两个单链表的头节点 headA 和 headB &#xff0c;请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点&#xff0c;返回 null 。 图示两个链表在节点 c1 开始相交&#xff1a; 题目数…

用 FreeMarker 动态构造 SQL 实现数据透视分析

在 ERP、BI 等系统中&#xff0c;数据透视分析&#xff08;Pivot Analysis&#xff09;是非常常见的需求&#xff1a;用户希望按任意维度&#xff08;如门店、时间、商品分类等&#xff09;进行分组统计&#xff0c;同时选择不同的指标&#xff08;如 GMV、订单数、客单价等&am…

13.深度学习——Minst手写数字识别

第一部分——起手式 import torch from torchvision import datasets, transforms import torch.nn as nn import torch.nn.functional as F import torch.optim as optimuse_cuda torch.cuda.is_available()if use_cuda:device torch.device("cuda") else: device…

【JAVA高级】实现word转pdf 实现,源码概述。深坑总结

之前的需求做好后,需求,客户突发奇想。要将生成的word转为pdf! 因为不想让下载文档的人改动文档。 【JAVA】实现word添加标签实现系统自动填入字段-CSDN博客 事实上这个需求难度较高,并不是直接转换就行的 word文档当中的很多东西都需要处理 public static byte[] gener…

数据驱动测试提升自动化效率

测试工程师老王盯着满屏重复代码叹气&#xff1a;“改个搜索条件要重写20个脚本&#xff0c;这班加到啥时候是个头&#xff1f;” 隔壁组的小李探过头&#xff1a;“试试数据驱动呗&#xff0c;一套脚本吃遍所有数据&#xff0c;我们组上周测了300个组合都没加班&#xff01;”…

模板引用(Template Refs)全解析2

三、v-for 中的模板引用 当在 v-for 中使用模板引用时,引用的 value 会自动变为一个数组,包含列表中所有元素/组件的引用(需 Vue 3.5+ 版本,旧版需手动处理且顺序不保证)。 1. 基本用法(Vue 3.5+) <script setup> import { ref, useTemplateRef, onMounted } f…

【Linux系统】进程间通信:System V IPC——共享内存

前文中我们介绍了管道——匿名管道和命名管道来实现进程间通信&#xff0c;在介绍怎么进行通信时&#xff0c;我们有提到过不止管道的方式进行通信&#xff0c;还有System V IPC&#xff0c;今天这篇文章我们就来学习一下System V IPC中的共享内存1. 为何引入共享内存&#xff…

[优选算法专题二滑动窗口——最大连续1的个数 III]

题目链接 最大连续1的个数 III 题目描述 题目解析 问题本质 输入&#xff1a;二进制数组nums&#xff08;只包含 0 和 1&#xff09;和整数k操作&#xff1a;最多可以将k个 0 翻转成 1目标&#xff1a;找到翻转后能得到的最长连续 1 的子数组长度 这个问题的核心是要找到一…

C#单元测试(xUnit + Moq + coverlet.collector)

C#单元测试 xUnit Moq coverlet.collector 1.添加库 MlyMathLib 2.编写库函数内容 using System;namespace MlyMathLib {public interface IUserRepo{string GetName(int id);}public class UserService{private readonly IUserRepo _repo;public UserService(IUserRepo repo…

【数据库】Oracle学习笔记整理之五:ORACLE体系结构 - 参数文件与控制文件(Parameter Files Control Files)

Oracle体系结构 - 参数文件与控制文件&#xff08;Parameter Files & Control Files&#xff09; 参数文件与控制文件是Oracle数据库的“双核基石”&#xff1a;参数文件是实例的“启动配置中心”&#xff0c;定义运行环境与规则&#xff1b;控制文件是数据库的“物理元数据…

GDB典型开发场景深度解析

GDB典型开发场景深度解析 以下是开发过程中最常见的GDB使用场景&#xff0c;结合具体实例和调试技巧&#xff0c;帮助开发者高效解决实际问题&#xff1a;一、崩溃分析&#xff08;Core Dump调试&#xff09; 场景&#xff1a;程序突然崩溃&#xff0c;生成了core文件 # 启动调…

存储、硬盘、文件系统、 IO相关常识总结

目录 &#xff08;一&#xff09;存储 &#xff08;1&#xff09;定义 &#xff08;2&#xff09;分类 &#xff08;二&#xff09;硬盘 &#xff08;1&#xff09;容量&#xff08;最主要的参数&#xff09; &#xff08;2&#xff09;转速 &#xff08;3&#xff09;访…

docker安装mongodb及java连接实战

1.docker部署mongodb docker run --name mongodb -d -p 27017:27017 -v /data/mongodbdata:/data/db -e MONGO_INITDB_ROOT_USERNAMEtestmongo -e MONGO_INITDB_ROOT_PASSWORDtest123456 mongodb:4.0.112.项目实战 <dependencies><dependency><groupId>org.m…

Java设计模式之《工厂模式》

目录 1、介绍 1.1、定义 1.2、优缺点 1.3、使用场景 2、实现 2.1、简单工厂模式 2.2、工厂方法模式 2.3、抽象工厂模式 3、小结 前言 在面向对象编程中&#xff0c;创建对象实例最常用的方式就是通过 new 操作符构造一个对象实例&#xff0c;但在某些情况下&#xff0…

【异步】js中异步的实现方式 async await /Promise / Generator

JS的异步相关知识 js里面一共有以下异步的解决方案 传统的回调 省略 。。。。 生成器 Generator 函数是 ES6 提供的一种异步编程解决方案, 语法上&#xff0c;首先可以把它理解成&#xff0c;Generator 函数是一个状态机&#xff0c;封装了多个内部状态。执行 Generator 函数…

JVM字节码文件结构

Class文件结构class文件是二进制文件&#xff0c;这里要介绍的是这个二级制文件的结构。思考&#xff1a;一个java文件编译成class文件&#xff0c;如果要描述一个java文件&#xff0c;需要哪些信息呢&#xff1f;基本信息&#xff1a;类名、父类、实现哪些接口、方法个数、每个…