Leetcode 刷题记录 06 —— 矩阵
本系列为笔者的 Leetcode 刷题记录,顺序为 Hot 100 题官方顺序,根据标签命名,记录笔者总结的做题思路,附部分代码解释和疑问解答。
目录
01 矩阵置零
方法一:标记数组
方法二:两个标记变量
02 螺旋矩阵
方法一:模拟
方法二:按层模拟
03 旋转图像
方法一:辅助数组
方法二:原地旋转
方法三:用翻转代替旋转
04 搜索二维矩阵 Ⅱ
方法一:直接查找
方法二:二分法
方法三:Z字形查找
01 矩阵置零
class Solution {
public:void setZeroes(vector<vector<int>>& matrix) {}
};
方法一:标记数组
时间复杂度 O(mn)
,空间复杂度 O(m + n)
-
建立两个数组
row(m)
和col(n)
,存储matrix
中行和列的含零情况 -
遍历数组,判断行或列含零,执行
matrix[i][j] = 0
class Solution {
public:void setZeroes(vector<vector<int>>& matrix) {int m = matrix.size();int n = matrix[0].size();vector<int> row(m), col(n);for(int i=0; i<m; ++i){for(int j=0; j<n; ++j){if(!matrix[i][j]){row[i] = true;col[j] = true;}}}for(int i=0; i<m; ++i){for(int j=0; j<n; ++j){if(row[i] || col[j]){matrix[i][j] = 0;}}}}
};
① vector<int> row(m), col(n);
这俩数组没有初始化啥的吗,它们声明时的默认元素值是多少?
使用 vector<int> row(m)
这样的语句声明一个 vector
并指定大小时, vector
会自动初始化为指定大小的元素,且每个元素默认初始化为零。
方法二:两个标记变量
时间复杂度 O(mn)
,空间复杂度 O(1)
-
建立两个标记
flag_col0
和flag_row0
,存储matrix
中除第零行和第零列的含零情况 -
遍历数组,判断
!matrix[i][0] || !matrix[0][j]
,执行matrix[i][j] = 0
-
第零行和第零列单独更新
class Solution {
public:void setZeroes(vector<vector<int>>& matrix) {int m = matrix.size(); //一共m行int n = matrix[0].size(); //一共n列int flag_col0 = false, flag_row0 = false;for(int i=0; i<m; ++i){if(!matrix[i][0]) flag_col0 = true;}for(int j=0; j<n; ++j){if(!matrix[0][j]) flag_row0 = true;}//处理大部分元素for(int i=1; i<m; ++i){for(int j=1; j<n; ++j){if(!matrix[i][j]){matrix[i][0] = 0;matrix[0][j] = 0;}}}for(int i=1; i<m; ++i){for(int j=1; j<n; ++j){if(!matrix[i][0] || !matrix[0][j]) {matrix[i][j] = 0;}}}//处理第零行、第零列元素if(flag_col0){for(int i=0; i<m; ++i){matrix[i][0] = 0;}}if(flag_row0){for(int j=0; j<n; ++j){matrix[0][j] = 0;}}}
};
02 螺旋矩阵
class Solution {
public:vector<int> spiralOrder(vector<vector<int>>& matrix) {}
};
方法一:模拟
时间复杂度 O(mn)
,空间复杂度 O(mn)
-
建立二维数组
visited(rows, vector<bool>(columns)
,存储矩阵元素的访问情况 -
遍历矩阵,判断
nextRow
和nextColumn
是否越过边界
class Solution {
public:static constexpr int dirs[4][2] = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}}; //向右、下、左、上vector<int> spiralOrder(vector<vector<int>>& matrix) {if (matrix.size() == 0 || matrix[0].size() == 0) return {};int rows = matrix.size(), columns = matrix[0].size(), total = rows * columns;vector<vector<bool>> visited(rows, vector<bool>(columns)); //标记vector<int> order(total); //答案int row = 0, column = 0, dirIndex = 0;for(int i=0; i<total; i++){order[i] = matrix[row][column]; //更新答案visited[row][column] = true; //更新标记int nextRow = row + dirs[dirIndex][0];int nextColumn = column + dirs[dirIndex][1];if(nextRow >= rows || nextRow < 0 || nextColumn >= columns || nextColumn < 0 || visited[nextRow][nextColumn]){dirIndex = (dirIndex + 1) % 4;}row += dirs[dirIndex][0];column += dirs[dirIndex][1];}return order;}
};
① static
意味着变量在程序的生命周期内只会被分配一次,并且在所有对该数组的函数调用中共享同一存储空间。
② constexpr
用于指定变量值在编译时就确定下来,它提示编译器尽量进行优化。
③ {{0, 1}, {1, 0}, {0, -1}, {-1, 0}}
每一对元素代表在二维平面上一个方向的坐标变换:
-
{0, 1}
:代表向右移动 —— 行不变,列加一 -
{1, 0}
:代表向下移动 —— 行加一,列不变 -
{0, -1}
:代表向左移动 —— 行不变,列减一 -
{-1, 0}
:代表向上移动 —— 行减一,列不变
④ vector<bool>(columns)
创建一个布尔向量,大小为 columns
,其中每个元素默认初始化为 false
。
⑤ vector<vector<bool>>(rows, ...)
表示创建一个这样的布尔向量的向量,其长度为 rows
,即每一行都是一个布尔向量,且每列都是初始化为 false
。
⑥ spiral
螺旋形的 constexpr
常量表达式
⑦ 在什么样的情况下 if (nextRow < 0 || nextRow >= rows || nextColumn < 0 || nextColumn >= columns || visited[nextRow][nextColumn])
中的 nextRow < 0
成立?
由于初始位置是 (0, 0)
且遍历顺序是顺时针螺旋,因此 nextRow < 0
通常在当前方向反转尝试向上之前使用过的路径上被访问时发生。
⑧ 将代码中涉及 nextRow
和 nextColumn
部分的片段改为如下片段如何?
if((column == (columns - 1)) || (row == (rows - 1)) || (column == 0) || matrix[row + dirs[dirIndex][0]][column + dirs[dirIndex][1]]){dirIndex = (dirIndex + 1) % 4;
}
matrix[row + dirs[dirIndex][0]][column + dirs[dirIndex][1]]
:这种方式不仅增加了代码复杂度,并且可能由于超出 matrix
的边界而导致访问无效内存,出现内存错误。
if (column == columns || row == rows || column == -1 || row == -1 ||
row + dirs[dirIndex][0] < 0 || row + dirs[dirIndex][0] >= rows ||
column + dirs[dirIndex][1] < 0 || column + dirs[dirIndex][1] >= columns ||
visited[row + dirs[dirIndex][0]][column + dirs[dirIndex][1]]) {
dirIndex = (dirIndex + 1) % 4;
}
row + dirs[dirIndex][0] < 0
:检查向当前方向移动后,新的行索引是否小于0。这会保持我们不越过上边界。
row + dirs[dirIndex][0] >= rows
:检查向当前方向移动后,新的行索引是否大于或等于总行数。这会确保我们不越过下边界。
column + dirs[dirIndex][1] < 0
:检查向当前方向移动后,新的列索引是否小于0。这会确保我们不越过左边界。
column + dirs[dirIndex][1] >= columns
:检查向当前方向移动后,新的列索引是否大于或等于总列数。这会确保我们不越过右边界。
方法二:按层模拟
时间复杂度 O(mn)
,空间复杂度 O(1)
class Solution {
public:vector<int> spiralOrder(vector<vector<int>>& matrix) {if(matrix.size() == 0 || matrix[0].size() == 0) return {};vector<int> order;int rows = matrix.size(), columns = matrix[0].size();int left = 0, right = columns-1, top = 0, bottom = rows-1;//主打一个遍历while(left<=right && top<=bottom){for(int column=left; column<=right; ++column) {order.push_back(matrix[top][column]);}for(int row=top+1; row<=bottom; ++row) {order.push_back(matrix[row][right]);}if(left<right && top<bottom){for(int column=right-1; column>=left+1; --column) {order.push_back(matrix[bottom][column]);}for(int row=bottom; row>=top+1; --row) {order.push_back(matrix[row][left]);}}top++;left++;right--;bottom--;}return order;}
};
① 为什么还要第二次判断 if(left<right && top<bottom)
呢?
在只有一行或者一列剩下时,第二次顺时针迭代会导致重复元素被添加到结果中。例如,当只剩下一行时,上面的第二次和第三次迭代(从右向左)会和已经处理的行产生重复。比如:
-
单行(例如,
[[1, 2, 3]]
) -
单列(例如,
[[1], [2], [3]]
)
03 旋转图像
class Solution {
public:void rotate(vector<vector<int>>& matrix) {}
};
方法一:辅助数组
时间复杂度 O(n^2)
,空间复杂度 O(n^2)
class Solution {
public:void rotate(vector<vector<int>>& matrix) {auto matrix_new = matrix;int n = matrix.size();for(int i=0; i<n; ++i){for(int j=0, j<n; ++j){matrix_new[j][n-1-i] = matrix[i][j];}}return matrix_new;}
};
auto matrix_new = matrix;
这行代码的作用是复制matrix
变量的值到一个新的变量matrix_new
中,matrix_new
的类型与matrix
一致。
对于大多数与标准库相关的容器(如 std::vector
),这会创建 matrix
的一个浅拷贝,整体上是深拷贝其内容,而不是仅仅复制指针(如果它是一个复杂数据结构)。
方法二:原地旋转
时间复杂度 O(n^2)
,空间复杂度 O(1)
class Solution {
public:void rotate(vector<vector<int>>& matrix) {int n = matrix.size();for(int x=0; x<n/2; ++x){for(int y=0; y<(n+1)/2; ++y){int flag = matrix[x][y];matrix[x][y] = matrix[n-1-y][x];matrix[n-1-y][x] = matrix[n-1-x][n-1-y];matrix[n-1-x][n-1-y] = matrix[y][n-1-x];matrix[y][n-1-x] = flag;}}}
};
方法三:用翻转代替旋转
时间复杂度 O(n^2)
,空间复杂度 O(1)
class Solution {
public:void rotate(vector<vector<int>>& matrix) {int n = matrix.size();for(int x=0; x<n/2; ++x){for(int y=0; y<n; ++y){swap(matrix[x][y], matrix[n-1-x][y]);}}for(int x=0; x<n; ++x){for(int y=0; y<x; ++y){swap(matrix[x][y], matrix[y][x]);}}}
};
04 搜索二维矩阵 Ⅱ
class Solution {
public:bool searchMatrix(vector<vector<int>>& matrix, int target) {}
};
方法一:直接查找
时间复杂度 O(mn)
,空间复杂度 O(1)
class Solution {
public:bool searchMatrix(vector<vector<int>>& matrix, int target) {for(const auto& row: matrix){for(int element: row){if(element == target) return true;}}return false;}
};
方法二:二分法
时间复杂度 O(mlogn)
,空间复杂度 O(1)
class Solution {
public:bool searchMatrix(vector<vector<int>>& matrix, int target) {for(const auto& row: matrix){auto it = lower_bound(row.begin(), row.end(), target);if(it != row.end() && *it == target) return true;}return false;}
};
lower_bound
是一个标准库函数,位于 <algorithm>
头文件中,用于在一个已排序的范围内查找目标值的位置。lower_bound
返回一个迭代器,指向范围内第一个不小于目标值的元素的位置,如果所有的元素都小于目标值,它将返回指向末尾的迭代器。
注意:使用 lower_bound
的前提是 row
必须是排序好的,否则结果是不确定的。
方法三:Z字形查找
class Solution {
public:bool searchMatrix(vector<vector<int>>& matrix, int target) {int m = matrix.size(), n = matrix[0].size();int x = 0, y = n-1;while(x < m && y >=0){if(matrix[x][y] == target) return true;else if(matrix[x][y] > target) y--;else x++;}return false;}
};
文章部分代码来源于力扣(LeetCode)
相关文章:

Leetcode 刷题记录 06 —— 矩阵
本系列为笔者的 Leetcode 刷题记录,顺序为 Hot 100 题官方顺序,根据标签命名,记录笔者总结的做题思路,附部分代码解释和疑问解答。 目录 01 矩阵置零 方法一:标记数组 方法二:两个标记变量 02 螺旋矩阵…...

什么样的物联网框架适合开展共享自助KTV唱歌项目?
现在物联网的广泛应用,也让更多用户们看到了它的实力,也使得共享经济遍地开花。其中共享自助唱歌设备也备受欢迎,那么适合开展共享自助KTV唱歌项目的物联网框架都应具备哪些特点呢? 智能化与自动化管理 物联网技术在共享KTV中的应…...
【Academy】HTTP Host 标头攻击 ------ HTTP Host header attacks
HTTP Host 标头攻击 ------ HTTP Host header attacks 1. 什么是 HTTP Host 标头?2. 什么是 HTTP Host 标头攻击?3. HTTP Host 标头漏洞是如何产生的?4. 如何测试 HTTP Host 标头漏洞4.1 提供任意 Host 标头4.2 检查有缺陷的验证4.3 发送不明…...

Web基础:HTML快速入门
HTML基础语法 HTML(超文本标记语言) 是用于创建网页内容的 标记语言,通过定义页面的 结构和内容 来告诉浏览器如何呈现网页。 超文本(Hypertext) 是一种通过 链接(Hyperlinks) 将不同文本、图像…...
技术领域,有许多优秀的博客和网站
在技术领域,有许多优秀的博客和网站为开发者、工程师和技术爱好者提供了丰富的学习资源和行业动态。以下是一些常用的技术博客和网站,涵盖了编程、软件开发、数据科学、人工智能、网络安全等多个领域: 1. 综合技术博客 1.1 Medium 网址: ht…...

k8s概念及k8s集群部署(Centos7)
Centos7部署k8s集群 部署之前,先简单说下k8s是个啥: 一、k8s简介: k8s,全称:kubernetes,它可以看作是一个分布式系统支撑平台。k8s的作用: 1、故障自愈: k8s这个玩意可以监控容器…...
《DeepSeek-V3:动态温度调节算法,开启推理新境界!》
在人工智能领域不断探索的征程中,DeepSeek-V3以其卓越的创新技术,尤其是动态温度调节算法,成为了备受瞩目的焦点。这项算法犹如一把神奇的钥匙,巧妙地开启了推理速度与精度动态平衡的大门,为大语言模型的发展开辟了新的…...

Python从入门到精通1:FastAPI
引言 在现代 Web 开发中,API 是前后端分离架构的核心。FastAPI 凭借其高性能、简洁的语法和自动文档生成功能,成为 Python 开发者的首选框架。本文将从零开始,详细讲解 FastAPI 的核心概念、安装配置、路由设计、请求处理以及实际应用案例&a…...

fastapi+angular停车管理系统可跨域
说明: 我计划用fastapiangular做一款停车管理系统,支持跨域 1.设计mysql数据库表, 2.建表,添加测试数据,多表查询, 3.在fastapi写接口查询数据, 4.用postman测试, 5.在angular前端展…...
前端题目类型
HTMLCSS常见面试题 HTML标签有哪些行内元素 img、picture、span、input、textarea、select、label 说说你对元素语义化的理解 元素语义化就是用正确的元素做正确的事情。虽然理论上所有html元素都可通过css样式实现相同效果,但这样会使事情复杂化,所以需…...
openwrt路由系统------lua、uci的关系
1. Luci 的核心组成 (1) Lua 简介:Luci 的界面和逻辑几乎完全使用 Lua 脚本语言编写。Lua 是一种轻量级、高效的嵌入式脚本语言,适合在资源受限的路由器环境中运行。作用: 生成动态 Web 页面(与后端交互渲染 HTML)。处理用户提交的表单数据(如修改 Wi-Fi 密码)。调用系…...

Elastic:AI 会开始取代网络安全工作吗?
作者:来自 Elastic Joe DeFever 不会,但它正在从根本上改变这些工作。 生成式 AI (GenAI) 正迅速成为日常安全工作流程中的一个重要组成部分。那么,它是合作伙伴还是竞争对手? GenAI 技术在安全堆栈几乎每个方面的广泛应用&#…...

Linux安装升级docker
Linux 安装升级docker Linux 安装升级docker背景升级停止docker服务备份原docker数据目录移除旧版本docker安装docker ce恢复数据目录启动docker参考 安装找到docker官网找到docker文档删除旧版本docker配置docker yum源参考官网继续安装docker设置开机自启配置加速测试 Linux …...

【经验分享】Ubuntu20.04编译RK3568 AI模型报错问题(已解决)
【经验分享】Ubuntu20.04编译RK3568 AI模型报错问题(已解决) 前言问题现象问题分析解决方案总结 前言 这里使用的是Rockchip提供的rknn_model_zoo,https://github.com/airockchip/rknn_model_zoo/tree/main 此解决方案适用于Rockchip芯片在U…...

国产算力助力工业智能新范式
随着人工智能、智能制造以及边缘计算等技术趋势的发展,算力设备正逐渐从中心云向边缘机房乃至边缘现场下沉。在此背景下,以工控机为例的部署于各类边缘现场的算力设备市场,也正面临着新的变革。 根据IDC 2024研究报告显示:在能源制…...
学习笔记:利用OpenAI实现阅卷智能体
https://zhuanlan.zhihu.com/p/18047953492 ### 学习笔记:利用OpenAI实现阅卷智能体 #### 一、背景与需求 在各类考试中,选择题、判断题、填空题的阅卷相对简单,只需对比答案与作答是否一致。然而,简答题的阅卷较为复杂ÿ…...
第6届传智杯复赛第一场
A小红劈字符串 题目链接 题目链接:A-小红劈字符串(B组)_第6届传智杯复赛第一场(补题) (nowcoder.com) 题目描述 小红拿到了一个仅由小写字母组成的字符串,她希望将其分割成两个非空子串,使得第…...

CSDN博客:Markdown编辑语法教程总结教程(中)
❤个人主页:折枝寄北的博客 Markdown编辑语法教程总结 前言1. 列表1.1 无序列表1.2 有序列表1.3 待办事项列表1.4 自定义列表 2. 图片2.1 直接插入图片2.2 插入带尺寸的图片2.3 插入宽度确定,高度等比例的图片2.4 插入高度确定宽度等比例的图片2.5 插入居…...
Codeforces Round 258 (Div. 2) E. Devu and Flowers 生成函数
题目链接 题目大意 有 n n n ( 1 ≤ n ≤ 20 ) (1\leq n \leq 20) (1≤n≤20) 个花瓶,第 i i i 个花瓶里有 f i f_i fi ( 1 ≤ f i ≤ 1 0 12 ) (1\leq f_i \leq 10^{12}) (1≤fi≤1012) 朵花。现在要选择 s s s ( 1 ≤ s ≤ 1 0 14 ) (1\leq s \leq 1…...

【高并发内存池】释放内存 + 申请和释放总结
高并发内存池 1. 释放内存1.1 thread cache1.2 central cache1.3 page cache 2. 申请和释放剩余补充 点赞👍👍收藏🌟🌟关注💖💖 你的支持是对我最大的鼓励,我们一起努力吧!😃&#x…...

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式
一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明:假设每台服务器已…...
在软件开发中正确使用MySQL日期时间类型的深度解析
在日常软件开发场景中,时间信息的存储是底层且核心的需求。从金融交易的精确记账时间、用户操作的行为日志,到供应链系统的物流节点时间戳,时间数据的准确性直接决定业务逻辑的可靠性。MySQL作为主流关系型数据库,其日期时间类型的…...

XCTF-web-easyupload
试了试php,php7,pht,phtml等,都没有用 尝试.user.ini 抓包修改将.user.ini修改为jpg图片 在上传一个123.jpg 用蚁剑连接,得到flag...

Unity3D中Gfx.WaitForPresent优化方案
前言 在Unity中,Gfx.WaitForPresent占用CPU过高通常表示主线程在等待GPU完成渲染(即CPU被阻塞),这表明存在GPU瓶颈或垂直同步/帧率设置问题。以下是系统的优化方案: 对惹,这里有一个游戏开发交流小组&…...

《Qt C++ 与 OpenCV:解锁视频播放程序设计的奥秘》
引言:探索视频播放程序设计之旅 在当今数字化时代,多媒体应用已渗透到我们生活的方方面面,从日常的视频娱乐到专业的视频监控、视频会议系统,视频播放程序作为多媒体应用的核心组成部分,扮演着至关重要的角色。无论是在个人电脑、移动设备还是智能电视等平台上,用户都期望…...

23-Oracle 23 ai 区块链表(Blockchain Table)
小伙伴有没有在金融强合规的领域中遇见,必须要保持数据不可变,管理员都无法修改和留痕的要求。比如医疗的电子病历中,影像检查检验结果不可篡改行的,药品追溯过程中数据只可插入无法删除的特性需求;登录日志、修改日志…...
QMC5883L的驱动
简介 本篇文章的代码已经上传到了github上面,开源代码 作为一个电子罗盘模块,我们可以通过I2C从中获取偏航角yaw,相对于六轴陀螺仪的yaw,qmc5883l几乎不会零飘并且成本较低。 参考资料 QMC5883L磁场传感器驱动 QMC5883L磁力计…...
基于数字孪生的水厂可视化平台建设:架构与实践
分享大纲: 1、数字孪生水厂可视化平台建设背景 2、数字孪生水厂可视化平台建设架构 3、数字孪生水厂可视化平台建设成效 近几年,数字孪生水厂的建设开展的如火如荼。作为提升水厂管理效率、优化资源的调度手段,基于数字孪生的水厂可视化平台的…...
【Go语言基础【12】】指针:声明、取地址、解引用
文章目录 零、概述:指针 vs. 引用(类比其他语言)一、指针基础概念二、指针声明与初始化三、指针操作符1. &:取地址(拿到内存地址)2. *:解引用(拿到值) 四、空指针&am…...
Caliper 负载(Workload)详细解析
Caliper 负载(Workload)详细解析 负载(Workload)是 Caliper 性能测试的核心部分,它定义了测试期间要执行的具体合约调用行为和交易模式。下面我将全面深入地讲解负载的各个方面。 一、负载模块基本结构 一个典型的负载模块(如 workload.js)包含以下基本结构: use strict;/…...