news 2026/9/10 6:34:25

Hot100数组题全攻略:双指针、前缀和与哈希表套路详解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Hot100数组题全攻略:双指针、前缀和与哈希表套路详解

数组算是我在力扣Hot 100这个题库里认真啃下来的第一个专题。刚开始真没当回事,觉得数组不就是for循环加下标访问,能难到哪里去?直到有一次面试,被一道“和为K的子数组”问得当场卡壳,我才意识到数组题型远没有想象中简单。后来我把Hot 100里所有数组类题目集中刷了三遍,又对着高频面试题把代码重写了无数次,才慢慢摸出一套完整的数组题解法体系。这篇文章就是我整理出来的完整笔记,从题型分类、通用套路到Java实现细节,全部拆开讲清楚。

这篇笔记的定位很明确:一是给刚开始刷Hot 100、不知道从哪里下手的Java选手踩出一条路;二是给刷过一遍但总是记不住套路、面试容易卡壳的同学做一次系统梳理。数组在Hot 100里出题密度非常高,据统计大约有三分之一题目的核心解法都建立在数组上,所以这块啃透了,后面的链表、字符串、矩阵题会轻松很多。下面我直接上干货。

1. 数组题在Hot 100中的分量与整体思路

1.1 数组题型为什么会成为面试必考

数组几乎是所有算法题的基础载体。面试官考察数组题,本质上是在考察你三个东西:对下标边界的敏感度、对空间复杂度的控制能力、以及对常见算法套路(双指针、哈希、前缀和、滑动窗口)的熟练程度。这三点恰恰是工程开发中最基础也最容易出bug的地方。

举个很简单的例子,很多人写for循环时习惯用for (int i = 0; i <= nums.length; i++),多一个等号数组就越界。这种低级错误在面试高压环境下特别容易犯,而数组题天然就是用来检验这种基本功的。

再说实际业务场景,数组本身在Java开发里太常用了,数据库查询结果集需要转数组、日志解析需要切片数组、统计报表需要对数组做聚合计算。面试官问数组题,不只是考算法,还在考察你写业务代码的底层功底。Hot 100里的数组题之所以经典,是因为每道题都能映射到一类真实问题。

1.2 先把数组题按“套路”分好类

很多同学刷Hot 100容易陷入一个误区:从头按题号一道一道刷。结果刷到第50题时,前面30题的解法早就忘光了。我的做法是先做分类,把数组题按照解法套路拆成几个大类,一类一类地啃,这样记忆才有锚点。

以Hot 100的数组题为例,我一般分成四类:

  • 查找类:典型代表是“两数之和”“三数之和”,核心思路是哈希表辅助或者排序后双指针。
  • 子数组类:典型代表是“最大子数组和”“和为K的子数组”,核心思路是动态规划、前缀和、滑动窗口。
  • 区间类:典型代表是“合并区间”,核心思路是排序加贪心。
  • 原地操作类:典型代表是“移动零”“除自身以外数组的乘积”,核心思路是快慢指针、状态标记,重点考察空间复杂度。

这个分类不是死的,比如“盛最多水的容器”既可以归为查找类也能归为双指针类,但分类的目的只是帮你建立思路索引。当你拿到一道没见过的题时,第一步就是判断它属于哪一类,然后去套那一类对应的解法模板。这个思维方式比背题重要得多。

1.3 我的专题刷题法:不要按题号硬刷

我个人的建议是,不要直接用LeetCode的题号顺序刷Hot 100,而是先按标签把所有数组题筛出来,集中一段时间只刷数组题。我第一轮就是按题号硬刷,刷到一半整个人都乱了,今天做哈希明天做回溯,知识体系压根建立不起来。

第二轮我换成“专题刷题法”:先花一天时间把数组题全部扫一遍,把每道题对应的解法标签写在表格里,然后按标签分组练习。比如今天只写双指针的题,明天只写前缀和的题,后天只写滑动窗口的题。这样做最大的好处是大脑能进入一种“模式识别”状态,同一个套路连续练五六道题之后,不用看题解也能顺手写出来。

分类表格比想象中有用。我后来把表格打印出来贴在显示器旁边,每天刷题前先看一眼,面试前也靠它快速恢复记忆。这类整理工作看起来费时间,但实际上比多刷十道题还值。

2. 高频基础方法:双指针、前缀和、哈希与滑动窗口

2.1 双指针:把O(n^2)暴力优化成O(n)的利器

双指针在数组题里的地位,就像弓箭在冷兵器时代一样,简单但好用。核心思想是维护两个指向数组不同位置的指针,根据条件移动其中一个或两个,从而在一次遍历里完成原本需要嵌套循环才能完成的比较。

以Hot 100里的“盛最多水的容器”为例,题目是给一个高度数组,找出两条线能容纳最多水的面积。暴力解法是两两组合求最大值,时间复杂度O(n^2),数据量一大直接超时。双指针的解法是从数组两端开始,每次移动高度较矮的那一侧指针,因为面积取决于较矮的板子,移动高板子只会让面积更小,所以这个移动策略是正确且高效的。

public int maxArea(int[] height) { int left = 0, right = height.length - 1; int max = 0; while (left < right) { int area = Math.min(height[left], height[right]) * (right - left); max = Math.max(max, area); if (height[left] < height[right]) { left++; } else { right--; } } return max; }

这段代码里最核心的是那行if (height[left] < height[right]),每移动一次就排除掉一类不可能成为最优解的组合。这类题的关键是分析“移动哪个指针不会丢失最优解”,想明白这一步,双指针基本就掌握了。

2.2 前缀和:连续子数组求和的第一反应

前缀和这个概念其实特别朴素:用一个新数组保存原数组从开头到当前位置的累加和。为什么有用?因为任意子数组[i, j]的和,都可以用两个前缀和相减得到,即preSum[j+1] - preSum[i]。这样一来,原本每次都要循环求和的区间块,变成了O(1)的查表运算。

我打一个不太严谨但很好懂的比方:前缀和就像记账。你只需要记住每个月底的账户余额,想知道某个月份的支出,直接用下个月的余额减去上个月的余额就行,不用再去翻每一天的消费记录。

前缀和的典型应用场景是“子数组和等于某个目标值”“子数组和最大/最小”“区域查询累计值”。Hot 100里的“和为K的子数组”就是前缀和的经典题目,但这里有个进阶技巧:单纯用前缀和数组仍然需要双重循环遍历所有区间,要真正优化到O(n),还得配合哈希表使用。关于这道题我在第三节会详细拆解。

2.3 哈希表辅助:用空间换时间的经典做法

数组题里哈希表最常见的用途就是找“之前有没有出现过某个值”。暴力做法是每到一个数就往前扫描一遍,哈希表做法是把每个已经见过的值存进Map,遇到新数时用O(1)的时间去查Map,空间开销从O(1)变成O(n),但时间从O(n^2)降到O(n)。

Java里面HashMap的底层是数组加链表加红黑树,它在算法题里几乎成为“两数之和”的标配解法。不过用哈希表有一个容易忽视的问题:key存什么、value存什么。比如两数之和里存的是“数值到下标”的映射;和为K的子数组里存的是“前缀和到出现次数”的映射。存错key或者存错value,代码逻辑直接崩。

我用哈希表时还有个经验:尽量先写getOrDefault,不要先判断containsKeyget,后者不仅代码啰嗦,还会比前者多做一次哈希操作,性能略差。两数之和解法里我没法用getOrDefault,因为要区分下标为0的情况,但很多场景下getOrDefault一行就能搞定。

2.4 滑动窗口:处理“连续区间”问题的通用框架

滑动窗口本质上也是双指针的一种,但它的特点是两个指针只向前移动,不回头,维护的区间像一个窗口在数组上滑过。它适合解决“连续子数组/子串满足某种条件”的问题,比如“长度最小的子数组”“无重复字符的最长子串”之类的变体。

滑动窗口的框架可以总结成四步:右指针不断右移扩大窗口;每次更新窗口内状态;判断窗口是否满足条件;如果不满足或为了找最优解,就移动左指针收缩窗口,同时更新状态。模板背熟之后,大部分滑动窗口题就是改改状态更新的代码。

但滑动窗口有个天然限制:窗口内的状态必须能通过“加减元素”快速更新。如果窗口收缩时状态很难维护,那就得换思路。比如“和为K的子数组”这道题,虽然也是连续子数组问题,但因为数组里可能有负数,滑动窗口的单调性被破坏,不能简单套用,必须走前缀和加哈希表的路线。所以别死记模板,要理解模板的适用前提。

3. hot100数组题实操拆解:五道必刷题目

3.1 两数之和:HashMap如何替代双重循环

两数之和几乎是所有人进入算法世界的入门题,题目不用多说:给定数组和一个目标值,找出数组中两个数之和等于目标值的下标。暴力解法是双重循环遍历每一对组合,时间复杂度O(n^2)。数据规模一大,这种解法在面试官眼里基本就是不及格。

优化思路是:遍历数组时,把已经访问过的数存进哈希表,每次处理当前数nums[i]时,直接查表看target - nums[i]是否出现过。如果出现过,就找到了答案;没出现过,就把当前数和它的下标存入Map,继续往后遍历。这样只用一次遍历,时间O(n),空间O(n)。

public int[] twoSum(int[] nums, int target) { Map<Integer, Integer> map = new HashMap<>(); for (int i = 0; i < nums.length; i++) { int need = target - nums[i]; if (map.containsKey(need)) { return new int[]{map.get(need), i}; } map.put(nums[i], i); } return new int[]{-1, -1}; }

这里有个细节:先把target - nums[i]算出来放在变量里再查表,不要直接在if条件里写map.containsKey(target - nums[i]),虽然功能一样,但后者会让调试时看不清到底存的是什么值,代码整洁度也差一些。返回值固定用new int[]{},不要先声明一个数组再手动赋值,后者多写两行还容易漏初始化。这道题我面试时被问过至少五次,每次都会附带一个追问:如果数组有序,能不能不用哈希表?答案是用双指针,从两端向中间逼近,空间复杂度可以降到O(1)。

3.2 移动零:快慢指针的现场教学

移动零的题目描述非常朴素:把数组里的所有0移动到末尾,同时保持非零元素的相对顺序。难点在于题目的额外要求:必须原地操作,不能复制数组。很多人的第一反应是新建一个数组,把非零元素放进去,再把后面填0,但这个做法空间复杂度是O(n),不满足要求。

快慢指针在这里非常好用。慢指针指向下一个应该放置非零元素的位置,快指针负责往前扫描找非零元素。每当快指针遇到非零元素,就和慢指针指向的位置交换,然后慢指针前进一位。这样一轮遍历结束后,所有非零元素都被换到数组前面,0自然被挤到末尾。

public void moveZeroes(int[] nums) { int slow = 0; for (int fast = 0; fast < nums.length; fast++) { if (nums[fast] != 0) { int tmp = nums[slow]; nums[slow] = nums[fast]; nums[fast] = tmp; slow++; } } }

注意我写的是交换,而不是“把非零元素赋值到前面再把原位置改成0”。交换的好处是操作逻辑简单,也利于扩展到其他题目。有一个边界情况容易踩坑:整个数组全是非零元素时,快慢指针始终指向同一个位置,交换相当于自己和自己换,没问题;但如果你用赋值加置0的写法,就得多加一个判断,否则会把没遍历到的数据覆盖掉。平时刷题我喜欢把这类“看起来没坑”的小题也认真写一遍,因为正式面试时手写代码很容易暴露这些细节。

3.3 最大子数组和:动态规划在数组题里的代表作

最大子数组和要求找出数组中的一个连续子数组,使得它们的和最大。比如[-2,1,-3,4,-1,2,1,-5,4]的最大子数组是[4,-1,2,1],和为6。这个看似简单的问题,暴力解法要枚举所有起点和终点,O(n^2),数据规模一大就废了。

正确解法用动态规划思想,关键定义是:pre表示以当前元素结尾的最大子数组和。状态转移方程特别简洁:pre = Math.max(pre + x, x)。意思是,要么把当前元素接在前面的子数组后面,要么从当前元素重新开始。为什么需要这个选择?因为如果前面的累加和是负数,接上当前元素只会拖累结果,不如从当前元素重新开头。

public int maxSubArray(int[] nums) { int pre = 0; int max = nums[0]; for (int x : nums) { pre = Math.max(pre + x, x); max = Math.max(max, pre); } return max; }

这里有一个经典的初始化坑:max的初始值不能设成0,因为如果数组里全是负数,正确答案会是一个负数。用nums[0]初始化就不会出问题。我见过很多人在这道题上栽跟头,不是状态转移方程不会写,而是初始化不严谨。另外这个状态转移其实只用了一个变量,不需要额外开数组,属于典型的滚动变量优化。

3.4 合并区间:排序后贪心合并的关键

合并区间是一类很实际的题目,可以理解为把多个时间区间合并成不重叠的完整区间。比如[[1,3],[2,6],[8,10],[15,18]],因为[1,3][2,6]有重叠,合并成[1,6],最终结果是[[1,6],[8,10],[15,18]]

解法的第一步几乎没有任何悬念:按区间左端点排序。排序之后,重叠的区间在位置上一定是相邻的,这为后续的合并提供了极大便利。然后遍历每个区间,维护一个当前合并区间。如果新区间的左端点大于当前合并区间的右端点,说明两者不相交,把当前合并区间加入结果,开始一个新的合并区间;否则说明有重叠,更新当前合并区间的右端点为较大值。

public int[][] merge(int[][] intervals) { Arrays.sort(intervals, (a, b) -> a[0] - b[0]); List<int[]> res = new ArrayList<>(); for (int[] cur : intervals) { if (res.isEmpty() || cur[0] > res.get(res.size() - 1)[1]) { res.add(cur); } else { int[] last = res.get(res.size() - 1); last[1] = Math.max(last[1], cur[1]); } } return res.toArray(new int[res.size()][]); }

这段代码有个很tricky的地方:当没有重叠时,res.add(cur)不是复制区间,而是直接存了原数组的引用,后续修改cur会不会影响结果?这里不会,因为进入不重叠分支的cur在下一轮迭代里会被指向新的区间对象,之前的cur不会再被修改。但如果觉得这样不够保险,可以new int[]{cur[0], cur[1]}后再add,代码长度差不多,可读性更好。另外注意排序时用(a, b) -> a[0] - b[0]可能int溢出,虽然区间端点一般不会出现极端值,但严谨一点可以写成Integer.compare(a[0], b[0])

3.5 和为K的子数组:前缀和与哈希表的组合应用

这道题是我印象最深的一道,也是我开头说的面试翻车现场。题目是求数组中和为k的连续子数组的个数。比如[1,2,3]k=3,答案是2,因为[1,2][3]都满足。

第一反应很容易想到滑动窗口,但这道题有个陷阱:数组元素可能包含负数,窗口扩大时和不一定变大,缩小时也不一定变小,滑动窗口的两个指针没法保持单调移动,所以此路不通。正确解法是前缀和加哈希表。

思路是这样的:遍历数组时维护一个累计和sum,对于当前位置i,我们想知道有多少个以i结尾的子数组和为k。子数组的区间是[j, i],对应的和为sum(i) - sum(j-1)。想让它等于k,就要知道前面有多少个位置的前缀和等于sum - k。所以用一个Map记录每个前缀和出现的次数,每次累加答案。

public int subarraySum(int[] nums, int k) { Map<Integer, Integer> prefixCount = new HashMap<>(); prefixCount.put(0, 1); int sum = 0, count = 0; for (int num : nums) { sum += num; count += prefixCount.getOrDefault(sum - k, 0); prefixCount.put(sum, prefixCount.getOrDefault(sum, 0) + 1); } return count; }

初始化时为什么要put(0, 1)?因为如果某个位置的前缀和恰好等于k,说明从数组开头到该位置的子数组本身就是答案,计算时sum - k = 0,需要能从Map里查到0出现过一次。这个初始值漏掉的话,这类子数组就被漏算了。我第一遍刷这道题时就是漏了这一步,答案总是差几个数,排查了很久才发现。这种细节不踩一次坑,光看题解是真的很难注意到。

4. Java数组的工程细节与高频踩坑

4.1 数组与集合互转的几个大坑

算法题里经常要把数组转成List方便操作,或者把List转回数组作为返回值。这中间有太多隐藏的坑,我一个个说。

第一个坑:Arrays.asList返回的List是定长的。很多人以为asList出来的集合和new ArrayList一样可以随便add和remove,但实际上它返回的是Arrays内部的一个固定长度视图,底层还是原数组,调用add会直接抛UnsupportedOperationException。如果后续代码里有增删操作,一定要这样写:new ArrayList<>(Arrays.asList(arr))

第二个坑:基本类型数组不能直接用asList。比如int[],在泛型推断时int[]被当成一个整体对象,asList返回的List<int[]>里只有一个元素,长度是1而不是数组长度。想要把int[]转成List<Integer>,常见做法是用Java 8 Stream:Arrays.stream(nums).boxed().collect(Collectors.toList())。这个方法用起来还行,但要注意boxed()不可省,否则流里的类型还是IntStream

第三个坑:List转数组时,toArray()无参版本返回的是Object[],并不是Integer[]。想要正确得到对应类型数组,需要传入一个类型正确的空数组:list.toArray(new Integer[0])。在算法题里,二维数组的构造也常用toArray(new int[res.size()][]),这种方式比较高效。

4.2 Arrays工具类:排序、二分、拷贝的正确用法

Java的Arrays工具类在算法题里使用频率极高,但很多人只停留在Arrays.sort这一招上。实际上,数组拷贝、二分查找、填充操作在特定场景下能极大简化代码。

Arrays.sort既能直接排序基本类型数组,也能通过传入比较器排序对象数组。二维数组按某一列排序是算法题高频操作,写法是Arrays.sort(intervals, (a, b) -> Integer.compare(a[0], b[0]))。注意Integer.compare比直接相减更安全,避免溢出。

Arrays.binarySearch是二分查找的现成实现,但返回值设计得很反直觉:找到了返回下标,没找到返回-(插入点) - 1。很多人用的时候忘记处理负数,直接拿来当下标用,结果数组越界。我一般只在数组已排序且能确定元素必然存在时才会用binarySearch,否则还是老老实实自己写二分。

Arrays.copyOfSystem.arraycopy是数组扩容和拷贝的底层实现,ArrayList的扩容就是靠System.arraycopy完成的。刷题时如果遇到需要“取数组前n个元素”的场景,Arrays.copyOfRange(nums, start, end)比手写循环清爽很多。

4.3 笔试场景下的输入输出与初始化问题

在真正的笔试环境中,很多题目给的输入不是函数参数,而是标准输入流里的字符串。这时候考验的就是字符串解析和数组初始化的基本功了。题目输入通常是[1,2,3,4],需要自己把中括号去掉,按逗号split,然后逐个parse成int。

我用得比较多的一种解析方式是:先用replace把中括号等无关字符处理掉,再以逗号分割,循环赋值。比如String[] parts = input.replace("[", "").replace("]", "").split(","),然后int[] arr = new int[parts.length],循环里Integer.parseInt(parts[i].trim())。这道工序看起来琐碎,但在笔试里至少能占3到5分钟,提前写好模板能节省大量时间。

初始化方面有个很常见的bug:定义最大最小值时,一定要初始化为Integer.MIN_VALUEInteger.MAX_VALUE,不能随手写成0。计算最大子数组和时,max初始化成0会漏掉全负数的情况;计算最小差值时,min初始化成0会导致结果永远是0。我建议在本地编辑器里把这几种初始化场景存成代码片段,写的时候直接调。

还有一个初学阶段特别容易忽略的问题:引用类型数组的默认值是null。比如String[] strArr = new String[5],里面全是null,直接调用strArr[0].length()会抛NullPointerException。我刚实习的时候写业务代码就犯过这个错,后来养成了一个习惯:声明完数组后,先想想里面存的是基本类型还是引用类型,如果是引用类型,检查是否每个元素都初始化了。

4.4 把“代码整洁度”练进肌肉记忆

面试手写代码和平时在IDE里写代码是完全不同的体验。没有自动补全,没有编译提示,甚至没有语法高亮,代码整洁度直接影响面试官对你的印象分。我在刷题时有几个固定的整洁度要求:大括号不省略,函数统一用public修饰,变量名用有含义的词而不是abtmp乱写,每个方法控制在30行以内。

还有一个容易被忽视的细节:边界条件统一放在方法开头处理。比如数组为空或者长度小于2时应该怎么做,先想清楚再写主逻辑。我在两数之和的解法里就有一个冗余写法return new int[]{-1, -1},这就是为了应对“没找到答案”的情况。虽然按题意不会发生,但写上之后代码是完整自洽的。面试官看到这种防御式写法,好感度会明显上升。

5. 常见问题与排查技巧实录

5.1 数组题最容易翻车的6个运行时报错

数组题出错几乎都集中在几个固定模式上,我把最常见的6类错误整理成一张速查表,没事翻一翻能省很多调试时间。

错误类型典型触发场景解决思路
ArrayIndexOutOfBoundsException循环边界多写等号、访问i+1打印i和数组长度,检查边界条件
NullPointerException引用类型数组元素未初始化初始化数组时逐元素赋值或填充数组
UnsupportedOperationExceptionArrays.asList后调用add/remove用new ArrayList包裹
ConcurrentModificationException遍历时修改List结构用迭代器或收集到新列表再处理
NumberFormatException从字符串解析整数时包含空格trim后再parse
整型溢出累加和超过Integer范围改用long记录中间结果

第5条我特别有感触,笔试里很多输入带空格,比如"1, 2, 3",split后每个元素前后可能都有空格,直接Integer.parseInt就会抛异常。所以我在所有parse之前都会先trim(),这个习惯已经刻进肌肉记忆了。

5.2 面试官喜欢追问的5个方向

数组题大部分都有明显的“追问链”:你给出一个解法后,面试官会逐步加条件,看你能否举一反三。我总结过一轮高频追问,写下来供参考。

第一个追问方向是空间复杂度。比如“两数之和”你用了HashMap,面试官会问能不能不用额外空间。这时数组有序可以用双指针降到O(1)空间,无序则要先排序再双指针,时间复杂度变成O(n log n)。

第二个追问方向是数组是否有序。很多题默认数组无序,一旦加了这个条件,解法可能完全不同。例如“两数之和”变得简单,“三数之和”可以配合双指针直接去除HashMap。

第三个追问方向是有没有负数。“和为K的子数组”如果有负数就不能用滑动窗口,面试官会专门选这种边界情况来测试你对算法适用条件的理解。回答“因为负数破坏了滑窗的单调性”比直接写代码更显功力。

第四个追问方向是数据规模。数据量在几千时O(n^2)问题不大,到了百万级别就必须O(n log n)甚至O(n)。面试官可能拿“最大子数组和”问你,数据量大到内存装不下怎么办,这就涉及分治和流式处理了。

第五个追问方向是变体应用题。把“移动零”改成“把奇数移到前面偶数移到后面”,或者把“合并区间”改成“插入一个新区间并合并”,这些变体看着新鲜,但底层套路一样,练熟了基本就是换皮。

5.3 我自己的诊断思路:定位Bug从哪开始

刷数组题遇到Bug时,我最不推荐的做法是盯着代码反复读。人眼对熟悉的代码有很强的“脑补”能力,越看越觉得自己写的没错。我的经验是:先用最小用例跑一遍,把每次循环的变量值打印出来,跟手算结果对比。

比如处理“移动零”时,我遇到过一次结果不对的情况,就在交换前后打印slowfast和整个数组,立刻发现是慢指针没有在交换后递增。这个错误用肉眼盯着代码其实很容易发现,但在高压环境下经常被忽略。打印调试法看似笨,其实是定位这类逻辑错误最快的方式。

另一个技巧是“增量验证”:不要等整道题全写完再跑,写到一个关键节点就分步验证一次。比如“合并区间”里,先只写排序并打印结果,确认排序正确后再写合并逻辑。分段调试虽然多花几分钟,但能大幅降低整段代码出错后无处下手的概率。

6. 刷完这一轮,我总结出的三条学习建议

第一,一定要按套路分组刷题,不要按题号顺序硬刷。我自己用“专题刷题法”建立分类表格后,复习效率提升了不止一倍。每道题除了记录解法标签,我还额外写了一句“这道题坑在哪”,比如“和为K的子数组要初始化prefixCount.put(0,1)”,“合并区间注意排序规则”。这样的备注才是属于自己的原创题解,比复制粘贴官方题解有价值得多。

第二,每道题写完代码后,试着自己从头讲一遍解题思路。能讲清楚,说明真的懂了;讲不清楚或者讲到一半卡住,说明理解还处在“背模板”阶段。这个办法我一开始也觉得挺麻烦,但坚持几周后发现,面试时被问到解题原理,我的反应速度比之前快了很多。

第三,代码一定要达到“能跑、能改、能背”三个标准。“能跑”指提交通过,“能改”指面对面试官改变条件时能快速调整代码,“能背”不是死记硬背,而是能不看题解直接从零手写核心片段。Hot 100的数组题刷到这里,我最大的体会是:算法题的进步不是线性的,而是平台期之后突然跳一跳。前面几十道题刷得想吐,最后一轮整理笔记时才发现,好多题目之间根本就是同一个套路换了个故事背景,数组题也是这样,摸透了它们,后面的链表和矩阵题就会顺很多。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/10 6:34:02

购物商城APP源码解读:从Android Studio导入到答辩演示全流程

简介&#xff1a;面向毕业设计和大作业场景的Android购物商城APP完整源码&#xff0c;基于Android Studio开发&#xff0c;覆盖注册登录、修改密码、重置密码&#xff08;邮箱验证&#xff09;、商品详情加载、购物车、个人信息修改等功能模块&#xff0c;适合正在深入学习Andr…

作者头像 李华
网站建设 2026/9/10 6:33:20

CANN/GE性能剖析特性介绍

GE Profiling 特性介绍 【免费下载链接】ge GE&#xff08;Graph Engine&#xff09;是面向昇腾的图编译器和执行器&#xff0c;提供了计算图优化、多流并行、内存复用和模型下沉等技术手段&#xff0c;加速模型执行效率&#xff0c;减少模型内存占用。 GE 提供对 PyTorch、Ten…

作者头像 李华
网站建设 2026/9/10 6:32:34

AI文本去AI味:humanizer五层改造法,让机器写作拥有真人感

上周帮朋友看一篇品牌推文&#xff0c;他拍着胸脯说“这版绝对看不出是 AI 写的&#xff0c;我还专门让人性化处理过”。我读完前两段就乐了&#xff1a;结构是标准的“痛点—方案—升华”三段式&#xff0c;每一段都用“在……的今天”开头&#xff0c;三个排比句举例&#xf…

作者头像 李华
网站建设 2026/9/10 6:30:06

AI编程助手Skills从入门到实战:告别重复提示词,封装可复用技能

那段时间我快被自己蠢哭了。明明给 AI 编程助手写了一大堆“规则”&#xff0c;每次开新会话都得把同样的话粘贴一遍&#xff0c;结果它该犯的错一个没少。直到我把目光转向“Skills”这个词&#xff0c;才意识到问题不在提示词长度&#xff0c;而在我一直在用最笨的方式跟 AI …

作者头像 李华
网站建设 2026/9/10 6:28:32

Composio CLI 安装完全指南:一键脚本、Shell 配置、校验与卸载

Composio CLI 安装完全指南&#xff1a;一键脚本、Shell 配置、校验与卸载 【免费下载链接】composio Composio powers 1000 toolkits, tool search, context management, authentication, and a sandboxed workbench to help you build AI agents that turn intent into actio…

作者头像 李华
网站建设 2026/9/10 6:28:17

电视端高清观影实操指南:设备选型、画质调优与字幕音轨全攻略

家里电视吃灰很久了吧&#xff1f;别急着怪电视剧不好看&#xff0c;八成是观影姿势不对。这篇东西不讲虚的&#xff0c;就把电视端观影从设备、软件、片源、画质增强到字幕音轨这些环节掰开揉碎&#xff0c;全是实操。先说说这篇内容覆盖什么&#xff1a;智能电视和电视盒子上…

作者头像 李华