【力扣】2722. 根据 ID 合并两个数组

【力扣】2722. 根据 ID 合并两个数组

文章目录

题目

现给定两个数组 arr1arr2 ,返回一个新的数组 joinedArray 。两个输入数组中的每个对象都包含一个 id 字段。

joinedArray 是一个通过 idarr1arr2 连接而成的数组。joinedArray 的长度应为唯一值 id 的长度。返回的数组应按 id 升序 排序。

如果一个 id 存在于一个数组中但不存在于另一个数组中,则该对象应包含在结果数组中且不进行修改。

如果两个对象共享一个 id ,则它们的属性应进行合并:

  • 如果一个键只存在于一个对象中,则该键值对应该包含在对象中。
  • 如果一个键在两个对象中都包含,则 arr2 中的值应覆盖 arr1 中的值。

示例 1:

复制代码
输入:
arr1 = [
    {"id": 1, "x": 1},
    {"id": 2, "x": 9}
], 
arr2 = [
    {"id": 3, "x": 5}
]
输出:
[
    {"id": 1, "x": 1},
    {"id": 2, "x": 9},
    {"id": 3, "x": 5}
]
解释:没有共同的 id,因此将 arr1 与 arr2 简单地连接起来。

示例 2:

复制代码
输入:
arr1 = [
    {"id": 1, "x": 2, "y": 3},
    {"id": 2, "x": 3, "y": 6}
], 
arr2 = [
    {"id": 2, "x": 10, "y": 20},
    {"id": 3, "x": 0, "y": 0}
]
输出:
[
    {"id": 1, "x": 2, "y": 3},
    {"id": 2, "x": 10, "y": 20},
    {"id": 3, "x": 0, "y": 0}
]
解释:id 为 1 和 id 为 3 的对象在结果数组中保持不变。id 为 2 的两个对象合并在一起。arr2 中的键覆盖 arr1 中的值。

示例 3:

复制代码
输入:
arr1 = [
    {"id": 1, "b": {"b": 94},"v": [4, 3], "y": 48}
]
arr2 = [
    {"id": 1, "b": {"c": 84}, "v": [1, 3]}
]
输出: [
    {"id": 1, "b": {"c": 84}, "v": [1, 3], "y": 48}
]
解释:具有 id 为 1 的对象合并在一起。对于键 "b" 和 "v" ,使用 arr2 中的值。由于键 "y" 只存在于 arr1 中,因此取 arr1 的值。

提示:

  • arr1 和 arr2 都是有效的 JSON 数组
  • 在 arr1 和 arr2 中都有唯一的键值 id
  • 2 <= JSON.stringify(arr1).length <= 106
  • 2 <= JSON.stringify(arr2).length <= 106

解决方案

概述

我们需要基于它们的 "id" 键合并两个数组 arr1 和 arr2。合并后的数组 joinedArray 应包含来自两个数组的所有唯一对象,并按其 id 值的升序排序。当对象具有相同的 id 时,它们的属性应合并,arr2 中的值将覆盖 arr1 中的值。如果一个 id 只存在于一个数组中,那么该 id 对应的对象应无修改地包含在结果中。

一些实际应用场景:

  • 数据集成: 在集成来自多个来源的数据时,每个来源可能使用单独的数组提供具有公共 ID 的数据。根据 ID 合并这些数组允许将数据组合和整合到一个单一的数据集中,以供分析或处理。
  • 社交媒体分析: 在分析社交媒体数据时,根据用户或帖子 ID 合并数组可以汇集相关信息,如用户资料、评论、点赞和分享。这使得可以进行综合分析、情感分析、识别热门帖子或发现用户行为中的模式。
  • 地理信息系统(GIS): 在 GIS 应用中,基于位置 ID 或地理标识符合并数组可以实现空间数据的集成。这可能涉及合并包含地理特征、属性和其他相关信息的数组,从而促进空间分析、制图和决策制定。
  • 供应链管理: 在供应链系统中,基于唯一标识符(如产品代码或订单 ID)合并数组允许跟踪和管理货物或服务的流动。合并数组有助于整合供应链不同阶段的信息,如采购、生产、分销和交付,从而实现有效的监控和优化。

方法 1:暴力法

  1. 我们首先创建一个新数组 combinedArray,通过组合 arr1 和 arr2 的内容来确保包含来自两个数组的所有对象。
  2. 接下来,我们初始化一个名为 merged 的空对象。该对象将用作容器,以存储基于其 ID 作为键的合并对象。
  3. 然后,我们使用 forEach 方法迭代 combinedArray 中的每个对象。对于每个对象,我们检查它的 ID 是否已存在于 merged 对象中作为键。
  4. 如果 ID 在 merged 对象中不存在,我们将向 merged 对象添加一个新的键值对。键是 ID,值是通过创建当前对象的副本 (...obj) 而生成的新对象。这确保了 merged 对象中的每个对象都有自己独立的副本。
  5. 但是,如果 ID 已经存在于 merged 对象中,这意味着已经存在具有相同 ID 的另一个对象。在这种情况下,我们执行属性的合并。我们通过复制其属性 (...merged[id]) 并然后用当前对象的属性 (...obj) 覆盖它们来更新 merged 中的现有对象。这确保了合并过程中 arr2 中的属性优先于 arr1。
  6. 在合并所有对象之后,我们使用 Object.values()从 merged 对象中提取值。这会创建一个数组 joinedArray,其中只包含已合并对象,而不包含 ID 键。
  7. 最后,我们使用基于 ID 键的升序对 joinedArray 进行排序。我们使用 sort 方法和比较函数 (a, b) => a.id - b.id 来确定正确的排序顺序。比较函数比较两个对象的 ID 并确定它们在排序数组中的位置。
  8. 最终,我们将排序后的 joinedArray 作为 join 函数的最终结果返回。
实现
js 复制代码
/**

 * @param {Array} arr1
 * @param {Array} arr2
 * @return {Array}
   */
   var join = function(arr1, arr2) {
     const combinedArray = arr1.concat(arr2);
     const merged = {};

  combinedArray.forEach((obj) => {
    const id = obj.id;
    if (!merged[id]) {
      merged[id] = { ...obj };
    } else {
      merged[id] = { ...merged[id], ...obj };
    }
  });

  const joinedArray = Object.values(merged);
  joinedArray.sort((a, b) => a.id - b.id);

  return joinedArray;
};
复杂度分析
  • 时间复杂度:由于排序函数的存在,时间复杂度为 O(nlogn),其中 n 是 combinedArray 中的元素总数(arr1 的长度加上 arr2 的长度)。迭代和合并过程也会对时间复杂度有所贡献,但排序操作占主导地位。
  • 空间复杂度:空间复杂度为 O(n),其中 n 是 combinedArray 中的元素总数。这是因为创建了一个新数组(combinedArray)和一个新对象(merged),每个都可能存储来自 arr1 和 arr2 的所有元素。

方法 2:使用 Map

概述

我们可以使用 Map 来合并两个数组 arr1 和 arr2。我们将 arr1 中的所有对象添加到 Map 中,然后根据它们的 ID 合并 arr2 中的对象。合并后的对象存储在名为 res 的数组中。最后,我们根据 ID 属性将 res 数组按升序排序。

算法
  1. 我们首先创建一个新的 Map 对象,命名为 map。Map 用于高效地存储和检索键-值对。
  2. 使用 for-of 循环,我们遍历 arr1 中的每个对象。对于每个对象,我们将其 ID 设置为键,将整个对象设置为 map 中的值。
  3. 接下来,我们使用另一个 for-of 循环来遍历 arr2 中的每个对象。对于每个对象,我们检查其 ID 是否已经存在于 map 中。
  4. 如果该 ID 不存在于 map 中,我们将该 ID 设置为键,将整个对象设置为 map 中的值。这确保了在不修改的情况下将对象包含在 res 数组中。
  5. 但是,如果 ID 已经存在于 map 中,我们使用 map.get(obj.id) 获取现有对象。然后,我们使用 Object.keys(obj) 来迭代当前对象的每个属性。
  6. 对于每个属性,我们使用当前对象的值更新现有对象的相应属性。此合并过程确保当对象具有相同的 ID 时,arr2 中的值将覆盖 arr1 中的值。
  7. 在合并所有对象后,我们创建一个名为 res 的空数组,用于存储最终结果。
  8. 我们使用map.keys()来迭代 map 的键。对于每个键,我们使用 map.get(key) 检索相应的对象,并将其推送到 res 数组中。
  9. 最后,我们使用 sort 方法和比较函数 (a, b) => a.id - b.id 来根据 ID 属性的升序对 res 数组进行排序。
  10. 最终,我们将排序后的 res 数组作为 join 函数的最终结果返回。
实现
js 复制代码
/**

 * @param {Array} arr1
 * @param {Array} arr2
 * @return {Array}
   */
   var join = function(arr1, arr2) {
   const map = new Map();
   for (const obj of arr1) map.set(obj.id, obj);
   for (const obj of arr2) {
       if (!map.has(obj.id)) map.set(obj.id, obj);
       else {
           const prevObj = map.get(obj.id);
           for (const key of Object.keys(obj)) prevObj[key] = obj[key];
       }
   }
   const res = new Array();
   for (let key of map.keys()) res.push(map.get(key));
   return res.sort((a,b)=>a.id-b.id); 
   };
复杂度分析
  • 时间复杂度:由于排序函数的存在,时间复杂度为 O(nlogn),其中 n 是combinedArray中的元素总数(arr1 的长度加上 arr2 的长度)。迭代和合并过程也会对时间复杂度有所贡献,但排序操作占主导地位。
  • 空间复杂度:空间复杂度为 O(n),其中 n 是 map 中的元素总数。

方法 3:使用双指针

概述

主要思路类似于合并两个已排序数组。我们遍历已排序的 arr1 和 arr2,比较它们的 ID 并将对象添加到结果的 joinedArray 中,然后根据需要增加 i 或 j 指针。一旦一个数组被完全处理,就插入另一个数组中的剩余对象。

算法
  1. 我们首先根据其 ID 对 arr1 和 arr2 进行升序排序。这确保在合并过程中以一致的顺序处理对象。
  2. 初始化一个名为joinedArray的空数组,用于存储合并的对象。
  3. 我们使用两个指针 i 和 j,来跟踪当前在 arr1 和 arr2 中的位置。我们从 i = 0 和 j = 0 开始。
  4. 进入一个 while 循环,直到达到 arr1 或 arr2 的末尾。在循环内部,我们比较当前位置的对象的 ID(arr1[i].id 和 arr2[j].id)。
  5. 如果 arr1 中的 ID 小于 arr2 中的 ID,我们将 arr1 中的对象添加到 joinedArray,并递增 i,以继续处理 arr1 中的下一个对象。
  6. 如果 arr1 中的 ID 大于 arr2 中的 ID,我们将 arr2 中的对象添加到 joinedArray,并递增 j,以继续处理 arr2 中的下一个对象。
  7. 如果 ID 相同,我们合并这两个对象的属性。我们通过将 arr1[i] 的属性展开(...arr1[i])然后用 arr2[j] 的相应值覆盖来创建一个新对象。合并的对象添加到 joinedArray,然后递增 i 和 j,以继续处理两个数组中的下一个对象。
  8. 在 while 循环结束后,我们检查是否还有未处理的对象在 arr1 或 arr2 中。如果有,我们进入单独的 while 循环,将剩余的对象添加到 joinedArray
  9. 最后,我们返回 joinedArray,其中包含来自两个数组的所有合并对象。
实现
js 复制代码
/**

 * @param {Array} arr1

 * @param {Array} arr2

 * @return {Array}
   */
   var join = function(arr1, arr2) {
   arr1.sort((a,b) => a.id - b.id)
   arr2.sort((a,b) => a.id - b.id)
   let i = 0
   let j = 0

   const joinedArray = []

   while(i < arr1.length && j < arr2.length) {

       if(arr1[i].id === arr2[j].id) {
           joinedArray.push({...arr1[i], ...arr2[j]})
           i++
           j++
           continue
       }
       
       if(arr1[i].id < arr2[j].id) {
           joinedArray.push({...arr1[i]})
           i++
           continue
       }
       
       joinedArray.push({...arr2[j]})
       j++

   }

   while(i < arr1.length) {
       joinedArray.push({...arr1[i]})
       i++
   }

   while(j < arr2.length) {
       joinedArray.push({...arr2[j]})
       j++
   }

   return joinedArray
   };
复杂度分析
  • 时间复杂度:arr.sort 的时间复杂度为 O(nlogn),其中 n 是最大数组的长度。
  • 空间复杂度:由于我们创建了一个 joinedArray 来存储结果,其大小可能与 arr1 和 arr2 的大小一样大,因此可以说空间复杂度为 O(n),其中 n 是 arr1 和 arr2 中元素的总数。但实际上,我们只考虑辅助空间(不包括输入和输出),真正的空间复杂度为 O(1)。这是因为算法的附加空间用于变量和指针(i 和 j),不随输入数组的大小而改变。

面试提示

  • 对结果数组排序的目的是什么?
    • 对结果数组按 ID 升序排序是为了确保对象以特定的顺序排列。这使得能够根据其 id 值更容易定位和检索对象,特别是用于进一步处理或分析时。
  • 合并过程是否可以在原始数组上进行就地修改?
    • 通常建议在合并过程中避免修改原始数组,以保持不可变性并避免意外的副作用。在合并过程中修改原始数组可能会引入复杂性,特别是如果数组在多个上下文中共享或用于多个目的。通常更好的做法是创建一个新的合并数组,以确保数据完整性并保持原始数组的原始状态。
  • 编程中为什么合并基于共同键的数组很有用?
    • 在编程中,基于共同键合并数组是常见的操作,特别是在处理关系型或结构化数据时。它允许我们将来自多个来源的信息组合和整合,从而促进数据分析、数据处理和数据集成。通过根据共享键合并数组,我们可以汇集相关数据,建立关系,并在合并数据集上执行进一步的操作。
相关推荐
Not Dr.Wang4222 小时前
自动控制系统稳定性研究及判据分析
算法
ffqws_2 小时前
A*算法:P5507 机关 题解
算法
xixixin_2 小时前
【React】中 Body 类限定法:优雅覆盖挂载到 body 的组件样式
前端·javascript·react.js
执着2592 小时前
力扣hot100 - 108、将有序数组转换为二叉搜索树
算法·leetcode·职场和发展
2501_901147832 小时前
学习笔记:单调递增数字求解的迭代优化与工程实践
linux·服务器·笔记·学习·算法
AI科技星2 小时前
张祥前统一场论核心场方程的经典验证-基于电子与质子的求导溯源及力的精确计算
线性代数·算法·机器学习·矩阵·概率论
kebijuelun2 小时前
ERNIE 5.0:统一自回归多模态与弹性训练
人工智能·算法·语言模型·transformer
历程里程碑3 小时前
普通数组----最大子数组和
大数据·算法·elasticsearch·搜索引擎·排序算法·哈希算法·散列表