Skip to content

Latest commit

 

History

History
 
 

033. Search for a Range

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 

这道题又是二分查找的变种。我因为时间有限,想出来的方法是最直接的。

首先先 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++ 目前最高效率。