LeetCode | Search for a Range(查找数据出现的范围)


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].

查找某一个值出现在数组中的范围。

方案一:

先二分查找到key,然后再二分查找,知道arr[mid]==key && arr[mid-1] < key时为最左边界,arr[mid]==key && arr[mid-1] > key为最右边界。但是要注意考察mid==0 和 mid==n-1时的情况。所以判断时还要加强条件。代码如下

int SearchRange(int arr[], int n, int key,int *left,int *right)
{
	int lo = 0, hi = n - 1, mid = 0;
	int lhi, rlo;

	while (lo <= hi){
		mid = (lo + hi) / 2;
		if (arr[mid] == key){
			lhi = mid;	//要取等于mid,防止左半区不再含有key值
			rlo = mid;	//同上,防止右半区不含key值
			while (lo <= lhi){
				mid = (lo + lhi) / 2;
				if (arr[mid] == key && (mid == 0 || arr[mid - 1] < key)){
					*left = mid;
					break;
				}
				if (arr[mid] >= key)	//arr[mid] == key的情况要放入这个里面
					lhi = mid - 1;
				else
					lo = mid + 1;
			}
			while (rlo <= hi){
				mid = (rlo + hi) / 2;
				if (arr[mid] == key && (mid == n - 1 || arr[mid + 1] > key)){
					*right = mid;
					break;
				}
				
				if (arr[mid] > key)
					hi = mid - 1;
				else			//arr[mid] == key的情况要放入这个里面
					rlo = mid + 1;
			}
			return 1;
		}
		if (arr[mid] > key)
			hi = mid - 1;
		else
			lo = mid + 1;
	}

	return 0;
}

方案二:

上面写的有些繁琐,还要先查到再左右加强条件遍历。我们可以分两次二分查找,第一次查找最左端,当arr[mid] >= key时,依然让hi = mid-1。这样得到的越界l刚好为左边界left;同理,第二次查找右边界,当arr[mid] <= key时,依然让l = mid+1。越界的r刚好为右边界。(这两个地方需要仔细考虑好,不然又会像上面一样,在中间添加很多代码来判断)

上面的思路充分利用了循环l<=r的特性。合适超出边界。还有判断时,等号应处于哪个范围。

当然最后还要判断查找失败时的情况。

class Solution {
//Binary Search
//we must know how to process the 3 cases below:
//1.how to find the right most target
//2.how to find the left most target
//3.how to find the insert position
public:
	vector<int> searchRange(int A[], int n, int target) {
		// Start typing your C/C++ solution below
		// DO NOT write int main() function
		int l = 0;
		int r = n-1;
		while (l <= r)
		{
			int mid = l+(r-l)/2;
			if (A[mid] >= target)//find the left most target 
				r = mid-1;
			else if (A[mid] < target)
				l = mid+1;
		}
		int left = l;
		l = 0; 
		r = n-1;
		while (l <= r)
		{
			int mid = l+(r-l)/2;
			if (A[mid] > target)//find the right most target
				r = mid-1;
			else if (A[mid] <= target)
				l = mid+1;
		}
		int right = r;
		if(A[left] != target || A[right] != target)
			left = right = -1;
		vector<int> ans(2);
		ans[0] = left;
		ans[1] = right;
		return ans;
	}
};

还有一种是传递一个标志符,开表示查找左边界还是右边界。简化了代码。但要考虑清楚如何判断

class Solution {
public:
    int findPos(int a[], int beg, int end, int key, bool findLeft)
    {
        if (beg > end)
            return -1;
            
        int mid = (beg + end) / 2;
        
        if (a[mid] == key)
        {
            int pos = findLeft ? findPos(a, beg, mid - 1, key, findLeft) : findPos(a, mid + 1, end, key, findLeft);
            return pos == -1 ? mid : pos;
        }
        else if (a[mid] < key)
            return findPos(a, mid + 1, end, key, findLeft);
        else
            return findPos(a, beg, mid - 1, key, findLeft);       
    }
    
    vector<int> searchRange(int A[], int n, int target) {
        // Start typing your C/C++ solution below
        // DO NOT write int main() function
        int leftPos = findPos(A, 0, n - 1, target, true);
        int rightPos = findPos(A, 0, n - 1, target, false);
        
        vector<int> ret;
        
        ret.push_back(leftPos);
        ret.push_back(rightPos);
        return ret;
    }
};


点赞