JavaScript数组配对计数时for循环内为何要二次递增索引i?
JavaScript数组配对计数问题的循环索引逻辑疑问
我正在学习JavaScript编程,至今已有6个月学习时长,目前通过练习Codewars题目锻炼算法逻辑能力。
在处理Codewars题目 Find All Pairs 时,我最初设计的逻辑思路如下:
初始化配对计数变量 初始化排序后的数组变量 循环遍历数组 设置初始索引为0 当索引小于传入数组长度时 递增索引 如果当前遍历项和索引+1位置的项值相等 配对计数变量+1 返回计数变量
对应实现代码为:
function duplicates(array) { let pairResult = 0; let sorrtedArray = array.sort(); for (let index = 0; index < sorrtedArray.length; index++) { if (sorrtedArray[index + 1] === sorrtedArray[index]) { pairResult += 1; index++ console.log(index); } } return pairResult; }
两个测试用例的运行输出如下:
console.log(duplicates([1, 2, 5, 6, 5, 2])) ====> 2console.log(duplicates([1, 2, 2, 20, 6, 20, 2, 6, 2])); ====> 5
我发现该代码存在计数错误,数字2被重复计数了。随后我查阅了题解,找到一份和我的实现逻辑几乎一致、可正确运行的代码:
function duplicates(array){ // 实现逻辑 const newArray = array.sort((a,b) => a-b); if (newArray.length <= 1) return 0; let count = 0; for (let i = 0; i < newArray.length ; i++) { if (newArray[i] == newArray[i+1]) { count++; i++; } } return count; }
我的疑问是:for循环的迭代声明中已经设置了i++来递增索引,为何在if判断匹配到成对元素时,还要在循环体内额外执行一次i++操作?
解答
这个写在if判断里的额外i++,作用就是跳过已经凑成对的元素,防止重复计数。
先理清楚JavaScript中for循环的固定执行顺序:
- 执行循环初始化语句(比如例子中的
let i = 0,仅在循环启动时执行一次) - 判断循环终止条件是否成立,不成立直接退出循环
- 完整执行循环体内的所有代码
- 执行循环头里写的递增语句(也就是你提到的循环声明里的
i++) - 回到第二步重复判断
也就是说,循环头里的i++是每轮循环跑完整个循环体之后才会执行的。
拿第二个测试用例举例,正确按数值升序排序后的数组是[1,2,2,2,2,6,6,20,20],顺着流程走就能明白跳步的必要性:
- 当i走到索引1的位置,值是2,和i+1位置(索引2)的2相等,说明找到一对,计数+1。这时候如果不手动加一次
i++,本轮循环结束后循环头会把i加到2,下一轮就会判断索引2和索引3的两个2——但索引2的2已经和索引1的2凑过对了,这时候再算就会重复计数,四个连续的2本来只能凑2对,这么写就会算出3对的错误结果。 - 要是在if里手动加一次
i++,匹配成功后i会先变成2,等本轮循环结束,循环头的i++再把i加到3,相当于直接跳过了已经配对过的索引2的位置,下一轮直接从索引3开始判断,四个连续的2就只会被算成2对,结果就正确了。
另外你自己写的代码出问题有两个核心原因:
- 直接调用
array.sort()不传比较函数的时候,JS会默认把元素转成字符串再按Unicode编码排序,数字排序结果会错乱,比如20会排到2前面,这是第二个用例结果错得离谱的主要原因 - 虽然你也在if里加了
index++,但因为排序结果不对,跳步逻辑自然也就无法正确生效
内容的提问来源于stack exchange,提问作者DanielG
相关产品推荐
相关产品推荐

