代码之家  ›  专栏  ›  技术社区  ›  Ala Eddine Menai

如何在不消除Javascript中重复的情况下找到两个数组之间的确切交集?

  •  1
  • Ala Eddine Menai  · 技术社区  · 1 年前

    我有两组数字 arr1 arr2 。我想找到它们之间的交集,结果中的每个元素都必须出现如中所示的次数 两个阵列 .

    经过2个小时的尝试,我制定了这个解决方案,但它并不能涵盖所有情况:

    密码

    /**
     * @param {number[]} nums1
     * @param {number[]} nums2
     * @return {number[]}
     */
    var intersect = function (nums1, nums2) {
        if (nums1.length == nums2.length) {
            if (nums1.includes(nums2) && nums2.includes(nums1)) {
                return nums1[0];
            }
            if (nums1.includes(nums2)) {
                return nums2.filter((v) => nums1.includes(v));
            } else {
                return nums1.filter((v) => nums2.includes(v));
            }
        }
        if (nums1.length < nums2.length)
            return nums1.filter((v) => nums2.includes(v));
        if (nums2.length < nums1.length)
            return nums2.filter((v) => nums1.includes(v));
    };
    
    console.log(intersect([1, 2, 2, 1], [2, 2]));
    console.log(intersect([1, 2, 2, 1], [2]));
    console.log(intersect([9, 4, 9, 8, 4], [4, 9, 5]));
    console.log(intersect([2, 1], [1, 1])); // failed ( should be [1]

    测试1(通过)

    • 输入: nums1=[1,2,2,1],nums2=[2,2]
    • 输出 : [2,2]

    测试2(通过)

    • 输入: nums1=[4,9,5],nums2=[9,4,9,8,4]
    • 输出 :[4,9]或[9,4]

    测试3(失败)

    • 输入: nums1=[3,1,2],nums2=[1,1]
    • 输出 : [1,1]
    2 回复  |  直到 1 年前
        1
  •  3
  •   Nina Scholz    1 年前

    您可以对项目进行计数并过滤第二个数组。

    const
        intersect = (nums1, nums2) => {
            const
                count = (c, i) => v => c[v] = (c[v] || 0) + i,
                counts = {};
                
            nums1.forEach(count(counts, 1));
            return nums2.filter((f => v => f(v) >= 0)(count(counts, -1)));
            
        };
    
    console.log(intersect([1, 2, 2, 1], [2, 2]));
    console.log(intersect([1, 2, 2, 1], [2]));
    console.log(intersect([9, 4, 9, 8, 4], [4, 9, 5]));
    console.log(intersect([2, 1], [1, 1])); // failed
        2
  •  2
  •   Cary Swoveland    1 年前

    在伪代码中,如果

    a1 = [9, 4, 9, 8, 4]
    a2 = [4, 9, 4, 5, 4]
    

    然后计算计数散列

    h1 = { 9=>2, 4=>2, 8=>1 }
    h2 = { 4=>3, 9=>1, 5=>1 }
    

    接下来,确定

    common_keys = h1.keys intersect h2.keys
      #=> [9, 4, 8] intersect [4, 9, 5]
      #=> [9, 4]
    

    然后

    b = common_keys.map { |key| [key] repeat [h1[key], h2[key]].min }
      #=> = [[9], [4, 4]]
    

    最后

    b.flatten
      #=> [9, 4, 4]
    
        3
  •  1
  •   Hunter McMillen zellus    1 年前

    最简单的方法是计算两个数组中每个N的出现次数,然后进行筛选:

    function frequency(nums) {
      return nums.reduce((acc, curr) => {
        if (acc[curr]) {
          acc[curr]++;
        } else {
          acc[curr] = 1;
    
        }
        return acc;
      }, {});
    }
    
    var intersect = function(nums1, nums2) {
      const nums1Frequencies = frequency(nums1);
      const nums2Frequencies = frequency(nums2);
    
             // only consider unique elements from nums1 (we already counted occurrences)
      return Array.from(new Set(nums1))
               // determine how many of N should appear in the result
               .map(n => [n, Math.min(nums1Frequencies[n], nums2Frequencies[n])])
               // filter out N that were not in both arrays
               .filter(([n, min]) => !Number.isNaN(min))
               // create an Array containing the number of N that should go into the result
               .map(([n, min]) => Array(min).fill(n))
               // flatten nested arrays
               .flat()
    };
    
    console.log(intersect([1, 2, 2, 1], [2, 2]));        // [2, 2]
    console.log(intersect([1, 2, 2, 1], [2]));           // [2]
    console.log(intersect([9, 4, 9, 8, 4], [4, 9, 5]));  // [9, 4]
    console.log(intersect([2, 1], [1, 1]));              // [1]
        4
  •  1
  •   VLAZ Sohail Arif    1 年前

    对两个阵列进行一次线性扫描以计数出现次数,然后对结果进行线性扫描以提取交集。

    此解决方案使用 Map 因为在处理基元时更安全。例如,如果数组具有混合值: [1, "1", 2] 这将是三个不同的计数,因为地图保留了其关键点的类型。

    /**
     * Counts occurrences of each member of array
     * @param {any[]} arr - array of any values
     * @return {Map} a map of the breakdowns - each key is a unique item in the array  (SameValueZero comparison) and the values are the counts of occurrences
     */
    const countMembers = arr => {
      const counts = new Map();
      
      for (const item of arr)
        counts.set(item, (counts.get(item) ?? 0) + 1);
        
      return counts;
    };
    
    /**
     * Merge maps where the keys intersect, skip keys that are only in one map
     * @param {Map} map1
     * @param {Map} map2
     * @param valueMergeCallback - determines how the values will be handled. Called with value from map1 and value from map2 as arguments
     * @return {Map} Map with keys that only show up in both map1 and map2 where the value is determined by valueMergeCallback
     */
    const intersectMergeMaps = (map1, map2, valueMergeCallback) => {
      const result = new Map();
      
      for (const [key, value1] of map1) {
        if (!map2.has(key))
          continue;
          
        const value2 = map2.get(key);
        result.set(key, valueMergeCallback(value1, value2));
      }
     
      return result;
    };
    
    /**
     * Helper function that creates an array of repeated values
     * @param {*} value - item that will show up in array
     * @param {number} repeat - how many times it should appear in the array
     * @return array containing only value repeat number of times
     */ 
    const repeatArray = (value, repeat) =>
      Array.from({length: repeat}, () => value);
      
      
    /**
     * @param {number[]} nums1
     * @param {number[]} nums2
     * @return {number[]}
     */
    const intersect = function (nums1, nums2) {
        const count1 = countMembers(nums1);
        const count2 = countMembers(nums2);
        
        const m = intersectMergeMaps(count1, count2, Math.min);
        
        return Array.from(m)
          .flatMap(([num, count]) => repeatArray(num, count));
    };
    
    console.log(intersect([1, 2, 2, 1], [2, 2]));
    console.log(intersect([1, 2, 2, 1], [2]));
    console.log(intersect([9, 4, 9, 8, 4], [4, 9, 5]));
    console.log(intersect([2, 1], [1, 1]));
    
    
    console.log(intersect([2, 1], [4, 3]));
        5
  •  0
  •   Ala Eddine Menai    1 年前

    @Nina Scholz的解决方案运行良好,但有以下几点:

    时间复杂性: 67.08%

    空间复杂性: 67.82%

    我试着找出我的算法的错误,发现我重复了同样的错误 检查 重复基于阵列 长度 .

    因此,与其使用 if/else 的外部 filter 方法我将条件移动到 滤器 方法

    我想忽略通过的 检查 .

    我找到的唯一方法就是把它去掉 value 来自 nums2 .

    密码

    return nums1.filter(
            (value) => nums2.includes(value) && nums2.splice(nums2.indexOf(value), 1)
        );
    };
    

    后果

    时间复杂性: 96.98%

    空间复杂性: 85.87%