二进制搜索算法的问题

时间:2019-02-24 22:51:39

标签: java algorithm search binary-search

我有一个任务来编写一个二进制搜索,该搜索返回我们正在寻找的值的第一次迭代。我一直在做一些在线研究,我的搜索看起来很像我在寻找的东西,但是我遇到了问题。如果我将此代码传递给一个看起来像{10,5,5,3,2}的数组,它将在中间找到5(它检查的第一件事),然后将其返回。但这不是5的第一次迭代,而是第二次。我究竟做错了什么?这有可能吗?

谢谢!

代码(我正在使用Java)

public static int binarySearch(int[] arr, int v){
    int lo = 0;
    int hi = arr.length-1;
    while(lo <= hi){
        int middle = (lo+hi)/2;
        if(v == arr[middle]){
            return middle;
        }
        else
        {
            if(v < arr[middle]){
                lo = middle+1;
            }  
            else
            {
                hi = middle-1;
            }
        }
    }
    return -1;
}

1 个答案:

答案 0 :(得分:1)

这是一种有效的修改算法。

public static int binarySearch(int[] arr, int v) {
  int lo = -1;
  int hi = arr.length - 1;

  while (hi - lo > 1 ) {
    int middle = (lo + hi) / 2;
    if (arr[middle] > v) {
      lo = middle;
    } else {
      hi = middle;
    }
  }

  if (v == arr[hi]) {
    return hi;
  } else {
    return -1;
  }
}

关键点是:

  • 间隔(lo,hi]在左边是排他的,在右边是排他的。
  • 在每一步中,我们将间隔的一半丢弃。当我们只涉及一个要素时,我们就停下来。尝试提前终止只会带来最小的性能提升,而它们通常会影响代码的易读性和/或引入错误。
  • arr[middle] = v时,我们分配hi = middle,从而丢掉右半部分。这样做是安全的,因为我们不在乎v以后发生的middle。我们确实关心arr[middle],它可能是第一次出现,也可能不是第一次出现,因此,我们使(lo,hi]包含在右侧。如果之前出现过v middle,我们将在后续迭代中找到它们。
  • 作为补充,可以使用更自然的定义[0, n)包含在左边,而排除在右边,查找最近出现的v

根据我的经验,此包含-排除的间隔定义会生成最短,最清晰和最通用的代码。人们一直在努力改进它,但是在极端情况下他们常常会纠结在一起。