这道题又是二分查找的变种。我因为时间有限,想出来的方法是最直接的。
首先先 init 一个 vector {-1,-1}, 这样如果没找到,直接返回无效值。
然后常规手法,二分查找,一旦找到,将 vec[0] = vec[1] = mid
.
由于咱们找的是一个范围,所以,仅需在这个位置上,前后扩展即可。
while (vec[0]>0 && A[vec[0]-1] == target)
--vec[0];
while (vec[1]<n-1 && A[vec[1]+1] == target)
++vec[1];
但这里有一个效率的最差情况,就是全部元素都是 target . 我最后的扩展成了 O(n).
针对这种极端情况,我在最开始加上一句判断:
if (A[0] == A[n-1] && A[0] == target) return vector<int>{0, n-1};
方法不惊艳,希望有更好解法的能告诉我。
在 issue #11 中,@ender233 告诉了我一个更好的思路。
他的本质思想,是用三次二分查找来避免最坏情况。但后两次,皆为二分查找的变种,即查找的不是目标,而是目标应该插入的位置。
这个"变种"的二分查找我尝试写了一下,判断要多得多,而且写的很丑。智商捉急且懒散成性的情况下,我略改了一下思路:
既然二分法可以从根本上将 O(n) 的情况减少到 O(logn) 的层次,那么三次二分与多次二分,差别其实并不大。(仍处于 O(logn) 的级别)
那么:
- 第一次二分,找到 target, 以及
iPos
。 - 接着二分左边 [0, iPos-1], 以及右边 [iPos+1, n-1]。依然找 target, 左边位置设为
lo
(low), 右边位置设为hi
(high). - 不断循环往左、右二分查找 target,直到找不到为止。那么此刻范围也就确定好了。
代码已更新。这样效率的确高,我去 LeetCode 试了一下,15ms, 属于 C++ 目前最高效率。