LeetCode – Find First and Last Position of Element in Sorted Array (Java)

Given a sorted array of integers, find the starting and ending position of a given target value. Your algorithm’s runtime complexity must be in the order of O(log n). If the target is not found in the array, return [-1, -1]. For example, given [5, 7, 7, 8, 8, 10] and target value 8, return [3, 4].

Analysis

Based on the requirement of O(log n), this is a binary search problem apparently.

Java Solution 1 – Log(n)

We can first find the start and then the end of the target.

public int[] searchRange(int[] nums, int target) {
    int l=0;
    int r=nums.length-1;
 
    while(l<r){
        int m=l+(r-l)/2;
        if(nums[m]<target){
            l=m+1;
        }else{
            r=m;
        }
    }
 
    int first=l;
    if(l<nums.length&&nums[l]==target){//l is in boundary and is the target
        l=0;
        r=nums.length-1;
        while(l<r){
            int m=l+(r-l+1)/2;
            if(nums[m]>target){
                r=m-1;
            }else{
                l=m;
            }
        }
 
        return new int[]{first, r};
    }
 
    return new int[]{-1,-1};
}

Java Solution 2 – (Deprecated)

public int[] searchRange(int[] nums, int target) {
    if(nums == null || nums.length == 0){
        return null;
    }
 
    int[] arr= new int[2];
    arr[0]=-1;
    arr[1]=-1;
 
    binarySearch(nums, 0, nums.length-1, target, arr);
 
    return arr;
}
 
public void binarySearch(int[] nums, int left, int right, int target, int[] arr){
    if(right<left) 
        return;
 
    if(nums[left]==nums[right] && nums[left]==target){
        arr[0]=left;
        arr[1]=right;
        return;
    }
 
    int mid = left+(right-left)/2;
 
 
    if(nums[mid]<target){
        binarySearch(nums, mid+1, right, target, arr);
    }else if(nums[mid]>target){
        binarySearch(nums, left, mid-1, target, arr);
    }else{
        arr[0]=mid;
        arr[1]=mid;
 
        //handle duplicates - left
        int t1 = mid;
        while(t1 >left && nums[t1]==nums[t1-1]){
            t1--;
            arr[0]=t1;
        }
 
        //handle duplicates - right
        int t2 = mid;
        while(t2 < right&& nums[t2]==nums[t2+1]){
            t2++;
            arr[1]=t2;
        }
        return;
    }
}

In the worst case, the time of the second solution is actually O(n).

8 thoughts on “LeetCode – Find First and Last Position of Element in Sorted Array (Java)”

  1. Very similar iterative solution. Added usage of “fail fast” methodology.
    Recursive solution also very similar.

    Iterative:

    int[] searchRange(int[] nums, int target)
    {
    if (nums == null ||
    nums.length == 0 ||
    target < nums[0] ||
    nums[nums.length-1] < target) return new int[]{-1, -1};

    int l = 0;
    int h = nums.length - 1;

    /* left: */
    while (l < h)
    {
    int m = (l+h)/2;
    if (nums[m] < target)
    {
    l = m + 1;
    }
    else
    {
    h = m;
    {
    }

    if (l == nums.length || nums[l] != target) return int[]{-1, -1};

    int left = l;

    h = nums.length - 1;

    /* Right: */
    while (l < h)
    {
    int m = (l+h+1)/2;
    if (target < nums[m])
    {
    h = m - 1;
    }
    else
    {
    l = m;
    }
    }

    return new int[]{left, h};
    }

    Recursive:

    // TODO: add const for `new int[]{-1, -1}`
    int[] searchRange(int[] nums, int target)
    {
    if (nums == null || num.length == 0) return new int[]{-1, -1};

    return searchRange(nums, 0, nums.length-1, int target)
    }

    int[] searchForRange(int[] nums, int from, int to, int target)
    {
    if (target < nums[from] || nums[to] < target) return new int[]{-1, -1};

    if (from == to) return new int[]{from, to};

    int mid = (from + to)/2;

    int[] rangeLower = searchForRange(nums, from, mid, target);

    if (rangeLower[0] != -1 && rangeLower[1] != mid) return rangeLower;

    int[] rangeHigher = searchRange(nums, mid+1, to, target);

    if (rangeHigher[0] == -1) return rangeLower;

    if (rangeLower[0] == -1) return rangeHigher;

    return new int[]{rangeLower[0], rangeHigher[1]};
    }

  2. The solution given is O(N) and not O(logN). Consider the example {1,6,6,6,6,6,6,6,6,6,6,8}. It will have to do operations N times here.
    In order to find the left and the right indices you would need to do the binary search again one for left side and other for right side of the mid if the value at mid equals the target. Below is my iterative code solution:


    public class Solution {
    public int[] searchRange(int[] nums, int target) {
    int out[]=new int[2];
    int l=0,r=nums.length-1;
    while(l<=r){
    int mid=(l+r)/2;
    if(nums[mid]==target){
    if(mid-1<0 || nums[mid-1]=nums.length || nums[mid+1]>target){
    out[1]=mid;
    }
    else{
    out[1]=rBSearch(nums,mid+1,target);
    }
    return out;
    }
    else if(nums[mid]>target){
    r=mid-1;
    }
    else{
    l=mid+1;
    }
    }
    out[0]=-1;
    out[1]=-1;
    return out;
    }

    private int lBSearch(int nums[],int end,int target){
    int l=0,r=end;
    while(l<=r){
    int mid=(l+r)/2;
    if(nums[mid]==target){
    if(mid-1<0 || nums[mid-1]<target){
    return mid;
    }
    else{
    r=mid-1;
    }
    }
    else{
    l=mid+1;
    }
    }
    return end;
    }

    private int rBSearch(int nums[],int start,int target){
    int l=start,r=nums.length-1;
    while(l=nums.length || nums[mid+1]>target){
    return mid;
    }
    else{
    l=mid+1;
    }
    }
    else{
    r=mid-1;
    }
    }
    return start;
    }
    }

  3. I think the solution is fine, because the number of work required to find the left and right border using binary search is just the same as finding the element itself and scanning for the left and right borders.

  4. Great answer! But you should make the change from right – 1 to mid – 1 in:

    else if(arr[mid] > target) return solution(arr, target, left, mid-1);

  5. What’s wrong with the above examplary solution? The question has explicitly required the time complexity to be O(logN), which obviously is not the correct answer.


    public static int[] solution(int[] arr, int target, int left, int right) {
    //parameter check
    if(arr == null || arr.length == 0) return new int[]{-1, -1};

    //boundary check
    if(left > right) return new int[]{-1, -1};

    int mid = (left+right)/2;
    if(arr[mid] target) return solution(arr, target, left, right-1);
    else {
    int[] leftRange = solution(arr, target, left, mid-1);
    int[] rightRange = solution(arr, target, mid+1, right);
    int leftEnd = (leftRange[0] == -1 ? mid : leftRange[0]);
    int rightEnd = (rightRange[1] == -1 ? mid : rightRange[1]);
    return new int[]{leftEnd, rightEnd};
    }
    }

    public static void main(String[] args) {
    System.out.println(Arrays.toString(solution(new int[]{1,8,8,8,9}, 8, 0, 4)));
    System.out.println(Arrays.toString(solution(new int[]{1,2,8,8,8,9}, 8, 0, 5)));
    System.out.println(Arrays.toString(solution(new int[]{1,2,3,8,8,8,9}, 8, 0, 6)));
    System.out.println(Arrays.toString(solution(new int[]{1,2,3,4,8,8,8,9}, 8, 0, 7)));
    System.out.println(Arrays.toString(solution(new int[]{1,2,3,4,5,8,8,9}, 8, 0, 7)));
    System.out.println(Arrays.toString(solution(new int[]{1,2,3,4,5,6,8,9}, 8, 0, 7)));
    System.out.println(Arrays.toString(solution(new int[]{1,2,3,4,8,8,8,9}, 7, 0, 7)));
    }

  6. We can use below code which has O(log N) complexity. This is initial structure, we can even modify further to clean it. (This is C# code)

    public int[] searchRange(int[] nums, int target)
    {
    int[] result = new int[2];
    int startIndex = 0;
    int endIndex = nums.Length – 1;
    int mid = -1;
    bool foundTarget = false;

    if (nums.Length > 0)
    {
    while (startIndex < endIndex)
    {
    mid = (startIndex + endIndex)/2;

    if (target == nums[mid])
    {
    foundTarget = true;
    break;
    }

    if (target nums[mid])
    startIndex = mid+1;
    }

    if (foundTarget)
    {
    startIndex = mid;
    endIndex = mid;

    while ((startIndex – 1) >= 0 && (nums[startIndex – 1] == target))
    startIndex–;

    while ((endIndex + 1) <= nums.Length && (nums[endIndex + 1] == target))
    endIndex++;

    result[0] = startIndex;
    result[1] = endIndex;
    }
    }

    return result;
    }

  7. this solution above time complexity is not log N, it clearly is N, which does not match the requirements.

Leave a Comment