检查未排序数组中是否存在[i] = 2 * a [j]?

时间:2012-03-31 15:44:20

标签: algorithm

给定一个[1,...,n]整数的未排序序列,给出一个O(nlogn)运行时算法来检查有两个索引i和j,使a [i] = 2 * a [j ]。该算法应在输入4,12,8,10上返回i = 0和j = 2,在输入4,3,1,11上返回false。

我认为无论如何我们必须对数组进行排序,即O(nlogn)。我不知道在那之后该怎么办。

6 个答案:

答案 0 :(得分:12)

注意:可以使用hash table平均在O(n) 1 上完成。

set <- new hash set
for each x in array:
   set.add(2*x)
for each x in array:
   if set.contains(x): 
        return true
return false

<强>证明:
=&GT;
如果有2个元素a[i]a[j],那么a[i] = 2 * a[j],那么当我们第一次迭代时,我们在读{{1}时将2*a[j]插入到集合中}}。在第二次迭代中,我们发现a[j]已设置,并返回true。

&LT =
如果算法返回true,则它找到a[i] == 2* a[j],使得a[i]已经在第二次迭代中的集合中。因此,在第一次启动时 - 我们插入了a[i]。只有在存在a[i]的第二个元素a[j]时才能执行此操作,并且在阅读a[i] == 2 * a[j]时我们插入了a[i]

注意:
为了返回元素的索引,可以简单地使用哈希图而不是集合,并且对于每个a[j]商店i作为关键字和2*a[i]作为价值。

示例:
i

首先为每个x - 2x插入哈希表和索引。你会得到:

Input = [4,12,8,10]

现在,在secod迭代中,检查每个元素是否在表中:

hashTable = {(8,0),(24,1),(16,2),(20,3)}

所以,最终输出是2.0 - 正如预期的那样。


(1)复杂性通知:
在这里,arr[0]: 4 is not in the table arr[1]: 12 is not in the table arr[2]: 8 is in the table - return the current index [2] and the value of 8 in the map, which is 0. 假设O(n)哈希函数。这并非总是如此。如果我们假设O(1)哈希函数,我们也可以假设使用radix-sort进行排序O(1),并使用O(n)的后处理[类似于@SteveJessop建议的那个]他的答案],我们也可以通过基于排序的算法实现O(n)

答案 1 :(得分:10)

  1. 如果您愿意扩展关于固定大小整数数组的观点,请对数组(O(n log n)O(n)进行排序)
  2. 在数组的开头(O(1))初始化两个指针(“fast”和“slow”)
  3. 反复:
    1. 增加“快速”,直到您找到偶数值&gt; =“慢”的值的两倍
    2. 如果“fast”的值恰好是“slow”值的两倍,则返回true
    3. 增加“慢”,直到找到值> = fast
    4. 的值的一半
    5. 如果“slow”的值恰好是“fast”值的一半,则返回true
  4. 如果其中一次增加尝试超过了结尾,则返回false
  5. 由于fastslow中的每一个都可以在到达数组末尾之前最多增加n次,因此“重复”部分为O(n)。< / p>

答案 2 :(得分:8)

你是对的,第一步是对数组进行排序。

对数组进行排序后,您可以在O(log n)时间内查明给定元素是否在数组内。因此,如果对于每个n元素,检查是否在O(log n)时间内包含了另一个元素,则最终会得到O(n log n)的运行时。

这对你有帮助吗?

答案 3 :(得分:1)

  1. 创建一对数组A = {(a [0],0),(a [1],1),...,(a [n-1],n-1)}
  2. 排序A,
  3. 对于A中的每个(a [i],i),进行二分搜索以查看是否存在(a [i] * 2,j)对。我们可以这样做,因为A是有序的。
  4. 步骤1是O(n),步骤2和3是O(n * log n)。

    此外,您可以在O(n)中执行第3步(不需要二进制搜索)。因为如果A [i]的对应元素是A [j],那么A [i + 1]的对应元素不能在A [0..j-1]中。所以我们可以保留两个指针,并在O(n)中找到答案。但无论如何,整个算法将是O(n log n)因为我们仍然进行排序。

答案 4 :(得分:0)

对数组进行排序是一个很好的选择--O(nlogn),假设你没有一些奇特的桶排序选项。

一旦它被排序,你只需要两次通过数组 - 我相信这是O(n)

创建一个“双打”列表,该列表从空开始。

然后,对于数组的每个元素:

  • 根据“双打”列表的第一个元素检查元素
    • 如果相同,则获胜
    • 如果元素较高,则抛弃“双打”列表的第一个元素并再次检查
  • 将其双精度添加到“双打”列表的末尾

  • 继续前行,直至找到双倍,或者到达第一个列表的末尾。

答案 5 :(得分:0)

您也可以使用平衡树,但它使用额外的空间,但也不会损坏数组。

i=0开始,并递增i,插入元素,检查树中是否存在当前元素的两倍或一半。

一个优点是它可以在O(M log M) M = min [max{i,j}]时间内工作。您可能会更改基于排序的算法以尝试执行O(M log M)但它可能会变得复杂。

顺便说一句,如果你只使用比较,那么通过将元素清晰度问题减少到这个,有Omega(n log n)下限:

复制输入数组。将此算法用于此问题两次。因此,除非您将散列类型的内容添加到图片中,否则您无法获得比Theta(n log n)算法更好的内容!