面试官:如何判断两个数组的内容是否相等

题目

给定两个数组,判断两数组内容是否相等。

  • 不使用排序
  • 不考虑元素位置

例:

[123] 和 [132// true
[123] 和 [124// false

思考几秒:有了😀😀

1. 直接遍历✍

  • 直接遍历第一个数组,并判断是否存在于在第二个数组中
  • 求差集, 如果差集数组有长度,也说明两数组不等(个人感觉比上面的麻烦就不举例了)
const arr1 =  ["apple""banana"1]
const arr2 =  ["apple"1"banana"]
function fn(arr1, arr2{
 // Arrary.some: 有一项不满足 返回false
 // Arrary.indexOf: 查到返回下标,查不到返回 -1
 if (arr1.length !== arr2.length) {
 return false;
 }
 return !arr1.some(item => arr2.indexOf(item)===-1)
}
fn(arr1,arr2) // true
  • 细心的小伙伴就会发现:NaN 会有问题
const arr1 =  ["apple""banana"NaN]
const arr2 =  ["apple"NaN"banana"]
function fn(arr1, arr2{
 if (arr1.length !== arr2.length) {
 return false;
 }
 return !arr1.some(item => arr2.indexOf(item)===-1)
}
fn(arr1,arr2) // false

Arrary.prototype.indexOf() 是使用的严格相等算法 => NaN值永远不相等

Array.prototype.includes() 是使用的零值相等算法 => NaN值视作相等

  • 严格相等算法: 与 === 运算符使用的算法相同
  • 零值相等不作为 JavaScript API 公开, -0和0 视作相等,NaN值视作相等,具体参考mdn文档:[1]
  • 使用includes
const arr1 =  ["apple""banana"NaN]
const arr2 =  ["apple"NaN"banana"]
function fn(arr1, arr2{
 if (arr1.length !== arr2.length) {
 return false;
 }
 return !arr1.some(item => !arr2.includes(item))
}
fn(arr1,arr2) // true

使用includes 确实可以判断NaN了,如果数组元素有重复呢?

// 重复的元素都是banana
const array1 = ["apple""banana""cherry""banana"];
const array2 = ["banana""apple""banana""cherry"];
// 或者
// 一个重复的元素是banana, 一个是apple
const array1 = ["apple""banana""cherry""banana"];
const array2 = ["banana""apple""apple""cherry"];

由上可知:这种行不通,接下来看看是否能从给数组元素添加标识入手

2. 把重复元素标识编号✍

这个简单:数组 元素重复 转换成val1, val2

function areArraysContentEqual(arr1, arr2{
 if (arr1.length !== arr2.length) {
 return false;
 }
 // 重复数组元素 加1、2、3
 const countArr1 = updateArray(arr1)
 const countArr2 = updateArray(arr2)
 /**
 * 
 * @param {*} arr 数组 元素重复 转换成val1, val2
 * @returns 
 */

 function updateArray(arr{
 const countMap = new Map();
 const updatedArr = [];
 for (const item of arr) {
 if (!countMap.has(item)) {
 // 如果元素是第一次出现,直接添加到结果数组
 countMap.set(item, 0);
 updatedArr.push(item);
 } else {
 // 如果元素已经出现过,添加带有编号的新元素到结果数组
 const count = countMap.get(item) + 1;
 countMap.set(item, count);
 updatedArr.push(`${item}${count}`);
 }
 }
 return updatedArr;
 }
 const flag = countArr1.some(item => !countArr2.includes(item))
 return !flag
}
const array1 = ["apple""banana""cherry""banana"];
const array2 = ["banana""apple""banana""cherry"];
areArraysContentEqual(array1, array2) // true
// 其实这种存在漏洞的
const array3 = ["apple""banana""cherry""banana"1'1''1' ];
const array4 = ["banana""apple""banana""cherry"'1'11];
// 应该是false
areArraysContentEqual(array3, array4) // true

因为把判断的 转为了字符串 updatedArr.push(${item}${count}) 所以出问题了

3. 统计元素次数(最终方案)✍

function areArraysContentEqual(arr1, arr2{
 if (arr1.length !== arr2.length) {
 return false;
 }
 // 创建计数对象,用于记录每个元素在数组中的出现次数
 const countMap1 = count(arr1)
 const countMap2 = count(arr2)
 // 统计数组中的元素出现次数
 function count(arr = []{
 const resMap = new Map();
 for (const item of arr) {
 resMap.set(item, (resMap.get(item) || 0) + 1);
 }
 return resMap
 }
 // 检查计数对象是否相等
 for (const [key, count] of countMap1) {
 if (countMap2.get(key) !== count) {
 return false;
 }
 }
 return true;
}
const array1 = ["apple""banana""cherry""banana"1'1''11'11];
const array2 = ["banana""apple""banana""cherry"'1'1'11'11];
areArraysContentEqual(array1, array2) // true

4. 评论区大佬方案(+1、-1)👍

  1. 只需要一个对象
  2. 遍历第一个数组就 +1
  3. 遍历第二个数组就 - 1
  4. 最后遍历对象,只要不是都是 0 就等于不匹配

这样就不需要俩个对象了,而且第二个遍历的时候如果找不到这个值的话也可以直接退出了

function areArraysContentEqual3(arr1, arr2{
 if (arr1.length !== arr2.length) {
 return false;
 }
 const countMap = new Map();
 // 计数第一个数组的元素
 for (const item of arr1) {
 countMap.set(item, (countMap.get(item) || 0) + 1);
 }
 // 比较第二个数组与计数
 for (const item of arr2) {
 const val = countMap.get(item);
 if (val === undefined || val <= 0) {
 return false;
 }
 countMap.set(item, val - 1);
 }
 return true;
}

5. 评论区大佬方案(操作第二个数组)👍

遍历第一个数组,在第二个数组找到就删除第二个数组中对应的元素,没有找到直接不等,最后再判断一下第二个数组的长度即可。实际使用中一般不直接操作原数组,浅复制一下就好

可能没有前几个性能好,但是想法还是很好的点赞~

function areArraysContentEqual2(arr1=[], arr2=[]{
 arr2 = [...arr2]
 if (arr1.length !== arr2.length) {
 return false;
 }
 arr1.some(item => {
 // 找到元素在第二个数组中的位置
 const index = arr2.findIndex(item1 => {
 if (isNaN(item) && isNaN(item1)) {
 return true
 }
 return item ===item1
 })
 if (index !== -1 ) {
 arr2.splice(index, 1)
 }
 })
 return !arr2.length
}

注意事项

这个题需要注意:

  • 先判断长度,长度不等 必然不等
  • 元素可重复
  • 边界情况考虑
    • '1' 和 1 (Object的key是字符串, Map的key没有限制)
    • NaN
    • null undefined

结语:

如果本文对你有收获,麻烦动动发财的小手,点点关注、点点赞!!!👻👻👻

因为收藏===会了

如果有不对、更好的方式实现、可以优化的地方欢迎在评论区指出,谢谢👾👾👾

关于本文

作者:程序员小易

链接:https://juejin.cn/post/7290786959441117243

最后

看完本文如果觉得有用,记得点个赞支持,收藏起来说不定哪天就用上啦~

专注前端开发,分享前端相关技术干货,公众号:南城大前端(ID: nanchengfe)

参考资料

[1]

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Equality_comparisons_and_sameness#%E9%9B%B6%E5%80%BC%E7%9B%B8%E7%AD%89

关注公众号👇👇👇

免费体验AI小工具,内置多种场景👇👇


最后不要忘了点个赞再走噢

相关推荐

  • 编写 if 时尽量不要带 else
  • java 使用mongoTemplate查询指定字段的值
  • shutdown() 、 shutdownNow() 、 awaitTermination() 区别
  • 如何使用 Promise 去控制并发请求?
  • 使用一致性哈希让数据均匀分布
  • 如何优雅的创建线程?
  • 一站式图计算平台 GraphScope,支持图分析、图的交互式查询和图学习任务
  • 使用React和Node构建实时协作的白板应用
  • 聊一聊 ReentrantLock 类的一些玩法
  • 大视觉语言模型基准数据集ReForm-Eval:新瓶装旧酒,给旧有的基准数据集换个形式就能用来评估新的大视觉语言模型
  • GPT-4V被曝离谱bug:突然执行神秘代码,空白图片读出打折信息,网友们都看呆了
  • 时序LightTS: 轻量采样的MLP结构网络
  • [开源]MIT开源协议,开箱即用的 layui vue 3.0 企业级前端模板
  • Linux 内核崩了,只因拔掉罗技的 USB 接收器.....
  • 开源联合、聚力共赢丨2023 CCF中国开源大会会议通知
  • 百亿级推送系统的容灾建设与实践
  • 如何让 ChatGPT 更好地思考,以获得更佳的输出效果
  • 1019.AI日报:Midjourney 正式发布了图像放大功能(2倍和4倍)
  • Llama 2第一、GPT-4第三!斯坦福大模型最新测评出炉
  • 上海新基建行动方案发布;传OpenAI寻求860亿美元估值;杨立昆反对过早监管AI丨AIGC大事日报