【问题标题】:Time Complexity - Refactor O(N²) to O(N)时间复杂度 - 将 O(N²) 重构为 O(N)
【发布时间】:2013-10-20 08:42:11
【问题描述】:

我这里有一个函数,它计算数组中唯一整数对的数量,谁的和是偶数。目前我已经使用嵌套循环对此进行了编码,但是这是低效的,因为嵌套循环会导致时间复杂度为 O(N²)

在本例中,A 表示数组,PQ 表示整数对。 Q 应始终大于 P,否则会导致非唯一整数对(其中 P 和 Q 可以指向数组中的相同值)

public int GetEvenSumCount(int[] A)
{
    // result storage
    int result = 0;

    // loop through each array element to get P
    for (int P = 0; P < A.Length; P++)
    {
        // loop through each array element to get Q
        for (int Q = P + 1; Q < A.Length; Q++)
        {
            // calculate whether A[P] + A[Q] is even.
            if ((A[P] + A[Q]) % 2 == 0)
            {
                result++;
            }
        }
    }
    return result;
}

我现在需要重构它,使最坏情况的时间复杂度为O(N),但我不知道从哪里开始!我知道这将只使用一个循环,而不是嵌套循环,但我不知道在这方面你会如何将 A[P]A[Q] 相加。

【问题讨论】:

  • 显然你不能通过显式访问每一对来做到这一点。但你不必这样做。将报告解决方案。
  • 这不会是 Twitter 的面试问题吧?
  • @templatetypedef,这是一个面试问题,但不适用于推特

标签: c# arrays algorithm big-o time-complexity


【解决方案1】:

两个整数的和只能是偶数,只有两个都是奇数或两个都是偶数。

扫描数组,计算奇数和偶数的个数。假设这些是 N1 和 N2。

The number of pairs = (N1 Choose 2) + (N2 Choose 2).
                    = N1*(N1-1)/2 + N2*(N2-1)/2

【讨论】:

  • 感谢您的回答,帮助很大!你能解释一下你为什么这样做: N1*(N1-1)/2 + N2*(N2-1)/2 吗?我知道它有效,但我不明白这是在做什么?
  • 这是您可以从 n 个项目中选择 2 个项目的方式数,而顺序并不重要。 C(n,2)。要获取更多信息,您可以通过谷歌搜索排列和组合,或访问此链接mathsisfun.com/combinatorics/combinations-permutations.html
【解决方案2】:

您可以通过两种方式获得偶数:

  1. 添加两个偶数,如2 + 4 = 6
  2. 添加两个奇数,如1 + 3 = 4

相反,偶数和奇数相加总是奇数,比如1 + 2 = 3

所以你能得到的偶数总数是:

  1. 偶数对的数量
  2. 另外,奇数对的数量

n 项目集合中的对数为:

N = n * (n-1) / 2

完整代码:

static bool IsEven(int i)
{
    return i % 2 == 0;
}

static bool IsOdd(int i)
{
    return i % 2 != 0;
}

static int GetPairCount(int n)
{
    return n * (n- 1) / 2;
}

public static int GetEvenSumCount(int[] A)
{
    int evensCount = A.Count(IsEven);
    int oddCount = A.Count(IsOdd);

    return GetPairCount(evensCount) + GetPairCount(oddCount);
}

如您所见,没有嵌套循环,您不需要实际计算总和。

这个实现的复杂度是 O(N)。

【讨论】:

    【解决方案3】:

    如承诺的那样,报告解决方案:

    static int GetEvenSumCountFast(int[] A)
    {
        int[] OddEven = new int[2];
        for (int i = 0; i < A.Length; i++)
            OddEven[A[i] & 1]++;
        return OddEven[0] * (OddEven[0] - 1) / 2 +
            OddEven[1] * (OddEven[1] - 1) / 2;
    }
    

    好吧,其他人已经解决了,但无论如何..

    替代方案:

    static int GetEvenSumCountFast(int[] A)
    {
        int odd = 0, even = 0;
        for (int i = 0; i < A.Length; i++)
        {
            odd += A[i] & 1;
            even += ~A[i] & 1;
        }
        return odd * (odd - 1) / 2 +
            even * (even - 1) / 2;
    }
    

    【讨论】:

    • 嗨,我注意到你有一个“快速”的例子。这是否比 O(N) 时间复杂度更快?
    • 不,它们都是 O(N) - “快”,因为它们比 O(N^2) 快。你不能在少于 O(N) 的时间内完成,最坏的情况是你必须查看所有输入。
    【解决方案4】:

    由于两个偶数之和是偶数,两个奇数之和也是(但奇数和偶数的数字是奇数)我首先将它们分为偶数和奇数:

    var grouped = A.GroupBy(x => x % 2 == 0);
    

    现在每个组中唯一对的数量(其中 n 是元素的数量)是:

    (n-1) + (n-2) + … + 1 = n * (n-1) / 2
    

    所以(如果我们在偶数组或奇数组中,则独立):

    return gouped.Sum(x => {var n = x.Count(); return n * (n-1) / 2; });
    

    【讨论】:

      【解决方案5】:

      感谢所有为这个问题做出贡献的人,我在这里记录了你所有的笔记,并生成了一个简洁的函数,它的行为符合预期,并且仍然符合所需的 O(N) 时间复杂度

      public int GetEvenSumCount(int[] A)
      {
          int odd = A.Count(o => o % 2 != 0);
          int even = N.Length - odd;
          return odd * (odd - 1) / 2 + even * (even - 1) / 2;
      }
      

      【讨论】:

        【解决方案6】:

        问题是找到唯一的偶数和......在上述所有解决方案中......当计算奇数和偶数的数量时,他们没有考虑到它们的唯一性。例如,如果有两个偶数具有相同值的数字说 4 和另一个偶数 6。将有两个值为 10 的偶数和它们是非唯一的

        【讨论】:

        • 我明白你的意思,但“可能”并不重要。而且是面试题,上面的答案还是错的! :-P
        • P.S.实际上会有四个偶数的值为 10,而不是两个……为什么? 1st 4 and 1st 6, 1st 4 and 2nd 6, 2nd 4 and 1st 6, 2nd 4 and 2nd 6 :-)
        【解决方案7】:

        嗯,我有一个 O (n) 的解决方案。的种类。你可以认为这是作弊。它可能是。

        “int”的范围有限 - +/- 2^31。

        我们必须假设可能的数组大小是无限的——否则 O () 表示法没有意义。如果数组大小被限制为 2^64 个元素,那么问题总是可以使用 2^128 个操作在恒定时间 O (1) 内解决...

        因此,为数组中包含的所有可能的 2^32 int 值创建一个位图。这需要 O (n) 步。从位图中创建一个新数组,删除所有重复项。该数组最多有 min (n, 2^32) 个条目。其余的总是可以在 2^64 次操作中完成,即 O (1)。因此总数为 O (n),但如果 n 约为 2^32,则具有 巨大 常数因子。

        如果数组包含字节而不是整数,这实际上是一个相当快的算法。

        现在找到一个有效的算法,这似乎有点困难。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2019-12-09
          • 2015-05-25
          • 1970-01-01
          • 1970-01-01
          • 2011-07-09
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多