我试图找到等于给定数字的子集(1-3长度)。我写了这个算法,它工作正常但速度太慢。我如何更改此算法以使其更快?
int PrintCombinations(int * array, int userLenght) {
int total = 0;
for (int i = 0; i < GetArrayLenght(array); ++i) {
if(userLenght == array[i]) {
printf("%d = %d\n", userLenght, array[i]);
++total;
}
}
for (int j = 0; j < GetArrayLenght(array); ++j) {
for (int k = j; k < GetArrayLenght(array); ++k) {
if(array[j] + array[k] == userLenght) {
printf("%d = %d + %d\n", userLenght, array[j], array[k]);
++total;
}
}
}
for (int l = 0; l < GetArrayLenght(array); ++l) {
for (int i = l; i < GetArrayLenght(array); ++i) {
for (int j = i; j < GetArrayLenght(array); ++j) {
if(array[l] + array[i] + array[j] == userLenght) {
printf("%d = %d + %d + %d\n", userLenght, array[j], array[l], array[i]);
++total;
}
}
}
}
return total;
}
答案 0 :(得分:1)
您应该在循环之前计算一次数组的长度。目前,它在每个循环的每次迭代中重新计算。如果此操作很昂贵,那么将计算移出循环将是优化代码的一种非常有效的方法。
例如,如果GetArrayLenght()
扫描数组中的终止值,则将调用移出循环会使时间复杂度从 O(N 4 )下降到 O(N 3 )
第二步是以不同方式嵌套循环,避免搜索子句中是否已超出长度(假设所有长度均为正数)。这不会改变时间复杂度,但仍可以显着减少测试次数:
int PrintCombinations(int *array, int userLength) {
int total = 0;
int length = GetArrayLenght(array);
for (int l = 0; l < length; ++l) {
if (array[l] > userLength)
continue;
if (array[l] == userLength) {
printf("%d = %d\n", userLength, array[l]);
++total;
}
for (int i = l; i < length; ++i) {
if (array[l] + array[i] > userLength)
continue;
if (array[l] + array[i] == userLength) {
printf("%d = %d + %d\n", userLength, array[l], array[i]);
++total;
}
for (int j = i; j < length; ++j) {
if (array[l] + array[i] + array[j] == userLength) {
printf("%d = %d + %d + %d\n",
userLength, array[j], array[l], array[i]);
++total;
}
}
}
}
return total;
}
如果你的数组非常大,如果你可以修改它或复制它,按升序排序将降低比较次数,并使用二进制搜索最后一次循环将带来时间复杂性从 O(N 3 )到 O(N 2 Log N)。
答案 1 :(得分:0)
我会使用排序数组。一旦数组[k + 1]&gt;您就可以停止遍历userLenght。
一旦你得到'k',就把它从k遍历到0.对于2项总和,只要array[i]+array[j] < userLenght
(i,j <= k)你知道那里是不可能的2项总和。 3项总和的相同技术。
BTW,在你的代码中,你需要将'for'放在'if'中,以便在我们找到总和时不要运行'for',否则'total'可能是错。
答案 2 :(得分:0)
所以,首先你按照增加顺序对数组进行排序,然后, 找到另一个数组,其中填充了长度为2的所有可能子集,称之为array2。 现在你的答案可以通过二进制搜索找到,这比你现在做的更快.. !!
编辑:
您将做的事情如下:
你正在寻找说值val.first排序原始数组。
搜索val作为两个元素的总和,你可以使用两个指针技术,在arr上并在线性时间得到答案,如果val作为两个元素的总和存在。如果不是,则转到步骤3。
然后首先构建arr2,arr2基本上是一个2D数组,其中第i行具有在对角元素之后开始的条目,arr2 [i] [j]将表示和arr [i] + arr [j]。再次搜索val,如何搜索?现在我们希望找到val作为三个条目的总和。所以我们遍历矩阵说你当前在arr2 [i] [j],你知道这是arr [i]和arr [j]的总和,所以从val中减去这两个条目,(这只是val-arr2 [i] [j]),并在arr中搜索结果。现在搜索arr中的结果,你不应该搜索整个数组,而是你将在两个子区间搜索结果.1。arr [i +1]到arr [j-1]
2. arr [j + 1]到arr [n-1]
上面的间隔确保你没有搜索重复的元素..这样做对于矩阵的所有元素,从第一个左边的条目开始,按顺序从左到右,然后是下一行,直到找到它。
的复杂性
案例1:O(nlogn)
案例2:O(nlogn)
案例3:O(n ^ 2)
早些时候
案例1:O(n)
案例2:O(n ^ 2)
案例3:O(n ^ 3)
所以你看到了重大进步..?
答案 3 :(得分:0)
<强>基本强>
GetArrayLenght(array)
的结果,因此与每个for循环的每次迭代相比,您只需要调用一次。使用单个for循环:
for (int l = 0; l < GetArrayLenght(array); ++l)
{
if(userLenght == array[l])
{
printf("%d = %d\n", userLenght, array[l]);
++total;
}
for (int i = l; i < GetArrayLenght(array); ++i)
{
if(array[l] + array[i] == userLenght)
{
printf("%d = %d + %d\n", userLenght, array[l], array[i]);
++total;
}
for (int j = i; j < GetArrayLenght(array); ++j)
{
if(array[l] + array[i] + array[j] == userLenght)
{
printf("%d = %d + %d + %d\n", userLenght, array[j], array[l], array[i]);
++total;
}
}
}
}
高级强>