Given a unsorted sequence of a[1,...,n] of integers, give an O(nlogn) runtime algorithm to check there are two indices i and j such that a[i] =2*a[j]. The algorithm should return i=0 and j=2 on input 4,12,8,10 and false on input 4,3,1,11.
I think we have to sort the array anyways which is O(nlogn). I'm not sure what to do after that.
Note: that can be done on 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
Proof:
=>
If there are 2 elements a[i]
and a[j]
such that a[i] = 2 * a[j]
, then when iterating first time, we inserted 2*a[j]
to the set when we read a[j]
. On the second iteration, we find that a[i] == 2* a[j]
is in set, and return true.
<=
If the algorithm returned true, then it found a[i]
such that a[i]
is already in the set in second iteration. So, during first itetation - we inserted a[i]
. That only can be done 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:
In order to return the indices of the elemets, one can simply use a hash-map instead of a set, and for each i
store 2*a[i]
as key and i
as value.
Example:
Input = [4,12,8,10]
first insert for each x - 2x to the hash table, and the index. You will get:
hashTable = {(8,0),(24,1),(16,2),(20,3)}
Now, on secod iteration you check for 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.
so, final output is 2,0 - as expected.
(1) Complexity notice:
In here, O(n)
assumes O(1)
hash function. This is not always true. If we do assume O(1)
hash function, we can also assume sorting with radix-sort is O(n)
, and using a post-processing of O(n)
[similar to the one suggested by @SteveJessop in his answer], we can also achieve O(n)
with sorting-based algorithm.
O(n log n)
, or O(n)
if you're willing to stretch a point about arrays of fixed-size integers)O(1)
)fast
Since each of fast
and slow
can be incremented at most n
times total before reaching the end of the array, the "repeatedly" part is O(n)
.
You're right that the first step is sorting the array.
Once the array is sorted, you can find out whether a given element is inside the array in O(log n)
time. So if for every of the n
elements, you check for the inclusion of another element in O(log n)
time, you end up with a runtime of O(n log n)
.
Does that help you?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With