【问题标题】:How to implement a lower_bound binary search algorithm in Java?如何在Java中实现lower_bound二分查找算法?
【发布时间】:2019-12-25 22:44:41
【问题描述】:

我想找到一个目标值 4 在序列 [1, 1, 2, 3, 3, 3, 3, 4, 4, 4, 5, 5, 6] 中最先出现的位置。当我使用 java.util.Arrays.binaySearch 时,它返回的索引是 9,但我预计是 7。

我看 java.util.Arrays.binaySearch source code

我发现了一些 cmets:

如果数组包含多个具有指定值的元素,则无法保证会找到哪一个。

那么如何在Java中实现一个lower_bound二分查找算法,返回目标值最先出现的地方。

注意:lower_bound这个概念来源于C++,但是我对C++不是很了解。

【问题讨论】:

  • 要找到前4个,将4视为大于目标值,将3视为小于目标值。 不要在数组中的值为 4 时提前终止算法。
  • 只需向后迭代,直到找到不同的值,存储前一个索引(在算法结束时返回)。
  • @JacobG。最坏的情况,这会导致 O(n) 算法,这违背了二分搜索的目的。

标签: java algorithm binary-search


【解决方案1】:

我认为下面的实现将正确地完成工作:

int firstOccurrence(int[] sequence, int x) {
    int min = 0;
    int max = sequence.length - 1;

    int result = -1;

    while (min <= max)
    {
        // find the mid value and compare it with x
        int mid = min + ((max - min) / 2);

        // if x is found, update result and search towards left
        if (x == sequence[mid]) {
            result = mid;
            max = mid - 1;
        } else if (x < sequence[mid]) {
            // discard right half
            max = mid - 1;
        } else {
            // discard left half
            min = mid + 1;
        }
    }

    // return the leftmost index equal to x or -1 if not found
    return result;
}

编辑:

改变计算 mid 的方式,避免更大的和溢出

// Previously, can overflow since we add two integer
int mid = (min + max) / 2;

// Now
int mid = min + ((max - min) / 2);

// Another way using the unsigned right shift operator
int mid = (low + high) >>> 1;
// The left operands value (low + high) is moved right
// by the number of bits specified (2 in this case) by the right operand and
// shifted values are filled up with zeros.
// The >>> treats the value as unsigned

【讨论】:

  • 感谢您的回答,但 (min+max)/2 可能会溢出。
  • 你能给我输入值吗?顺便说一句,您可以像这样计算中间值:mid = min + ((max - min) / 2);
【解决方案2】:

在这个答案的基础上回答另一个二分搜索问题:

How can I simplify this working Binary Search code in C?

这是一个等效于 C++ 中的lower_bound 的搜索。它返回小于您要查找的值的元素数。那将是 第一次出现的索引,或者如果没有出现则插入的位置:

int numSmaller(int[] seq, int valueToFind)
{
    int pos=0;
    int limit=seq.length;
    while(pos<limit)
    {
        int testpos = pos+((limit-pos)>>1);

        if (seq[testpos]<valueToFind)
            pos=testpos+1;
        else
            limit=testpos;
    }
    return pos;
}

请注意,我们每次迭代只需进行一次比较。

链接的答案突出了以这种方式编写二分搜索的几个优点。

【讨论】:

  • 如果序列中所有元素都大于valueToFind你会返回0,这是一个错误,如果序列中所有元素都小于valueToFind你会返回length + 1,不好返回价值。
  • 如果所有元素都更大,我将返回零。这就是它应该做的。如果所有元素都少了,那么我就返回length,这也是它应该做的。
  • 而右移来做除法并不快,因为编译器会优化/ 2,但不能对位移做太多,而且不是所有的程序员都会理解这个操作。
  • 向右移位有时会更快,但 /2 也可以正常工作,即使它不能优化为移位,除非编译器能够确定操作数始终为正数。该表达式的重要部分是避免溢出
  • @BSO 你只需要检查一下,当然。 OP 要求等效于 C++ 下界,这就是我提供的。我还准确地解释了它的作用,并给它起了一个让它显而易见的名字。事实证明,对于您特别想搜索 first 出现的位置的情况,这种合同也是最方便和最有效的。想一些实际的用例并尝试一下。
【解决方案3】:

它认为它会帮助你

public static boolean binarysearch(int[] data, int target, int low, int high){
    if(low>high){
        System.out.println("Target not found");
        return false;}
        else{
                int mid=(low+high)/2;
                if(target==data[mid])
                    return true;
                else if(target<data[mid])
                    return binarysearch(data, target, low, high);
                else
                    return binarysearch(data, target, low, high);
                }
}

【讨论】:

  • 你认为有人问如何在 java 中实现下限会受益于递归代码而不是迭代代码吗?
猜你喜欢
  • 2014-05-10
  • 2023-03-05
  • 1970-01-01
  • 1970-01-01
  • 2015-06-10
  • 2012-08-31
  • 1970-01-01
  • 2016-02-23
  • 1970-01-01
相关资源
最近更新 更多