3

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

{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);
}
4

3 回答 3

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;
    

测试:(有重复)

    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;

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

于 2013-12-20T12:26:33.350 回答
0

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

当我们执行两次二进制搜索时,这将给出 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;
}
于 2013-10-14T05:03:50.367 回答
0

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

    Arrays.binarySearch

您基本上需要第一个元素(4)的第一个出现和最后一个(23)的最后出现和减法。但是不需要 (4) 存在,因此请阅读 Arrays.binarySearch 的文档,它会告诉您 (4) 的位置。

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

如果有前一次查看 i/2,则在索引 i 处找到第一次出现,如果有(4)查看 i/4,否则查看 3*i/4 ...

于 2013-03-08T10:13:08.640 回答