一尘不染

在排序数组中找到大于目标的第一个元素

algorithm

在一般的二进制搜索中,我们正在寻找出现在数组中的值。但是,有时我们需要找到大于或小于目标的第一个元素。

这是我的丑陋,不完整的解决方案:

// Assume all elements are positive, i.e., greater than zero
int bs (int[] a, int t) {
  int s = 0, e = a.length;
  int firstlarge = 1 << 30;
  int firstlargeindex = -1;
  while (s < e) {
    int m = (s + e) / 2;
    if (a[m] > t) {
      // how can I know a[m] is the first larger than
      if(a[m] < firstlarge) {
        firstlarge = a[m];
        firstlargeindex = m;
      }
      e = m - 1; 
    } else if (a[m] < /* something */) {
      // go to the right part
      // how can i know is the first less than  
    }
  }
}

对于这种问题是否有更优雅的解决方案?


阅读 561

收藏
2020-07-28

共1个答案

一尘不染

解决此问题的一种方法是考虑对数组的转换版本执行二进制搜索,其中已通过应用函数修改了数组

f(x) = 1 if x > target
       0 else

现在,目标是找到此函数取值1的第一位。我们可以使用二进制搜索来做到这一点,如下所示:

int low = 0, high = numElems; // numElems is the size of the array i.e arr.size() 
while (low != high) {
    int mid = (low + high) / 2; // Or a fancy way to avoid int overflow
    if (arr[mid] <= target) {
        /* This index, and everything below it, must not be the first element
         * greater than what we're looking for because this element is no greater
         * than the element.
         */
        low = mid + 1;
    }
    else {
        /* This element is at least as large as the element, so anything after it can't
         * be the first element that's at least as large.
         */
        high = mid;
    }
}
/* Now, low and high both point to the element in question. */

要查看该算法是否正确,请考虑进行每个比较。如果我们找到一个不大于目标元素的元素,则该元素及其下方的所有内容可能都不匹配,因此无需搜索该区域。我们可以递归搜索右半部分。如果我们发现一个大于所讨论元素的元素,那么它后面的任何东西也必须更大,因此它们不能成为第
一个 更大的元素,因此我们不需要搜索它们。因此,中间元素是它最后可能的位置。

请注意,在每次迭代中,我们会至少考虑其余元素的一半。如果执行顶部分支,则[low,(low + high)/
2]范围内的元素都将被丢弃,从而使我们失去地板((low + high)/ 2)-low +1> =(low +高)/ 2-低=(高-低)/ 2个元素。

如果执行底部分支,则将丢弃[(low + high)/ 2 +1,high]范围内的元素。这使我们损失了高-底(低+高)/ 2 + 1> =高-(低+高)/
2 =(高-低)/ 2个元素。

因此,在此过程的O(lg n)迭代中,我们最终将发现第一个元素大于目标。

编辑:这是在数组0 0 1 1 1 1上运行的算法的痕迹。

最初,我们有

0 0 1 1 1 1
L = 0       H = 6

因此我们计算mid =(0 + 6)/ 2 = 3,所以我们检查位置3处的元素,该元素的值为1。由于1> 0,我们将high设置为mid = 3。

0 0 1
L     H

我们计算mid =(0 + 3)/ 2 = 1,所以我们检查元素1。由于其值0 <= 0,我们将mid = low + 1 = 2设置为零。现在剩下L =
2和H = 3:

0 0 1
    L H

现在,我们计算mid =(2 + 3)/ 2 =2。索引2处的元素为1,并且由于1≥0,我们将H = mid =
2设置为该点,然后停止,实际上我们正在寻找在第一个大于0的元素处。

希望这可以帮助!

2020-07-28