获取数组未使用随机索引时触发Maximum call stack size exceeded错误
问题原因分析
你的代码触发“Maximum call stack size exceeded”错误,核心问题有两个:
- 递归调用未返回有效结果:当生成的索引已被使用时,你递归调用
getRandomIndex()但没有将递归得到的正确结果返回给上层调用。这会导致上层函数依然返回最初生成的重复索引,且随着已使用索引占比越来越高,递归次数会急剧增加,最终撑爆调用栈。 - 缺少边界终止条件:当
usedIndexes长度等于数组长度时(所有索引已被耗尽),函数会无限递归寻找不存在的未使用索引,直接触发栈溢出。
另外,你的随机索引生成代码Math.floor(Math.random() * (arr.length - 0)) + 0可简化为Math.floor(Math.random() * arr.length),效果完全一致。
修复方案一:修正递归逻辑
const arr = [81, 33, 45, 22, 97, 19, 60]; const usedIndexes = []; function getRandomIndex() { // 提前判断是否所有索引已被使用,避免无限递归 if (usedIndexes.length === arr.length) { return -1; // 可根据需求改为抛出错误或其他处理 } const rndIdx = Math.floor(Math.random() * arr.length); if (usedIndexes.includes(rndIdx)) { // 递归调用时必须返回结果 return getRandomIndex(); } else { usedIndexes.push(rndIdx); return rndIdx; } } for(let i = 0; i < arr.length; i++) { console.log(getRandomIndex()); }
修复方案二:使用洗牌算法(更高效)
递归的“拒绝采样”方式在数组较大时效率极低,更优方案是采用Fisher-Yates洗牌算法直接打乱索引序列,无需重复检测和递归:
const arr = [81, 33, 45, 22, 97, 19, 60]; // 生成初始索引数组 const indexes = arr.map((_, idx) => idx); // Fisher-Yates洗牌打乱顺序 for (let i = indexes.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); [indexes[i], indexes[j]] = [indexes[j], indexes[i]]; } // 依次取出打乱后的索引,即随机不重复的结果 for (const idx of indexes) { console.log(idx); }
该方案时间复杂度为O(n),彻底避免栈溢出问题,同时效率远高于递归方式。
内容的提问来源于stack exchange,提问作者TToprak1
相关产品推荐
相关产品推荐

