我尝试过自己解决,但只能找到一个O(n²)的解法。
我考虑使用计数排序,但是内存使用太大。
创建一个长度是输入数组两倍的新数组。时间复杂度为O(2N)
在O(N)的时间内复制所有数字
在O(N)的时间内复制数字的平方
进行基数排序(因为都是整数)。时间复杂度为O(N)
遍历一次以查看是否有两个相邻的数字相同。时间复杂度为O(N)
完成!时间复杂度为O(1)
有两种基本的方法可以做到这一点。
对数组进行排序,然后对每个数字的平方执行二分搜索。总体复杂度为O(nlogn),但需要排序,会破坏原始顺序(这可能对你的情况很重要)。
将数组的所有项插入哈希表中(或任何快速的“set”数据结构)。然后再次迭代数组元素,检查其平方是否存在于哈希表中。使用哈希表可获得O(n)的总体复杂度,但需要额外的O(n)空间。您还可以使用基于树的“set”(例如C++中的“std::set”或Java中的“TreeSet”),这将为您提供O(nlogn)的复杂度。
每个“指针”都是严格向前移动的,因此总体复杂度为O(N),假设基数排序为O(N),平方和比较为O(1)。我想提问者打算让人们做出这些假设。
回应另一篇答案中提问者的评论:如果输入中的整数没有界限,则我认为无法完成。仅计算一个整数的平方就需要大于线性的时间(至少:没有已知的乘法线性算法),所以考虑一个大小为n位的输入,由大小分别为n / 3
位和2 * n / 3
位的两个整数组成。测试一个是否是另一个的平方不能在O(n)时间内完成。我想。我可能错了。
uint32_t index(uint32_t i ) { return i/4; }
unsigned char bit1( uint32_t i ) { return 1<<( (i%4)*2 ); }
unsigned char bit2( uint32_t i ) { return 1<<( (i%4)*2 +1 ); }
bool hasValueAndSquare( std::vector<uint32_t> & v )
{
const uint32_t max_square=65535;
unsigned char found[(max_square+1)/4]={0};
for(unsigned int i=0; i<v.size(); ++i)
{
if (v[i]<=max_square)
{
found[ index(v[i]) ] |= bit1(v[i]);
if ((found[ index(v[i])] & bit2(v[i])) == bit2(v[i])) return true;
}
uint32_t w = (uint32_t)round(sqrt(v[i]));
if( w*w == v[i] )
{
found[ index(w) ] |= bit2(w);
if ((found[index(w)] & bit1(w)) == bit1(w)) return true;
}
}
return false;
}
这段代码并没有经过测试,优化也不是很充分,一个合适的整数平方根函数会更好一些。不过编译器应该会内联所有位访问函数,所以它们应该没问题。
需要注意的是,如果我们使用 64 位整数,那么内存消耗将变得更大,我们需要一个 1Gb 的数组而不是一个 16Kb 的数组 - 这可能不太实用。
个人认为Anon的答案(带有“方块”的小算法)比它看起来更有用:从中删除“从方块中删除所有小于e的”行,该算法可以处理未排序的输入数组。
如果我们假设具有足够空间的典型作业机器,则“方块”数据结构可以建模为布尔标志数组,产生真正的O(1)查找时间。
不排序,可以处理重复项:
迭代数组以找到最小和最大的整数。 O(n)
创建一个大小为差异的位数组。 O(1) 时间,O(k) 空间
(现在,最小值和最大值之间的每个可能的整数都在数组中有一个对应的位)
迭代旧数组,将找到的每个整数对应的位设置为 1。 O(n)
再次迭代旧数组,检查整数的平方是否具有其对应的位集。 O(n)
(虽然我没有排序,但这个算法可以非常容易地修改为创建一种排序算法,它以 O(n+k) 时间和 O(k) 空间进行排序)
k
也是一个重要因素。但是,从算法的角度来看,一个数组可以在O(1)中被“声明”为已清零,因此在这种情况下,k
不被计算为复杂性的一部分(一个现实世界的例子:理论上,一个数组可以通过硬件中的简单开关在O(1)中被清零;我不知道有哪些计算机实际上实现了这个功能)。 - BlueRaja - Danny Pflughoeft优化笔记
哈希集和基数排序算法都可以通过注意以下三个事实进行优化:
下面的优化算法通常比未优化的情况快5倍,并且使用的RAM不到一半。在某些情况下,如果数据大小与L2/L3缓存大小相似,则它们可能会快100倍或更多。
基于基数排序的优化算法
数据结构是五个整数列表:IN、Aodd、Bodd、Aeven、Beven A和B列表使用IN的一半整数大小。(例如,如果IN = 64位,则A和B = 32位)
如果任何一个线性扫描找到匹配项,则立即返回该匹配项。
这比直接使用基数排序算法快得多的原因是:
基于哈希集的优化算法
数据结构是整数IN的列表,加上两个哈希集A和B。A和B集合使用IN的一半整数大小。
这比直接使用哈希集算法更快的原因是:
此处还有一个额外的小优化:A 和 B 可以是一个单独的哈希集,其中每个条目都带有位标志,表示整数是否在 A 或 B 中(因为它既不能在 A 中又不能在 B 中,否则算法将终止)。
bool ArrayContainsNumberAndSquare(int number, int[] array):
boolean numberFound, squareFound;
int square = number * number;
foreach int i in array
(
numberFound = numberFound || i == number;
squareFound = squareFound || i == square;
)
return numberFound && squareFound;