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

点击下方“前端开发爱好者”,选择“设为星标第一时间关注技术干货!

题目

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

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

例:

[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

最后

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

参考资料

[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

欢迎长按图片加好友,我会第一时间和你分享前端行业趋势面试资源学习途径等等。

添加好友备注【进阶学习】拉你进技术交流群

关注公众号后,在首页:

  • 回复面试题,获取最新大厂面试资料。
  • 回复简历,获取 3200 套 简历模板。
  • 回复React实战,获取 React 最新实战教程。
  • 回复Vue实战,获取 Vue 最新实战教程。
  • 回复ts,获取 TypeScript 精讲课程。
  • 回复vite,获取 Vite 精讲课程。
  • 回复uniapp,获取 uniapp 精讲课程。
  • 回复js书籍,获取 js 进阶 必看书籍。
  • 回复Node,获取 Nodejs+koa2 实战教程。
  • 回复数据结构算法,获取数据结构算法教程。
  • 回复架构师,获取 架构师学习资源教程。
  • 更多教程资源应有尽有,欢迎关注获取

相关推荐

  • 强化学习的一周「GitHub 热点速览」
  • 这些过时的前端技术请不要再继续学了!
  • 如何使用 Pinia ORM 管理 Vue 中的状态
  • 知乎热帖:为什么很多人在一家公司工作 2-3 年就会跳槽?
  • 简单有效!Direct Inversion: 三行代码提升基于Diffusion的图像编辑效果
  • 复旦大学自然语言处理实验室:如何构建和训练ChatGPT
  • 提升图神经网络性能方法综述
  • 前端程序员是怎么做物联网开发的
  • 8 城联动,1024 程序员节技术嘉年华成功举办!
  • 聚力谋发展,开源耀星城,2023 CCF中国开源大会圆满落幕
  • 一个实验性的开源项目DB-GPT:使用本地大模型与数据和环境进行交互
  • 1022.AI日报:GPT-5 可能不会比 GPT-4 好多少?
  • 国民游戏王者荣耀的地图轻量解决方案
  • [开源]基于SaaS模式的进销存+简单财务的系统,进销存多商户系统
  • 开源一个在线课程资料的管理系统开源项目,很不错
  • 这样的面试,不去也罢!
  • 什么时候都用微服务,只会害了你
  • 寻宝 AI 时代,OSC 邀你来苏州轰趴!
  • 优先展示冒牌货且定向至恶意软件,网友:是时候摆脱Google了
  • JSDoc 真能取代 TypeScript?