LeetCode 三道高频中等数组算法详解|除自身乘积、矩阵置零、螺旋矩阵

🔥你好我是fengxin_rou这是我的个人主页 fengxin_rou的主页

❄️欢迎查看我的专栏我的专栏

《Java后端学习》《JAVASE基础》《JUC并发》《redis》《JVM虚拟机》《MYSQL》《黑马点评》《rabbitmq》《JavaWeb+AI的talis学习系统》《苍穹外卖》

目录

一、前言:为什么数组算法是算法入门核心?

[二、238. 除自身以外数组的乘积(中等)](#二、238. 除自身以外数组的乘积(中等))

[2.1 题目描述](#2.1 题目描述)

[2.2 误区分析(新手常踩坑)](#2.2 误区分析(新手常踩坑))

[2.3 核心解题思路](#2.3 核心解题思路)

[2.4 Java 完整 AC 代码(逐行注释)](#2.4 Java 完整 AC 代码(逐行注释))

[2.5 复杂度分析](#2.5 复杂度分析)

[2.6 解题复盘](#2.6 解题复盘)

[三、73. 矩阵置零(中等)](#三、73. 矩阵置零(中等))

[3.1 题目描述](#3.1 题目描述)

[3.2 新手误区](#3.2 新手误区)

[3.3 核心解题思路](#3.3 核心解题思路)

[3.4 Java 完整 AC 代码](#3.4 Java 完整 AC 代码)

[3.5 复杂度分析](#3.5 复杂度分析)

[3.6 进阶优化思路(面试加分项)](#3.6 进阶优化思路(面试加分项))

[四、54. 螺旋矩阵(中等)](#四、54. 螺旋矩阵(中等))

[4.1 题目描述](#4.1 题目描述)

[4.2 解题难点](#4.2 解题难点)

[4.3 核心解题思路](#4.3 核心解题思路)

[4.4 Java 完整 AC 代码](#4.4 Java 完整 AC 代码)

[4.5 复杂度分析](#4.5 复杂度分析)

[4.6 易错点总结](#4.6 易错点总结)

五、三道算法题核心思维总结

六、刷题优化与复盘建议

结语


一、前言:为什么数组算法是算法入门核心?

在算法体系中,数组是最基础、最朴素的线性结构,几乎所有高级算法(动态规划、双指针、贪心、单调栈)都建立在数组遍历的基础之上。很多同学刷题卡顿的核心原因,不是看不懂复杂算法,而是基础数组思维不扎实、边界处理不严谨、不会空间优化

LeetCode 中等难度数组题,普遍具备以下特点:

  • 逻辑不难,但是细节极多、易错点密集

  • 暴力解法能通过部分案例,但无法满足时间、空间要求

  • 需要思维转换:拆分遍历、状态标记、边界收缩、空间复用

今天讲解的三道题,覆盖了数组算法三大核心经典思想:

  • 前后缀拆分思想(238)

  • 原地标记、状态存储思想(73)

  • 动态边界模拟、循环遍历思想(54)

三道题吃透,可解决 80% 中等数组、矩阵类面试题型。

二、238. 除自身以外数组的乘积(中等)

2.1 题目描述

给你一个整数数组 nums,返回数组 answer,其中 answeri 等于 nums 中除 numsi 之外其余各元素的乘积。

限制条件

  • 请勿使用除法

  • 时间复杂度必须 O(n)

  • 32 位整数范围内,无需考虑溢出

2.2 误区分析(新手常踩坑)

很多新手第一反应:求出数组总乘积,每个位置除以当前数字即可。

但题目明确禁止除法,且存在数组含 0 的场景,除法方案完全不可行。暴力双重循环 O(n²) 会超时,无法通过大数据案例。因此必须使用前后缀乘积拆分的优化思路。

2.3 核心解题思路

任意位置 i 的结果 = i 左侧所有数的乘积 × i 右侧所有数的乘积

我们可以将计算拆分为两次单层遍历:

  1. 左遍历(前缀乘积):从前往后遍历,让 ansi 存储当前下标左侧所有元素的累积乘积。

  2. 右遍历(后缀乘积):从后往前遍历,用临时变量记录右侧累积乘积,与当前 ansi 相乘,得到最终结果。

该方案全程仅两次线性遍历,严格满足 O(n) 时间复杂度,且复用结果数组,无额外数组开销,空间最优。

2.4 Java 完整 AC 代码(逐行注释)

复制代码
class Solution {
    public int[] productExceptSelf(int[] nums) {
        int len = nums.length;
        // 空数组直接返回
        if(len == 0) return new int[0];
        int[] ans = new int[len];
        
        // 第一位左侧无元素,前缀乘积默认为1
        ans[0] = 1;
        // 第一次遍历:计算所有位置的前缀乘积
        for(int i = 1; i < len ; i++){
            ans[i] = nums[i-1] * ans[i-1]; 
        }

        // 临时变量存储后缀乘积
        int temp = 1;
        // 第二次遍历:从后往前,累乘右侧后缀乘积
        for(int i = len -2 ; i >= 0 ; i--){
            temp *= nums[i + 1];
            ans[i] *= temp;
        }
        return ans;
    }
}

2.5 复杂度分析

  • 时间复杂度:O(n),两次线性遍历,无嵌套循环

  • 空间复杂度:O(1),仅使用常数临时变量,结果数组不计入额外空间

2.6 解题复盘

本题核心思维就是拆分问题、空间换时间、复用数组。很多算法题不能直接求解结果,需要把结果拆分为「前缀+后缀」「左状态+右状态」,分两次遍历合并结果,这是数组优化最经典的套路,可通用大量算法题型。


三、73. 矩阵置零(中等)

3.1 题目描述

给定一个 m x n 的矩阵,如果一个元素为 0,则将其所在行和列的所有元素都设为 0。要求使用原地算法,尽可能节省空间。

3.2 新手误区

新手最容易犯的错误:遍历过程中直接置零

如果一边遍历一边修改矩阵,后续遍历会把「原本正常的 0」和「我们手动置的 0」混淆,导致整表全部置 0,出现错误结果。

因此正确思路必须分为两步:先标记、后修改

3.3 核心解题思路

  1. 创建两个布尔数组,分别记录「当前行是否存在 0」「当前列是否存在 0」

  2. 第一次遍历全矩阵,完成所有行列的 0 状态标记

  3. 第二次遍历全矩阵,只要当前行/列被标记为含 0,就将当前位置置零

该思路逻辑清晰、可读性极强、无边界 bug,面试中优先推荐该写法,不易翻车。

3.4 Java 完整 AC 代码

java 复制代码
class Solution {
    public void setZeroes(int[][] matrix) {
        int m = matrix.length;
        int n = matrix[0].length;
        // 定义行、列标记数组
        boolean[] rowHasZero = new boolean[m];
        boolean[] colHasZero = new boolean[n];

        // 第一次遍历:标记所有需要置零的行和列
        for(int i = 0; i < m; i++){
            for(int j = 0; j < n; j++){
                if( matrix[i][j] == 0){
                    rowHasZero[i] = colHasZero[j] = true;
                }
            }
        }

        // 第二次遍历:根据标记原地置零
        for(int i = 0; i < m; i++){
            for(int j = 0; j < n; j++){
                if(rowHasZero[i] || colHasZero[j]){
                    matrix[i][j] = 0;
                }
            }
        }
    }
}

3.5 复杂度分析

  • 时间复杂度:O(m*n),两次矩阵遍历

  • 空间复杂度:O(m+n),使用两个标记数组,是性价比极高的解法

3.6 进阶优化思路(面试加分项)

如果题目要求极致空间优化,可以复用矩阵第一行、第一列作为标记位,可以将空间复杂度压缩至 O(1),适合高阶面试手撕代码,原理和本文思路一致,只是将外部数组改为原地标记。


四、54. 螺旋矩阵(中等)

4.1 题目描述

给你一个 m 行 n 列的矩阵 matrix,请按照顺时针螺旋顺序,返回矩阵中的所有元素。

螺旋遍历顺序:从左到右 → 从上到下 → 从右到左 → 从下到上,循环往复,逐层向内收缩。

4.2 解题难点

本题无复杂算法,难点在于边界控制与循环终止条件。很多同学代码逻辑正确,但边界判断出错,导致漏遍历、重复遍历、数组越界。

4.3 核心解题思路

定义四个动态边界:

  • 左边界 l、右边界 r

  • 上边界 t、下边界 b

四轮遍历 + 边界收缩:

  1. 左 → 右:遍历完成后,上边界下移

  2. 上 → 下:遍历完成后,右边界左移

  3. 右 → 左:遍历完成后,下边界上移

  4. 下 → 上:遍历完成后,左边界右移

每一轮遍历结束后判断边界是否交叉,交叉则说明遍历完成,直接终止循环。

4.4 Java 完整 AC 代码

java 复制代码
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

class Solution {
    public List<Integer> spiralOrder(int[][] matrix) {
        if(matrix.length == 0)
            return new ArrayList<Integer>();
        // 定义上下左右边界
        int l = 0,r = matrix[0].length-1,t = 0,b = matrix.length-1,x = 0;
        Integer[] ans = new Integer[(r+1) * (b+1)];

        while(true){
            // 1. 从左往右遍历顶层
            for(int i = l ; i <= r; i++) ans[x++] = matrix[t][i];
            if(++t > b) break;

            // 2. 从上往下遍历右侧
            for(int i = t ; i <= b ; i++) ans[x++] = matrix[i][r];
            if(--r < l) break;

            // 3. 从右往左遍历底层
            for(int i = r ; i >= l ; i-- ) ans[x++] = matrix[b][i];
            if(--b < t) break;

            // 4. 从下往上遍历左侧
            for(int i = b; i >= t; i--) ans[x++] = matrix[i][l];
            if(++l > r) break;
        }
        return Arrays.asList(ans);
    }
}

4.5 复杂度分析

  • 时间复杂度:O(m*n),每个元素仅遍历一次

  • 空间复杂度:O(1),仅边界变量,结果集合不计入额外空间

4.6 易错点总结

  • 边界收缩顺序不能乱,必须按遍历顺序对应收缩

  • 每一轮遍历结束必须判断边界交叉,否则会重复遍历内层数据

  • 单行、单列矩阵的特殊场景,依靠边界判断自动兼容,无需单独特判

五、三道算法题核心思维总结

这三道中等难度数组题,覆盖了面试最核心的三种基础算法思维,也是所有进阶算法的前提:

  1. 拆分思维(238):复杂结果拆分为左右两段,分治遍历、合并结果,规避暴力解法的缺陷。

  2. 标记思维(73):只读遍历做标记、二次遍历做修改,隔离数据读取与修改,避免数据污染。

  3. 边界模拟思维(54):用动态边界代替固定下标,适配矩阵逐层遍历场景,通用性极强。

刷题不要只背代码,要吃透通用思维模型,遇到同类变式题可以直接套思路,这才是算法刷题的真正意义。

六、刷题优化与复盘建议

  1. 数组类题目优先思考:能否一次遍历、能否空间复用、能否拆分前后缀,优先压缩时间与空间复杂度;

  2. 矩阵模拟类题目,核心永远是边界控制,代码逻辑简单,细节决定是否 AC;

  3. 刷题后务必复盘:记录易错点、优化思路、面试口述思路,做到手撕无 bug、口述逻辑清晰。

结语

中等数组题是算法分水岭,刷透基础数组与矩阵题,能够极大提升代码思维、边界处理能力与面试临场手撕能力。本文三道高频题,是校招、实习、面试必刷题库,建议大家独立手写两遍,吃透思路、摆脱题解依赖,真正做到举一反三。

相关推荐
8Qi810 小时前
LeetCode 75:颜色分类(荷兰国旗问题)—— Java 题解 ✅
java·算法·leetcode·指针·排序
888CC++12 小时前
如何在 C 语言中进行程序调试?
前端·javascript·算法
(●—●)橘子……13 小时前
力扣第503场周赛练习理解
python·学习·算法·leetcode·职场和发展·周赛
明志数科15 小时前
4D时序标注技术详解:让机器人理解连续动作的数据基础
java·算法·机器人
KaMeidebaby15 小时前
卡梅德生物技术快报|原核表达系统工艺优化:包涵体重折叠 + 分子筛纯化实现功能 RBD 高效制备,附全参数配置
前端·人工智能·算法·数据挖掘·数据分析
无限码力16 小时前
携程0510笔试真题【单数组交换】
算法·携程笔试·携程笔试真题·携程0510笔试真题
BlockWay16 小时前
WEEX Labs 周度观察:微软-OpenAI 合作调整与AI 多云趋势
大数据·人工智能·算法·安全·microsoft
风筝在晴天搁浅17 小时前
快手 CodeTop LeetCode 224.基本计算器
数据结构·算法·leetcode