如何高效检测带前缀的连续编号列表中的缺失元素?
高效查找缺失的带s前缀连续编号
嘿,我来帮你优化这个查找缺失编号的方案!你的核心需求是在不额外创建大数组的前提下,高效找出缺失的连续编号,要做到时间复杂度O(n)、空间复杂度接近O(1)(除了存储缺失结果的空间,如果只是输出的话可以完全O(1))。
先拆解下你现有方案可以优化的点,再给出更简洁高效的实现:
优化思路
- 简化编号提取:不用
toLowerCase().split("s")这么繁琐的操作,直接用slice(1)截取s后面的部分再转成数字,既简洁又高效。 - 跟踪预期编号:从数组第一个元素的编号开始,遍历数组时对比当前元素的编号和我们预期的编号——如果不一致,说明中间存在缺失,直到预期编号追上当前元素的编号为止。这种方式不需要额外创建理想数组,空间复杂度直接是O(1)(如果只是输出缺失项,完全不需要额外存储空间)。
高效实现代码
const arr = ["s00","s01","s02","s03","s04","s05","s07","s08","s09","s10","s11","s12","s13","s14","s17","s19","s20","s21","s22","s24","s25","s26","s27","s28","s30","s32","s33","s34","s36","s38","s39","s41","s43","s44","s45","s46","s47","s48","s49","s50","s51","s52","s53","s54","s55","s56","s58","s60","s61","s62","s63","s64","s65","s67","s69","s70"]; // 提取第一个元素的编号作为初始预期值 let expected = parseInt(arr[0].slice(1), 10); for (const item of arr) { const current = parseInt(item.slice(1), 10); // 当预期编号小于当前编号时,说明中间存在缺失项 while (expected < current) { // 补零保持和原数组一致的格式 console.log(`Seems like s${expected.toString().padStart(2, '0')} is missing`); expected++; } // 预期编号追上当前编号,准备检查下一个元素 expected++; } // 如果你需要检查数组末尾之后的缺失(比如你之前设定的到s200),可以添加这段: const maxExpected = 200; while (expected < maxExpected) { console.log(`Seems like s${expected.toString().padStart(2, '0')} is missing`); expected++; }
为什么这个方案更优?
- 时间复杂度O(n):每个元素只遍历一次,缺失的编号每个也只处理一次,整体是线性时间,效率拉满。
- 空间复杂度O(1):只用到了几个变量(
expected、current),完全没有创建额外的大数组,完美符合你要的接近O(1)的要求。 - 逻辑更清晰:去掉了复杂的嵌套循环,用预期值跟踪的方式,天然就能处理连续缺失的情况,比如s15、s16这种连续缺失的项,会被逐个找出来。
另外,如果你需要把缺失的编号收集到数组里而不是直接输出,只需要把console.log换成missing.push(s${expected.toString().padStart(2, '0')})——这时候空间复杂度是O(k)(k是缺失的数量),这也是不可避免的,但原数组的处理还是保持O(1)的额外空间。
对比你之前的方案
- 你第一个方案没法处理连续缺失的情况,第二个方案虽然能解决但逻辑有点绕;这个方案用预期值跟踪的方式,直观又高效。
- 你创建理想数组的方案空间复杂度是O(m)(m是理想数组长度),对于大型列表来说非常浪费内存,这个方案完全避免了这个问题。
内容的提问来源于stack exchange,提问作者Adelin
相关产品推荐
相关产品推荐

