[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]
.
二分查找很简单,可是二分查找真的很简单吗?考虑下面几个问题:
1. 二分查找target,返回任意找到的下标,没找到返回-1;
2.二分查找第一次出现target的下标,没找到返回-1;
3.二分查找最后一次出现target的下标,没找到返回-1;
4.二分查找数组中比target小的最大的数,不存在返回-1;
5.二分查找数组中比target大的最小的数,不存在返回-1.
如果能快速写出上面5个问题的代码,那么你已经基本掌握二分查找了。那么再来看这道题,对应的就是上面的问题2和问题3了。怎么处理这种问题呢?其实只要把复杂的问题简单化,我们就能很快找到其中的奥秘。那么不妨我们就考虑只有两个元素的情况,一般来说,我习惯让R = n - 1,这样M一定是两个元素中的第一个元素:
如果我们要找最左边的位置,那么当A[M]==target时,就应该固定L,改变R,也就是:
if(A[M] >= target) R = M - 1;
最后的L就是我们要找的;
反之,如果要找最右边的位置,那么就固定R,改变L,也就是:
if(A[M] <= target) L = M + 1;
最后的R就是我们要找的。
这样是不是就很清楚了呢?而对于4,5问,也是同样的道理,只对2,3问我们想到的分别是L,R,而4,5问我们要的是R,L,或者说是L-1,R+1。
1 class Solution { 2 public: 3 int searchLeft(int A[], int n, int target) { 4 int L = 0, R = n - 1, M; 5 while (L <= R) { 6 M = L + ((R - L) >> 1); 7 if (A[M] >= target) R = M - 1; 8 else L = M + 1; 9 } 10 return (A[L] != target) ? -1 : L; 11 } 12 13 int searchRight(int A[], int n, int target) { 14 int L = 0, R = n - 1, M; 15 while (L <= R) { 16 M = L + ((R - L ) >> 1); 17 if (A[M] <= target) L = M + 1; 18 else R = M - 1; 19 } 20 return (A[R] != target) ? -1 : R; 21 } 22 23 vector<int> searchRange(int A[], int n, int target) { 24 vector<int> res(2); 25 res[0] = searchLeft(A, n, target); 26 res[1] = searchRight(A, n, target); 27 return res; 28 } 29 };
这里有个小问题,如果数组里没有target,且target比最小数还小或者比最大数还大,可能会出现下标访问越界,所以访问前要先判断一下返回的下标是不是在0~n-1之间。