这不,今天在网上看到一条帖子,真是让人忍不住笑出声来。
事情是这样的,一位网友周一早上上班太困,眼皮打架,决定偷偷溜到公司楼梯间小憩一会。谁知道,他正睡得香呢,老板出来抽烟了。老板站在楼梯口,而这位同事只能战战兢兢地蹲在楼梯角落里,生怕被抓到。结果当然是——被老板发现了!
这画面我想象得出来。有种做坏事被逮住了的尴尬。但其实偶尔一次也没有那么严重,倒不必自己吓自己。
评论区的朋友倒是很冷静,给出了“递根烟过去就行了”的高招。
确实,大大方方的反而比畏畏缩缩的更好,毕竟谁没有偶尔宕机的时候呢?
算法题:等差数列划分
今天给大家分享一个看似简单,却有点“坑”的算法题:等差数列划分。如果你也跟我一样,一开始看这题会觉得它和你平时在做的那些复杂题目比起来实在是小儿科,但它的考点和技巧却让很多人踩了不少坑。
反正我个人挺有感触的——一开始就想直接从公式入手,结果整个人被算法题的坑给卡住了。
题目分析
给定一个数组 nums
,你需要将它划分成多个连续的子序列,每个子序列都是等差数列,并且你需要返回是否可以完成这个划分。为了不让题目看起来那么抽象,举个例子。
比如说:
输入: [1, 2, 3, 4, 5, 6]
输出: true
你可以把它划分成两个子序列 [1, 2, 3]
和 [4, 5, 6]
,每个子序列都是等差数列。
再比如:
输入: [1, 2, 3, 4, 5]
输出: false
这个就不行了,因为无论你怎么划分,最后一个子序列的长度都会小于 3。
关键思路
这道题的难度并不在于你能不能发现等差数列的规律,而是在于如何通过巧妙的算法来判断这个数组是否可以满足划分要求。
最简单的想法就是:
对 nums
数组进行排序。然后通过贪心算法,逐步划分,确保每次划分出一个满足等差数列的子序列。
但有个小陷阱,如果你直接去做排序并贪心分配,你会发现这个方法往往会导致数组没有办法完成划分。你可能会在某些场景下漏掉某些子序列的组合,导致返回结果不正确。
改进方案
经过一番思考后,我发现其实可以通过使用 哈希表 来帮助我们高效地分配数字。我们用哈希表来记录每个数值能“继续”加入哪个子序列。
我们在遍历整个数组的时候,先去找当前的数是否能“继续”加入到已有的子序列中。如果能,就接着用这个子序列;如果不能,就启动一个新的子序列。
代码实现
好了,废话不多说,直接上代码:
import java.util.HashMap;
public class ArithmeticSlices {
public boolean isPossibleDivide(int[] nums, int n) {
if (nums.length % n != 0) return false;
// 首先将数组排序
Arrays.sort(nums);
// 使用两个哈希表,记录每个数字的可用子序列长度
HashMap<Integer, Integer> endCounts = new HashMap<>();
HashMap<Integer, Integer> startCounts = new HashMap<>();
// 遍历数组
for (int num : nums) {
// 如果当前的数字可以连接到已有的子序列
if (startCounts.containsKey(num)) {
// 使用这个子序列
int length = startCounts.get(num);
startCounts.put(num, length - 1); // 使用掉一个子序列
endCounts.put(num + 1, endCounts.getOrDefault(num + 1, 0) + 1); // 扩展这个子序列
} else {
// 否则,开启一个新的子序列
endCounts.put(num + 1, endCounts.getOrDefault(num + 1, 0) + 1);
}
// 每个数字最多只能在一个子序列中出现
if (endCounts.get(num) == 0) {
startCounts.put(num + 1, startCounts.getOrDefault(num + 1, 0) + 1);
} else {
endCounts.put(num, endCounts.get(num) - 1);
}
}
return endCounts.size() == 0;
}
}
解释一下代码
排序数组:首先对
nums
进行排序,确保我们能从小到大进行检查。这样能更好地保证我们分配数字时的顺序。**哈希表
endCounts
和startCounts
**:endCounts
记录当前数字接下来可以延续的子序列个数,而startCounts
记录某个数字开始时的子序列数量。我们用这两个哈希表来判断能否继续延续一个子序列或开启新子序列。贪心策略:每次遍历
nums
数组时,尝试将当前数字放到一个已有的子序列中(如果可以),否则就开始一个新的子序列。结束条件:如果没有剩下不能分配的数字,那么就可以返回
true
,否则返回false
。
对编程、职场感兴趣的同学,可以链接我,微信:coder301 拉你进入“程序员交流群”。