如何在排序数组上使用二进制搜索来查找特定范围内的整数数。 (有重复)

时间:2013-03-08 10:05:22

标签: java search binary-search

假设你有一个排序的整数数组:

{3,4,4,6,10,15,15,19,23,23,24,30}

并且您想要找到4和23范围内的整数数。

{4,4,6,10,15,15,19,23,23}

因此结果将是9。

我写了一个二进制搜索实现,但是我不确定如何修改它以考虑到可以有多个整数匹配范围上限的事实。

我想在方法签名中添加一个布尔值来询问是否要查找键的上限,但是我不确定是否可以在保持O(log(N))的同时在单个方法中完成复杂性。

或者是否有其他方法可以在O(log(N))时间内在排序数组中找到该范围内的项目数?

这是我到目前为止所做的:

int start = rangeBinarySearch(arr, 4, false);
int end = rangeBinarySearch(arr, 23, true); // true would indicate that I want the position of the last occurrence of the key.

int totalInRange = (Math.abs(end) - Math.abs(start) -1)


private static int rangeBinarySearch(int[] items, int key, boolean lastIndex) {
    if(items == null)
        throw new IllegalArgumentException();

    int start = 0;
    int end = items.length - 1;

    while(start <= end) {
        int mIndex = (start + end) / 2;
        int middle = items[mIndex];

        if(middle < key)
            start = (mIndex +1);
        else if(middle > key)
            end = (mIndex -1);
        else
            return mIndex; // Possible something here to find the upper bounds?
    }

    return -(start +1);
}

3 个答案:

答案 0 :(得分:2)

下限和上限的范围二进制搜索是不同。这里不同意味着他们有不同的停止标准和返回步骤。

  1. 对于下限(左范围),您可以调用以下函数来获取值大于或等于它的有序数组中的索引,否则为-1。

    int binarySearchForLeftRange(int a[], int length, int left_range)
    {
        if (a[length-1] < left_range)
            return -1;
    
        int low = 0;
        int high = length-1;
    
        while (low<=high)
        {
            int mid = low+((high-low)/2);
    
            if(a[mid] >= left_range)
                high = mid-1;
            else //if(a[mid]<i)
                low = mid+1;
        }
    
        return high+1;
    }
    
  2. 对于上限(右侧范围),您可以调用以下函数来获取值小于或等于它的有序数组中的索引,否则为-1。

    int binarySearchForRightRange(int a[], int length, int right_range)
    {
        if (a[0] > right_range)
            return -1;
    
        int low = 0;
        int high = length-1;
    
        while (low<=high)
        {
            int mid = low+((high-low)/2);
    
            if(a[mid] > right_range)
                high = mid-1;
            else //if(a[mid]<i)
                low = mid+1;
        }
    
        return low-1;
    }
    
  3. 最后,如果您想获得此范围内的元素数量,可以根据上述两个函数的返回值轻松实现。

    int index_left = binarySearchForLeftRange(a, length, left_range);
    int index_right = binarySearchForRightRange(a, length, right_range);
    
    if (index_left==-1 || index_right==-1 || index_left>index_right)
        count = 0;
    else
        count = index_right-index_left+1;
    

  4. 测试 :(带有重复项)

        int a[] = {3,4,4,6,10,15,15,19,23,23,24,30};
        int length = sizeof(arr)/sizeof(arr[0]);
    
        int left_range = 4;
        int right_range = 23;
        int index_left = binarySearchForLeftRange(a, length, left_range); // will be 1
        int index_right = binarySearchForRightRange(a, length, right_range); // will be 9
    
        int count; // will be 9
        if (index_left==-1 || index_right==-1 || index_left>index_right)
            count = 0;
        else
            count = index_right-index_left+1;
    

    编辑:当然,您可以通过传递一个额外的标志来将前两个函数合并为一个,以指示它是下限或上限,但如果不是则会更清楚。你的选择!

答案 1 :(得分:0)

如果您没有学习算法,请改用标准函数:

    Arrays.binarySearch

你基本上需要首先出现你的第一个元素(4)并且最后一次出现(23)和减去。但是没有必要(4)在那里,所以阅读Arrays.binarySearch的文档,它会给你(4)的位置。

如果您期望很多(4)s,您必须编写自己的binSearch,它将返回第一个和最后一个索引:

在索引i处找到第一次出现 如果有前一个看i / 2,如果有(4)看看i /​​ 4别的看看3 * i / 4 ...

答案 2 :(得分:0)

您需要执行两次二进制搜索以查找rangeLow之前的最低索引和rangeHigh之后的highestIndex,这样您就可以计算该范围内的重复项。

当我们执行两次二进制搜索时,这将给出o(2 log n)的时间复杂度。

private int searchArrayForNumbersInRange(int[] arr, int start, int end) {
    int leftIndex = searchLeft(arr, start);
    int rightIndex = searchRight(arr, end);
    int count;

    if (leftIndex < 0 || rightIndex < 0)
        return -1;
    if (rightIndex == leftIndex)
        count = 1;
    else {
        count = rightIndex - leftIndex;
    }
    return count;
}

private int searchLeft(int[] arr, int start) {
    int lo = 0;
    int hi = arr.length - 1;

    while (lo <= hi) {
        int mid = lo + (hi - lo) / 2;

        if (arr[mid] == start && arr[mid -1] < start) {
            return mid - 1;
        }

        if (arr[mid] >= start)
            hi = mid - 1;
        else
            lo = mid + 1;
    }

    return -1;
}

private int searchRight(int[] arr, int end) {
    int lo = 0;
    int hi = arr.length -1;

    while (lo <= hi) {
        int mid = lo + (hi - lo) / 2;

        if (arr[mid] == end && arr[mid+1] > end)
            return mid;

        if (mid <= end)
            lo = mid + 1;
        else
            hi = mid - 1;
    }

    return -1;
}