Sensiblewings47
Sensiblewings47

Reputation: 566

Efficient algo to find number of integers in a sorted array that are within a certain range in O(log(N)) time?

I came across a interview question that has to be done in O(logn)

Given a sorted integer array and a number, find the start and end indexes of the number in the array.

Ex1: Array = {0,0,2,3,3,3,3,4,7,7,9} and Number = 3 --> Output = {3,6} 

Ex2: Array = {0,0,2,3,3,3,3,4,7,7,9} and Number = 5 --> Output = {-1,-1} 

I am trying to find an efficient algo for this but so fat have not been successful.

Upvotes: 9

Views: 1288

Answers (6)

M. Irvin
M. Irvin

Reputation: 153

It may be error on my end, but Ron Teller's answer has an infinite loop when I've tested it. Here's a working example in Java, that can be tested here if you change the searchRange function to not be static.

import java.io.*;
import java.util.*;
import java.text.*;
import java.math.*;
import java.util.regex.*;

public class RangeInArray {
    // DO NOT MODIFY THE LIST
    public static ArrayList<Integer> searchRange(final List<Integer> a, int b) {
        ArrayList<Integer> range = new ArrayList<>();

        int startIndex = findStartIndex(a, b);

        if(a.get(startIndex) != b) {
            range.add(-1);
            range.add(-1);
            return range;
        }

        range.add(startIndex);
        range.add(findEndIndex(a, b));
        return range;
    }

    public static int findStartIndex(List<Integer> a, int b) {
        int midIndex = 0, lowerBound = 0, upperBound = a.size() - 1;

        while(lowerBound < upperBound) {
            midIndex = (upperBound + lowerBound) / 2;
            if(b <= a.get(midIndex)) upperBound = midIndex - 1;
            else lowerBound = midIndex + 1;
        }

        if(a.get(lowerBound) == b) return lowerBound;
        return lowerBound + 1;
    }

    public static int findEndIndex(List<Integer> a, int b) {
        int midIndex = 0, lowerBound = 0, upperBound = a.size() - 1;

        while(lowerBound < upperBound) {
            midIndex = (upperBound + lowerBound) / 2;
            if(b < a.get(midIndex)) upperBound = midIndex - 1;
            else lowerBound = midIndex + 1;
        }

        if(a.get(lowerBound) == b) return lowerBound;
        return lowerBound - 1;
    }

    public static void main(String[] args) {
        ArrayList<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(1);
        list.add(2);
        list.add(2);
        list.add(2);
        list.add(2);
        list.add(2);
        list.add(2);
        list.add(3);
        list.add(4);
        list.add(4);
        list.add(4);
        list.add(4);
        list.add(5);
        list.add(5);
        list.add(5);
        System.out.println("Calling search range");
        for(int n : searchRange(list, 2)) {
            System.out.print(n + " ");
        }
    }
}

Upvotes: 0

John Kurlak
John Kurlak

Reputation: 6690

Since no one has posted working code yet, I'll post some (Java):

public class DuplicateNumberRangeFinder {
    public static void main(String[] args) {
        int[] nums = { 0, 0, 2, 3, 3, 3, 3, 4, 7, 7, 9 };
        Range range = findDuplicateNumberRange(nums, 3);

        System.out.println(range);
    }

    public static Range findDuplicateNumberRange(int[] nums, int toFind) {
        Range notFound = new Range(-1, -1);

        if (nums == null || nums.length == 0) {
            return notFound;
        }

        int startIndex = notFound.startIndex;
        int endIndex = notFound.endIndex;
        int n = nums.length;
        int low = 0;
        int high = n - 1;

        while (low <= high) {
            int mid = low + (high - low) / 2;

            if (nums[mid] == toFind && (mid == 0 || nums[mid - 1] < toFind)) {
                startIndex = mid;
                break;
            } else if (nums[mid] < toFind) {
                low = mid + 1;
            } else if (nums[mid] >= toFind) {
                high = mid - 1;
            }
        }

        low = 0;
        high = n - 1;

        while (low <= high) {
            int mid = low + (high - low) / 2;

            if (nums[mid] == toFind && (mid == n - 1 || nums[mid + 1] > toFind)) {
                endIndex = mid;
                break;
            } else if (nums[mid] <= toFind) {
                low = mid + 1;
            } else if (nums[mid] > toFind) {
                high = mid - 1;
            }
        }

        return new Range(startIndex, endIndex);
    }

    private static class Range {
        int startIndex;
        int endIndex;

        public Range(int startIndex, int endIndex) {
            this.startIndex = startIndex;
            this.endIndex = endIndex;
        }

        public String toString() {
            return "[" + this.startIndex + ", " + this.endIndex + "]";
        }
    }
}

Upvotes: 0

Ron Teller
Ron Teller

Reputation: 1890

You can use the concept of binary search to find the starting and ending index:

  • To find the starting index, you halve the array, if the value is equal to or greater than the input number, repeat with the lower half of the array, otherwise repeat with the higher half. stop when you reached an array of size 1.
  • To find the starting index, you halve the array, if the value is greater than the input number, repeat with the lower half of the array, otherwise repeat with the higher half. stop when you reached an array of size 1.

Note that when we reached an array of size 1, we may be one cell next to the input number, so we check if it equals the input number, if not, we fix the index by adding/decreasing 1 from the index we found.

findStartIndex(int[] A, int num)
{
    int start = 0; end = A.length-1;

    while (end != start) 
    {
        mid = (end - start)/2;

        if (A[mid] >= num)
            end = mid;
        else
            start = mid;
    }

    if(A[start] == num) 
        return start;
    else 
        return start+1;
}

findEndIndex(int[] A, int num)
{
    int start = 0; end = A.length-1;

    while (end != start) 
    {
        mid = (end - start)/2;

        if (A[mid] > num)
            end = mid;
        else
            start = mid;
    }

    if(A[start] == num) 
        return start;
    else 
        return start-1;
}

And the whole procedure:

int start = findStartIndex(A, num);

if (A[start]!=num) 
{ 
     print("-1,-1"); 
}
else
{
     int end = findEndIndex(A, num);
     print(start, end);
}

Upvotes: 4

SQB
SQB

Reputation: 4078

Double binary search. You start with lower index = 0, upper index = length - 1. Then you check the point halfway and adjust your indexes accordingly.

The trick is that once you've found target, the pivot splits in two pivots.

Upvotes: 0

aaronman
aaronman

Reputation: 18771

The solution is to binary search the array concurrently (does't actually have to be concurrent :P ) at the start. The key is that the left and right searches are slightly different. For the right side if you encounter a dupe you have to search to the right, and for the left side if you encounter a dupe you search to the left. what you are searching for is the boundary so on the right side you check for.

 yournum, not_yournum  

This is the boundary and on the left side you just search for the boundary in the opposite direction. At the end return the indices of the boundaries.

Upvotes: 2

HC_
HC_

Reputation: 1060

Sounds like a binary search -- log graphs iirc represent the effect of "halving" with each increment, which basically is binary search.

Pseudocode:

Set number to search for
Get length of array, check if number is at the half point
if the half is > the #, check the half of the bottom half. is <, do the inverse
repeat
    if the half point is the #, mark the first time this happens as a variable storing its index
    then repeat binary searches above , and then binary searches below (separately), such that you check for how far to the left/right it can repeat.
    note*: and you sort binary left/right instead of just incrementally, in case your code is tested in a dataset with like 1,000,000 3's in a row or something

Is this clear enough to go from there?

Upvotes: 2

Related Questions