Note: what can be done in O(n) 1 on average using a hash table.
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
Evidence:
=>
If there are 2 elements a[i] and a[j] such that a[i] = 2 * a[j] , then at the first iteration we insert 2*a[j] into the set when we read a[j] . In the second iteration, we find that a[i] == 2* a[j] is in the set and returns true.
<=
If the algorithm returned true, then he found a[i] in such a way that a[i] already in the set at the second iteration. So, during the first iteration, we inserted a[i] . This can be done only if there is a second element a[j] such that a[i] == 2 * a[j] , and we inserted a[i] when reading a[j] .
Note:
To return element indices, you can simply use a hash map instead of a set, and for each i save 2*a[i] as a key and i as a value.
Example:
Input = [4,12,8,10]
first insert for each x - 2x hash table and index. You'll get:
hashTable = {(8,0),(24,1),(16,2),(20,3)}
Now, in the iteration of secod, you check each element if it is in the table:
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.
therefore, the final result is 2.0 - as expected.
(1) Notification of difficulty:
Here O(n) takes an O(1) hash function. This is not always true. If we accept the hash function O(1) , we can also assume that sorting using radix-sort is O(n) , and using post-processing O(n) [similar to the one suggested by @SteveJessop in his answer ], we can also achieve O(n) using a sorting algorithm.