时间复杂度 - 重构O(N²)到O(N)

时间:2013-10-12 08:28:26

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

我在这里有一个函数来计算数组中唯一整数对的数量,其总和是偶数。目前我使用嵌套循环对此进行了编码,但这是低效的,因为嵌套循环导致时间复杂度为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]相加。

7 个答案:

答案 0 :(得分:5)

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

  1. 添加两个偶数值,例如2 + 4 = 6
  2. 添加两个奇数值,例如1 + 3 = 4
  3. 相反,添加奇数值的偶数值将始终为奇数,如1 + 2 = 3

    所以你可以得到的偶数总和是:

    1. 偶数值对的数量
    2. 另外,奇数值对的数量
    3. 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)。

答案 1 :(得分:2)

只有当两者都是奇数或两者都是偶数时,两个整数之和才可以是偶数。

扫描阵列,并计算奇数和偶数的数量。让我们说这些是N1和N2。

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

答案 2 :(得分:1)

按照承诺,用解决方案报告:

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;
}

答案 3 :(得分:1)

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

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; });

答案 4 :(得分:0)

感谢所有为此问题做出贡献的人,我已经在这里完成了所有注释,并制作了一个简洁的功能,其功能符合预期,并且仍然符合所需的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;
}

答案 5 :(得分:0)

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

答案 6 :(得分:0)

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

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

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

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

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

现在找到一种有效的算法,这似乎很难。