0

Given: sorted integer array, integers are all different - no duplicates.

Problem: Write an algorithm (as good as possible in runtime) based on divide and conquer for checking if there is an index i that A[i]=i exists in the array.

Well I thought about Binary Search which is O(logn) run time complexity. Is there something faster than that?

12
  • Faster than that is only O(1) and I don't think you can find an algorithm for solving your problem in that time. So, yes, Binary Search is your best option. Commented Jun 1, 2017 at 20:05
  • Binary search seems to be the obvious choice. O(loglogn) would also be faster than O(logn) but I cannot see how this would be achieved. Commented Jun 1, 2017 at 20:26
  • 2
    Divide and conquer also implies O(logn) because no matter by what constant c the input is divided, it is still O(logn). E.g. if c = 4 would give log of base 4 which is logn/log4 and because log4 is a constant it is still O(logn). Commented Jun 1, 2017 at 20:34
  • @maraca I see. Thank you Maraca Commented Jun 1, 2017 at 20:38
  • 1
    Is there anything known about the range of the values, and their distribution in that range? Commented Jun 1, 2017 at 21:37

3 Answers 3

2

The requirement that the input is sorted in itself is not enough. The additional requirement that there are no two equal values in the input array is a necessary condition to be able to use a binary search.

If that would not be a condition, you could not use a binary search, as can be seen in the following example (assuming 0 is the first index):

    i:   0 1 2 3 4 5 6
        --------------
  A[i]: -1 0 x 4 y 6 7
               ^

With a binary search you'd take the middle element and decide at which side of it there could be an i with A[i]=i. The problem is that in the above example the solution could be at either side of the centre: if x=2, then the solution is at the left, and when y=4, the solution is at the right. So divide and conquer will not work. Only when it is assured that the input has no duplicate values, a divide and conquer algorithm can work.

On top of that you can immediately exclude input arrays where the first value is greater than 0, because then there is no way to achieve A[i]=i without having duplicate values. Similarly, an input array where the last value is less than the last index, does not have an i with A[i]=i.

This consideration will also work during the divide and conquer. Take this example:

    i:   0 1 2 3 4 5 6 7 8
        -------------------
  A[i]: -4 0 1 2 5 6 7 8 10

At first both values at the ends are verified: they don't exclude a solution, so the middle value at index 4 is taken. Since its value (5) is greater than the index (4), the range from index 4 to 8 can be ignored. So in the next iteration of the algorithm the range between index 0 and 3 (included) are considered.

But the value at the rightmost index (3) is less than 3 (it is 2). By the above mentioned rule, this means there cannot be a solution, and so the algorithm can stop right there: making more divisions would be futile.

So here is a JavaScript implementation for this:

function hasSelfReference(arr) {
    let last = arr.length - 1;
    if (last < 0 || arr[0] > 0 || arr[last] < last) return false;

    let first = 0;
    while (first < last) {
        let mid = (first + last) >> 1;
        if (arr[mid] < mid) {
            first = mid + 1;
            if (arr[first] > first) return false;
        } else if (arr[mid] > mid) {
            last = mid - 1;
            if (arr[last] < last) return false;
        } else 
            return true;
    }
    return arr[first] === first; // arr[first] may be undefined: not a problem in JS
}

console.log(hasSelfReference([3, 4, 5, 6])); // false
console.log(hasSelfReference([-4, -2, 1, 2, 7])); // false
console.log(hasSelfReference([-4, -2, 1, 3, 7])); // true
console.log(hasSelfReference([])); // false
console.log(hasSelfReference([-4, 0, 1, 2, 5, 6, 7, 8, 10])); // false

As with the usual divide and conquer algorithms, this has a O(logn) (worst-case) time complexity.

When the array has a matching index, then the number of iterations is logn, but when there is no match, and the array is large, there will often be an early exit out of the loop.

Sign up to request clarification or add additional context in comments.

Comments

2

Let's look at an example: assume A[i]=i-1 for all indices exept k, where A[k]=k. Just sampling the array at one place does not tell you anything about the position of k (unless you happen to hit k by pure luck).

Therefore, I don't think that you can get a worst case runtime better than O(n).

2 Comments

what if the integers are all different?
@IlanAizelmanWS I don't understand your comment. Which difference does this make? And btw. in my example all integers are different (with at most one exception).
1

I think it will be best to use binary search as

1) You are given sorted integers

2) You need a divide and conquer algorithm

3) Running time of binary search is O(logn) which is better than linear search

3 Comments

Just has one little drawback: it's not applicable here.
what if the integers are all different?
@IlanAizelmanWS it will work efficiently for different integers. Different integers will not effect the time complexity or efficiency of binary search algorithm

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.