如何减少两个for循环的大O复杂度

时间:2018-07-09 07:04:04

标签: c big-o asymptotic-complexity

该函数必须返回数组songs(整数数组,其中包括歌曲的长度,以秒为单位)中数字对的计数,以使形成的数字对总计整分钟。

long playlist(int songs_count, int *songs) {
    int i, j, k = 0;
    for (i = 0; i < songs_count; i++) {
        for (j = i + 1; j < songs_count; j++)
            if ((songs[i] + songs[j]) % 60 == 0)
                k++;
    }
    return k;
}

3 个答案:

答案 0 :(得分:6)

第一个直接方法是这样的:

  • 创建一个包含60个条目的数组,并将seconds%60的其余部分初始化为全零。
  • 计算每首歌曲的剩余部分并增加数组中的相关条目。
  • 遍历所有可能的余数(1..29)
  • 对于每个其余部分,您需要组合a = array[i]首歌曲和b = array[60-i]首匹配的歌曲:num = a*b; k += num;
  • 对于i==0i==30,您需要特殊处理,因为匹配的歌曲位于同一数组元素中:num = a*(a-1);

这会将时间复杂度降低到O(N):

您需要

  • n上循环以填充数组(在构建歌曲列表时可以完成一次)和
  • 0..30上循环进行计算。

这将导致O(N)+O(1)

编辑:根据您的规则(歌曲的顺序是否重要),您可能需要乘以2。

答案 1 :(得分:1)

如果秒的值较小,则可以使用哈希映射(在c ++中为unordered_map)来解决〜O(n)复杂性。

假设您知道该对的最大值是600秒,那么您可以让另一个数组存储这些值。

for(int i = 1; i <= 10; i++)
{
    a[i-1] = i * 60;
}

//a[] = {60, 120, 180, 240, 300, 360, 420, 480, 540, 600};
unordered_map <int, int> mymap;

for(int i = 0; i < songs_count; i++)
{
    mymap[i] = songs[i];
}

现在您可以将代码修改为(解决方案适用于C ++):

long playlist(int songs_count, int *songs) 
{
    int i, j, k = 0;
    for(int i = 1; i <= 10; i++)
    {
        a[i-1] = i * 60;
    }

    //a[] = {60, 120, 180, 240, 300, 360, 420, 480, 540, 600};
    unordered_map <int, int> mymap;

    for(int i = 0; i < songs_count; i++)
    {
        mymap[i] = songs[i];
    }

    for (i = 0; i < songs_count; i++) 
    {
        for (j = 0; j < n /*size of a*/; j++)
        {
            if (a[j] > songs[i] && mymap.find(a[j] - songs[i]) != mymap.end())
            {
                k++;
            }
        }
    }
    return k;
}

答案 2 :(得分:1)

您可以通过一种简单的方法将复杂度从 O(N 2 降低到 O(N)

  • 使用数组int a[60],为i中的每个0 .. 59计算持续时间为i秒的歌曲总数分钟数。单程就足够了。
  • 对于数组中的每个i,计算感兴趣的歌曲对的数量。再次需要一次通行证。
  • 要计算对数,需要额外说明:
    • 订购了几副吗?
    • 一对可以拥有两首相同的歌曲吗?

假设一对歌曲必须具有不同的歌曲且顺序无所谓,这是一种实现方式:

long playlist(int songs_count, int *songs) {
    long a[60] = { 0 };
    int i;
    long total;
    for (i = 0; i < songs_count; i++)
        a[songs[i] % 60]++;
    total = (a[0] * (a[0] - 1) + a[30] * (a[30] - 1)) / 2;
    for (i = 1; i <= 29; i++)
        total += a[i] * a[60 - i];
    return total;
}

如果顺序很重要,并且成对的歌曲可以有两次相同的歌曲,则计算是不同的:

long playlist(int songs_count, int *songs) {
    long a[60] = { 0 };
    int i;
    long total;
    for (i = 0; i < songs_count; i++)
        a[songs[i] % 60]++;
    total = 0;
    for (i = 0; i < 60; i++)
        total += a[i] * a[(60 - i) % 60];
    return total;
}