假设 a1,...,an
是一组实数序列。令 m
为序列中的最小值,M
为序列中的最大值。
我证明了存在两个元素 x,y
满足 |x-y|<=(M-m)/n
。
那么,有没有找到这样的两个元素的算法,其时间复杂度为 O(n)
?
我考虑过对序列进行排序,但由于我不知道 M
的大小,因此无法使用基数/桶或其他我熟悉的线性时间算法。
如果您有任何想法,我将不胜感激。 提前致谢。
假设 a1,...,an
是一组实数序列。令 m
为序列中的最小值,M
为序列中的最大值。
我证明了存在两个元素 x,y
满足 |x-y|<=(M-m)/n
。
那么,有没有找到这样的两个元素的算法,其时间复杂度为 O(n)
?
我考虑过对序列进行排序,但由于我不知道 M
的大小,因此无法使用基数/桶或其他我熟悉的线性时间算法。
如果您有任何想法,我将不胜感激。 提前致谢。
首先找出n
,M
,m
。如果没有提供,可以在O(n)
的时间内确定它们。
然后创建一个包含n+1
个元素的内存存储器;我们将使用这个存储器来存储n+1
个桶,每个桶的宽度为w=(M-m)/n
。
这些桶均等覆盖了值的范围:第一个桶从[m; m+w[
开始,第二个桶从[m+w; m+2*w[
开始,第n
个桶从[m+(n-1)*w; m+n*w[ = [M-w; M[
开始,而第n+1
个桶从[M; M+w[
开始。
接下来,我们遍历所有值,并根据分配的间隔将其排序到相应的桶中。每个桶最多只能有一个元素。如果桶已经被填满,则表示元素之间的距离比半开区间的边界更近,例如我们找到元素x, y
使得|x-y| < w = (M-m)/n
。
如果找不到这样的两个元素,则之后n
个桶中的n+1
个总桶会被填充一个元素。然后将所有这些元素进行排序。
我们再次遍历所有桶,并仅比较相邻桶的内容距离,看是否有两个元素满足条件。
由于桶的宽度,对于不相邻的桶,该条件不可能成立:对于那些桶,距离始终为|x-y| > w
。
(在第4步中,最后一项不等式的实现也是半开区间无法关闭的原因,以及为什么我们需要n+1
个桶而不是n
个桶的原因。另一种选择是使用n
个桶,并使最后一个桶成为一个特殊情况,即[M; M+w]
。但是O(n+1)=O(n)
,使用n+1
个步骤优于特殊处理最后一个桶。)
步骤1的运行时间为O(n)
,步骤2为0
——我们实际上在那里什么也没有做,步骤3的运行时间为O(n)
,而步骤4的运行时间为O(n)
,因为每个桶中只有1
个元素。总共是O(n)
。
首先注意真正的阈值应该是(M-m)/(n-1)
。
第一步是在O(N)时间内计算最小的m
和最大的M
元素。
您需要计算mid = (m + M)/2
的值。
将小于mid
的值放在数组开头,将大于mid
的值放在数组末尾。
选择元素数量最多的那一部分,并迭代直到剩下的数字很少。
如果两个部分具有相同数量的元素,则可以选择其中任何一个。 如果剩余部分的元素比n/2
要多得多,则为了保持O(n)复杂度,你只能保留n/2 + 1
个元素,因为目标不是找到最小差异,而只是一个足够小的差异。
如@btilly所说的评论中指出,此解决方案在某些情况下可能会失败,例如使用输入[0, 2.1, 2.9, 5]
。因此,需要计算左侧最大值和右侧最小值,以检查答案是否不是right_min - left_max
。即使解决方案变得不太优雅,也不会改变O(n)的复杂度。
搜索过程的复杂度:O(n) + O(n/2) + O(n/4) + ... + O(2) = O(2n) = O(n)。
n/2
,那么您不必保留所有元素。至少保留n/2
个元素就足够了。OP并没有要求最小差异,只要求差异足够小即可。 - DamienDamien在他的评论中是正确的,正确的结果是必须存在x、y,使得|x-y| <= (M-m)/(n-1)。如果你有序列[0, 1, 2, 3, 4],你有5个元素,但没有两个元素之间的距离小于(M-m)/n = (4-0)/5 = 4/5。
通过正确的阈值,解决方案很容易 - 通过一次扫描输入来找到M和m,然后将输入分成(n-1)个大小为(M-m)/(n-1)的桶,将处于一对桶边界上的值放入两个桶中。根据鸽笼原理,至少一个桶中必须有两个值。
(M-m)/(n-1)
! - Damien