检查数字是否在数组中,复杂性最小

时间:2013-07-16 12:16:59

标签: algorithm language-agnostic

给出一个数组,任何两个后续元素之间的距离是一(+1或-1)。我们给出了一个数字。我们如何以最小的复杂度检查数字是否在数组中。

6 个答案:

答案 0 :(得分:8)

您可以进行二元搜索。

如果我们要查找的元素位于第一个和最后一个元素之间,我们知道该元素出现在数组中,我们可以停止。

如果不是,请检查数组中可能出现的最小和最大可能值,方法是找出第一个和最后一个元素之间的差异,减去元素数量,将其除以2,然后从/减去/添加两者的最小/最大。

更明确地说:

temp = abs(arr[left] - arr[right]) - (left - right)
minPossible = min(arr[left], arr[right]) - 2*temp
maxPossible = max(arr[left], arr[right]) + 2*temp

递归重复,将数组分成两半(或拆分as Daniel suggested)。

为什么上面给出了最小/最大可能:

可以考虑如下:你需要一些元素,这些元素等于从左边和右边的差异,从一个到另一个。除此之外,对于每一步,你需要再次上/下一步。

仍然不幸的是O(n)最坏的情况。

这就是O(n)最坏情况不能被击败的原因:

(类似于David's proof)。

示例输入= [1,0,1,0,1,0,1,0]

假设我们正在寻找2。

显然不存在,但是如果0中的一个被改为2怎么办?我们需要检查每个零。一旦我们跳过一个,那个就可能是2。

因此我们必须检查至少1/2的元素,因此它仍然是O(n / 2)= O(n)。

答案 1 :(得分:5)

您可以使用以下算法保存检查某些(可能是大多数)元素。

如果你的数字是85并且数组的第一个数字是100,你可以跳过(15-1)= 14个数字(当然15是100和85之间的距离)因为它们最接近85是99,98,97,...,86。所以你只需检查第15个数字。如果该数字不是85,请继续重复相同的算法。这使你可以跳过数组,它仍然是O(N)但可能在时钟时间上比单独检查更快。

最糟糕的情况是:我正在寻找85。

  • 第一个数字是86.我不能跳过任何数字,因为(1-1)= 0而下一个数字很可能是85。
  • 我检查下一个号码。它是87.啊,现在我可以跳过一个号,因为(2-1)= 1;我跳过的下一个号码可能是88或86,但从不85.
  • 我检查其他下一个号码,它是86。
  • 一切都是一样的,因为阵列实际上是86,87,86,87,86,87 ......所以我最终检查了所有的87,这几乎是数字的一半。

在我阅读this answer之前,我认为这是最佳算法。

答案 2 :(得分:1)

此问题预计单元探测器复杂度为Ω(n),因此不会发生次线性算法。考虑可能的输入

210101010...10
012101010...10
010121010...10
...
010101010...12

概率相等。通过Yao的引理,对于固定输入分布,最佳随机算法并不比最佳确定性算法更好。在找到2之前,所有未被排除的输入看起来都相同。因此,每个正确的确定性算法必须以某种顺序探测0个位置,探测到预期的n / 4(或其附近)位置,直到找到2。

答案 3 :(得分:0)

当然,复杂性不能低于O(N)。那说我会经历整个阵列。

无论何时

  • 读取值减去所需数量的绝对值大于OR后面的元素数量
  • 读取值等于期望值

我会“破解”算法,将“是”作为答案。

默认情况下,答案是“不”! :)

答案 4 :(得分:0)

假设您在数组中查找值v。如果在给定位置i,值a[i]v-d,我们要查找的值至少距离d i个单元格的距离。因此,您可以跳过其间的单元格,因为最多可以包含值v-d+1v-d+2等等。所以这里有一个草率的递归算法:

template <class ForwardIter, class T>
ForwardIter find_in_singlestep_range(ForwardIter first, ForwardIter last, T val) {
  if (first == last) return last;
  if (*first == val) return first;

  auto max_diff = last-first;
  auto diff = make_signed<T>{val} - make_signed<T>{*first};
  if (diff < 0) diff = -diff;
  if (diff > max_diff) return last;

  return find_in_singlestep_range(first+diff, last, val);
}

复杂性: 你将获得最多1/2 * N +1比较,因为假设你从未遇到过该元素,最坏的情况可能是diff是1,2,2,2,2,所以你跳过每一个元素。 (如果diff是2,则下一个diff只能是2或4。)

如果你进行二分法搜索,也许你有更好的复杂性:如果子阵列的中间元素与val的差异大于子阵列大小的一半,你可以跳过子阵列中的搜索。

答案 5 :(得分:0)

由于元素之间的差异为1(+1或-1),因此min(array)max(array)之间的任何数字都将在数组中。如果您缓存最小值和最大值,则第一次搜索为O(N),其他每次搜索都为O(1)

如果数组的元素是分数而不是整数,那么有一个额外的先决条件测试,fractionPart(min(array))-fractionPart(number)==0