34. Search for a Range

  • 原题
    Given an array of integers nums sorted in ascending order, 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].
    (给定一个升序数组,找到目标值的起始位置和结束位置。时间复杂度需为O(log n)。 如果目标值没找到,返回 [-1, -1] )

  • 思路一
    在有序数组中查找值,首先想到的就是二分查找,但是这道题是需要找到起始位置和结束位置。那么也很容易想到,我通过二分查找找到目标值后,然后以当前目标值的下标为终点,继续向前查找目标值,如此循环直到二分查找没有找到为止。同理,结束位置也是如此的思路。
    AC解一

class Solution {
    public int[] searchRange(int[] nums, int target) {
        int len = nums.length, start = 0, end = len - 1;
        int min, max;
        min = max = find(nums, start, end, target);
        while(true) {
            int minS = find(nums, start, min - 1, target);
            if( minS == -1) break;
            else min = minS;
        }
        while(true) {
            int maxL = find(nums, max + 1, end, target);
            if( maxL == -1) break;
            else max = maxL;
        }
        return new int[]{min, max};
    }

    private int find(int[] nums, int start, int end, int target) {
        while(start <= end) {
            int mid = (start + end) / 2;
            if(target == nums[mid]) return mid; 
            else if(target > nums[mid]) start = mid + 1;
            else end = mid - 1;
        }
        return -1;
    }
}

  • 思路二:
    对二分查找做个变种。当找到目标值时不急着立马返回下标,而是继续向前查找直到找到最小值。其中的关键点就是将小于等于目标值这两种情况合并起来判断,并让 end = mid,而不再是 end = mid - 1,因为此时 target <= nums[mid] ,所以你无法将 end = mid - 1。
    其实就是对思路一的简化。至于最大下标则是查找比目标值大1的数的下标减1 ,即 find(nums, target + 1) - 1。
    AC解
class Solution {
    public int[] searchRange(int[] nums, int target) {
        int s = find(nums, target);
        if(s == nums.length || nums[s] != target) return new int[]{-1, -1};
        if(s == nums.length - 1) return new int[]{s, s};
        return new int[]{s, find(nums, target + 1) - 1};
    }

    private int find(int[] nums, int target) {
        int start = 0, end = nums.length;
        while(start < end) {
            int mid = (end + start) / 2;
            if(target > nums[mid]) start = mid + 1;
            else end = mid;
        }
        return start;
    }
}