【问题标题】:Given an array, count the pairs whose sums are multiples of 60给定一个数组,计算总和为 60 的倍数的对
【发布时间】:2019-06-07 21:54:09
【问题描述】:

给定一个数组,你如何找到加起来为 60 或可被 60 整除的数对(两个值)的数量。注意:必须比 O(N^2) 快。

输入:[10, 50, 30, 90] 输出:2 推理:10+50 = 60, 30 + 90 = 120(120能被60整除)

输入:[60,60,60] 输出:3 推理:60 + 60 = 120, 60 + 60 = 120, 60 + 60 = 120

我下面的代码将在 O(N) 时间内运行,但我不知道如何处理彼此相等的对(即,如果数组中有 2 30 个值将加 1到您的计数器,但如果数组中有 3 30 个值,则会将 3 添加到您的计数器)。我想我应该创建一个组合函数(即 2C2 或 3C2),但那是一个线性函数,不会让函数回到 O(N^2) 吗?

values(myList) {
    var obj = {};
    var count = 0;

    // loop through array and mod each value and insert it into a dictionary
    myList.forEach((elem, index) => {
        if (!obj.hasOwnProperty(elem % 60)) {
            obj[elem % 60] = 1;
        } else {
            obj[elem % 60]++;
        }
    });

    for (var keys in obj) {
        if (obj.hasOwnProperty(60 - keys)) {
            if (60 - keys == keys) {
                // take care of pairs
                // obj[keys] = x --> xC2
            } else {
                count += Math.min(obj[keys], obj[60 - keys]);
                delete obj[keys]
                delete obj[60 - keys];
            }
        }
    }
    return count;
}

【问题讨论】:

  • 组合函数的线性实现是可以的,因为它相对于它所代表的元素数量是线性的。所以你在组合函数内部要做的工作越多,它运行的就越少。
  • 您的问题标题似乎具有误导性。它说“找到...的所有对”,但是在您的示例中,您似乎想要“找到...的对数”。请澄清。
  • 请记住,“obj.hasOwnProperty”也不是免费调用。随着房产数量的增加,它会变得更加昂贵。

标签: javascript algorithm time-complexity big-o


【解决方案1】:

更新:这个解决方案是 n^2,所以这不能回答原来的问题。

let values = [60,10,20,50,40,30, 120, 60, 10];

let count = 0;

for (let i = 0; i < values.length; i++){
    for (let j = i+1; j < values.length; j++){
        let v = values[i] + values[j];
        if (v == 60 || !(v % 60)){
            count++;
        }
    }    
}

更新 2:

要使其成为 n + n*log(n),可以使用 mod 值创建一个排序树,然后遍历每个 mod 值并查找 60-mod 值以找到组成的对数区别。节点也可以优化存储重复次数。这能解决你的问题吗?

【讨论】:

  • 这仍然是一个二次时间算法。它是 O(n * (n-1)/2)。如果您的数组长度为 50 个项,则内部循环运行 1225 次。如果它的长度为 100 项目,则运行 4950
  • 是的,但它小于你原来的问题的 n^2。
  • 并非如此。是 O(n^2)。这是一个解释:stackoverflow.com/questions/8261895/big-oh-notation
  • 对,你说得对,让我看看能不能提出更好的方案。
  • 添加了一个 n*log(n) 解决方案的想法,适合吗?
【解决方案2】:

如果我们计算每个元素可以与目前看到的数字组成的对,我们可以使用简单的加法而不是管理组合或微妙的边缘情况。

function f(A, m){
  const rs = new Array(m+1).fill(0);
  for (let x of A){
    if (rs[(m - x % m) % m])
      rs[m] += rs[(m - x % m) % m];
    rs[x % m]++;
  }
  return rs[m];
}

console.log(f([10, 50, 30, 30], 60));
console.log(f([30, 10, 50, 30, 30], 60));
console.log(f([1, 5, 3, 3, 6, 24], 6));

(顺便说一句,我不确定您为什么要区分加起来为 60 的两个数字和总和可被 60 整除的值的对,因为前者包含在后者中。)

【讨论】:

    【解决方案3】:

    不需要组合。这是简单的数学。

    我是n * (n-1) / 2

    假设您有 4 个项目 a,b,c,d

    对将是:

    • (a,b)
    • (a,c)
    • (a,d)
    • (b,c)
    • (b,d)
    • (c,d)

    对于 4 个项目,我们有 4 * 3 / 2 = 6

    #UPDATE:

    改变

    count += Math.min(obj[keys], obj[60 - keys]);
    

    count += obj[keys] * obj[60 - keys];
    

    考虑 2 个键 - 1248

    • 12 包含元素 - 12,72,132
    • 48 有元素 - 48,108

    从技术上讲,您正在为它们存储计数,即 3 和 2。 如果你观察,总没有。我们可以做的配对是 3 * 2 = 6 而不是 Math.min(3,2);

    【讨论】:

      【解决方案4】:

      您可以在 O(1) 时间内计算 nC2,因为 nC2 = n!/(n−2)!·2! = n·(n em>-1)·(n-2)!/(n-2)!·2! = n·(n-1)/2! = n·( n-1)/2.

      也就是说,您可能需要考虑一种不同的方法:您可以在构建 obj 时添加到 count,而不是基于 obj 计算 count 的单独循环。这可能更直观,因为它消除了对特殊情况的需要。 (由你决定。)

      顺便说一句,您的if (60 - keys == keys) 测试不正确;这将检测keys == 30 的情况,但不会检测keys == 0 的情况。 (您可能还需要解决一些其他错误。)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-03-13
        • 1970-01-01
        • 2012-12-30
        • 2014-12-19
        • 2019-03-03
        • 1970-01-01
        • 1970-01-01
        • 2022-10-23
        相关资源
        最近更新 更多