Algorithm 三重求和算法?
我们有一个带有m个正整数的数组A,有什么算法可以 如果a中有一个三元组(x,y,z),则返回true 使得A[x]+A[y]+A[z]=200 否则返回false。数组中的数字是不同的,运行时间必须为O(n)Algorithm 三重求和算法?,algorithm,big-o,asymptotic-complexity,Algorithm,Big O,Asymptotic Complexity,我们有一个带有m个正整数的数组A,有什么算法可以 如果a中有一个三元组(x,y,z),则返回true 使得A[x]+A[y]+A[z]=200 否则返回false。数组中的数字是不同的,运行时间必须为O(n) 我想出了O(n^3)。关于如何用O(n)实现这一点有什么想法吗?这被称为3SUM问题,目前还没有线性解决方案。我使用二进制搜索算法提供了一个以O(n^2)运行的伪代码: sumTriple(А[1...n]: array of integers,sum: integer): bool
我想出了O(n^3)。关于如何用O(n)实现这一点有什么想法吗?这被称为3SUM问题,目前还没有线性解决方案。我使用二进制搜索算法提供了一个以O(n^2)运行的伪代码:
sumTriple(А[1...n]: array of integers,sum: integer): bool
sort(A)
for i ← 1 to n-2
j ← i+1
k ← n
while k > j
if A[i]+A[j]+A[k] = sum
print i,j,k
return true
else if A[i]+A[j]+A[k] > sum
k ← k-1
else // A[i]+A[j]+A[k] < sum
j ← j+1
return false
sumtree(А[1…n]:整数数组,和:整数):bool
排序(A)
因为我← 1至n-2
J← i+1
K← N
当k>j时
如果A[i]+A[j]+A[k]=和
打印i,j,k
返回真值
如果A[i]+A[j]+A[k]>和
K← k-1
else//A[i]+A[j]+A[k]
您可以找到有关该问题的更多信息和详细信息由于元素是唯一的,这归结为对O(n)中的数组进行预处理以过滤大于200的冗余元素(它们都不在三元组中) 然后,您有一个大小不大于200的数组 检查此数组中的所有三元组是
O(200^3)=O(1)
(但在常量方面可以更有效地完成)
所以,这将是O(n)uo(200^3)=O(n)我想你可以通过位运算来解决这个问题。例如在C++ STL.中的位集 使用3个位集,第一个位集缓存通过添加1个数字可以获取的所有数字,第二个位集缓存通过添加2个数字可以获取的所有数字,第三个位集缓存通过添加3个数字可以获取的所有数字。然后,如果有新的数字出现,您可以通过简单的位操作来维护位集
这里是一个C++代码示例:
bitset<256> bs[4];
for (int i = 0; i < 4; ++i)
bs[i].reset();
int N, number;
cin >> N;
while (N--)
{
cin >> number;
bs[3] |= (bs[2] << number);
bs[2] |= (bs[1] << number);
if (number <= 200)
bs[1].set(number);
//cout << "1: " << bs[1] << endl;
//cout << "2: " << bs[2] << endl;
//cout << "3: " << bs[3] << endl;
}
cout << bs[3][200] << endl;
位集bs[4];
对于(int i=0;i<4;++i)
bs[i].reset();
int N,数字;
cin>>N;
而(N--)
{
cin>>数量;
我同意@amit的解决方案,但有一个问题:在我们的情况下,我们怎样才能做得更好,只是更快
这是我的解决方案,它几乎基于amit的想法,但渐近复杂性==O(n+sum*(sum+1)/2)
,其中n
是输入数组的长度
首先,我们需要n
步骤来过滤输入数组,并将每个值减去和放入新数组,其中值的索引等于值。在这一步结束时,我们得到了数组,数组的大小等于和,我们可以访问O(1)
中的任何值
最后,要找到x,y,z
,我们只需要sum*(sum+1)/2步
typedef struct SumATripleResult
{
unsigned int x;
unsigned int y;
unsigned int z;
} SumATripleResult;
SumATripleResult sumATriple(unsigned int totalSum, unsigned int *inputArray, unsigned int n)
{
SumATripleResult result;
unsigned int array[totalSum];
//Filter the input array and put each value into 'array' where array[value] = value
for (size_t i = 0; i<n; i++)
{
unsigned int value = inputArray[i];
if(value<totalSum)
{
array[value] = value;
}
}
unsigned int x;
unsigned int y;
unsigned int z;
for (size_t i = 0; i<totalSum; i++)
{
x = array[i];
for (size_t j = i+1; x>0 && j<totalSum; j++)
{
y = array[j];
if( y==0 || x + y >= totalSum) continue;
unsigned int zIdx = totalSum - (x + y);
if(zIdx == x || zIdx == y) continue;
z = array[zIdx];
if( z != 0)
{
result.x = x;
result.y = y;
result.z = z;
return result;
}
}
}
//nothing found
return result;
}
//Test
unsigned int array[] = {1, 21, 30, 12, 15, 10, 3, 5, 6, 11, 17, 31};
SumATripleResult r = sumATriple(52, array, 12);
printf("result = %d %d %d", r.x, r.y, r.y);
r = sumATriple(49, array, 12);
printf("result = %d %d %d", r.x, r.y, r.y);
r = sumATriple(32, array, 12);
printf("result = %d %d %d", r.x, r.y, r.y);
typedef结构SumATripleResult
{
无符号整数x;
无符号整数y;
无符号整数z;
}苏门答腊结果;
sumATriple结果sumATriple(无符号整数总和、无符号整数*输入阵列、无符号整数n)
{
苏门答腊结果;
无符号整数数组[总和];
//过滤输入数组并将每个值放入“数组”,其中数组[value]=value
对于(size_t i=0;iis)值200是常量?实际上,由于元素是不同的,假设200是常量-您的算法是O(1)
,因为数组中的元素数以200为界。@amit这是为什么?数组中可能有数十亿个不同的数字都大于200。@tobias_k所以过滤掉它们,然后执行O(200^3)=O(1)算法,我假设没有冗余元素(所以元素在[1200]范围内)如果不是这样的话,把它转换成ti很简单。是的,200常数。数组可以是任意大小,也可以是任意正数。我们只想找到3个数,当我们把它们加起来=200时,你忽略了一个重要的事实,即和有界于200,元素是唯一的。根据这个观察,有线性时间al根据他的说法,数字不是唯一的:“数组中的数字是不同的”。是的,每个数字不会重复两次。(即使没有这个假设,当总和为常数时,也可以在线性时间内完成,但这会稍微复杂一些-只需确保每个元素的添加次数不超过3次。使用大小为200的计数器数组很容易做到)筛选出的数字应大于或等于200,因为它们是正数integers@cricket_007从技术上讲,是的,您也不需要199198,但这些优化对于大O表示法来说是无关紧要的(如果有,我会优化最后一部分或从这200个元素中找到三元组)他还可以避免使用^3
而选择^2
,因为如果他将所有数字放入一个集合中,他可以很快看到a+B达到200后所需的数字是否也在集合中。