如何降低两个 for 循环的大 O 复杂度
How can I reduce big O complexity of two for loops
该函数必须 return 数组 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;
}
如果seconds的值比较小,可以用hash map(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;
}
第一个直接的方法是这样的:
- 创建一个包含 60 个条目的数组,
seconds%60
的其余部分初始化为全零。
- 计算每首歌曲的余数并递增数组中的相关条目。
- 迭代所有可能的余数 (1..29)
- 对于每个剩余部分,您有
a = array[i]
首歌曲和 b = array[60-i]
首需要合并的匹配歌曲:num = a*b; k += num;
- 对于
i==0
和 i==30
您需要特殊处理,因为匹配歌曲位于同一数组元素中:num = a*(a-1);
这会将时间复杂度降低到 O(N):
你需要
- 循环
n
来填充数组(可以在构建歌曲列表时完成一次)和
- 循环
0..30
进行计算。
这导致 O(N)+O(1)
编辑:根据您的规则(歌曲的顺序是否重要)您可能需要乘以 2。
您可以将复杂度从 O(N2) 降低到 O(N) 一个简单的方法:
- 使用数组
int a[60]
,对于 0
..59
中的每个 i
,计算持续时间为 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;
}
该函数必须 return 数组 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;
}
如果seconds的值比较小,可以用hash map(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;
}
第一个直接的方法是这样的:
- 创建一个包含 60 个条目的数组,
seconds%60
的其余部分初始化为全零。 - 计算每首歌曲的余数并递增数组中的相关条目。
- 迭代所有可能的余数 (1..29)
- 对于每个剩余部分,您有
a = array[i]
首歌曲和b = array[60-i]
首需要合并的匹配歌曲:num = a*b; k += num;
- 对于
i==0
和i==30
您需要特殊处理,因为匹配歌曲位于同一数组元素中:num = a*(a-1);
这会将时间复杂度降低到 O(N):
你需要
- 循环
n
来填充数组(可以在构建歌曲列表时完成一次)和 - 循环
0..30
进行计算。
这导致 O(N)+O(1)
编辑:根据您的规则(歌曲的顺序是否重要)您可能需要乘以 2。
您可以将复杂度从 O(N2) 降低到 O(N) 一个简单的方法:
- 使用数组
int a[60]
,对于0
..59
中的每个i
,计算持续时间为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;
}