如何优化双字符数组共通元素保留及删除次数统计的冗余代码?
嘿,我来帮你搞定这个代码优化的问题,顺便把你不熟悉的reduce和map用法讲明白~
首先明确核心需求:要让两个字符数组元素一致,需要删除的总次数 = 两个数组的总长度 - 2 × 两个数组的公共元素总数(每个公共元素在两边都不用删,剩下的都要删掉)。
第一步:解决基础场景(无重复元素)
如果数组里没有重复元素,我们可以用filter快速找出公共元素,再计算结果:
function countDeletions(arr1, arr2) { // 筛选出arr1中同时存在于arr2的元素 const commonElements = arr1.filter(item => arr2.includes(item)); // 总删除次数 = 总长度 - 2倍公共元素数 return arr1.length + arr2.length - 2 * commonElements.length; }
测试示例:countDeletions(["a","b"], ["a","b","c","d","e"]) // 输出3,完全符合预期。
但这个方案有个问题:如果数组里有重复元素(比如arr1=["a","a"], arr2=["a","b"]),它会错误计算公共元素数,这时候我们需要统计元素的出现频率。
第二步:处理重复元素(用reduce统计频率)
reduce是用来累积数组元素生成单一结果的神器,最适合做频率统计。先写一个用reduce生成频率表的函数:
// 生成元素频率映射表:{元素: 出现次数} function getFrequencyMap(arr) { return arr.reduce((frequencyMap, currentItem) => { // 如果当前元素已在映射表中,次数+1,否则设为1 frequencyMap[currentItem] = (frequencyMap[currentItem] || 0) + 1; // 返回更新后的映射表,作为下一次循环的累积值 return frequencyMap; }, {}); // 初始值是空对象,作为第一次循环的frequencyMap }
这里给你拆解reduce的工作流程:
- 第一个参数是回调函数,接收两个核心参数:
frequencyMap(累积的结果,第一次是初始值{})和currentItem(当前遍历的数组元素)。 - 每次循环更新映射表的计数,最后返回这个表,最终得到整个数组的频率统计。
有了频率表,我们就可以计算两个数组的真实公共元素总数(考虑重复次数):
function countDeletions(arr1, arr2) { const freq1 = getFrequencyMap(arr1); const freq2 = getFrequencyMap(arr2); let commonCount = 0; // 遍历其中一个频率表,取两个表中元素出现次数的最小值累加 for (const key in freq1) { if (freq2[key]) { commonCount += Math.min(freq1[key], freq2[key]); } } return arr1.length + arr2.length - 2 * commonCount; }
测试重复元素场景:countDeletions(["a","a"], ["a","b"]) // 输出2,正确(arr1删1个a,arr2删1个b,总删除2次)。
第三步:进一步优化(用一次reduce完成统计)
我们还可以省略第二个频率表,直接用reduce遍历第二个数组,同时消耗第一个频率表的计数,更高效:
function countDeletions(arr1, arr2) { // 先统计arr1的元素频率 const freqMap = arr1.reduce((map, item) => { map[item] = (map[item] || 0) + 1; return map; }, {}); // 遍历arr2,统计公共元素总数 const commonCount = arr2.reduce((count, item) => { // 如果当前元素在arr1还有剩余次数,计数+1,同时减少频率表的次数 if (freqMap[item] > 0) { count++; freqMap[item]--; } return count; }, 0); // 初始计数为0 return arr1.length + arr2.length - 2 * commonCount; }
这个版本只用了两次reduce,没有额外的循环,代码更简洁,效率也更高。
关于map的补充说明
你提到对map不熟悉,它的作用是把数组的每个元素转换成新元素,返回一个新数组,比如:
const arr = ["a", "b", "c"]; const upperArr = arr.map(item => item.toUpperCase()); // 输出["A", "B", "C"]
如果用map做频率统计,效率会很低(需要嵌套filter),所以这类统计场景reduce是更好的选择。
测试用例验证
- 无公共元素:
countDeletions(["x","y"], ["a","b"]) // 输出4 - 完全相同数组:
countDeletions(["a","b","c"], ["a","b","c"]) // 输出0 - 多重复元素:
countDeletions(["a","a","b"], ["a","b","b"]) // 输出2(最终可以是["a","b"],删1个a和1个b)
内容的提问来源于stack exchange,提问作者Martin Muldoon

