查找特定元素上已排序数组的范围索引

时间:2016-09-26 21:42:38

标签: arrays algorithm

我正在使用数组,其中集合已被排序。任务是找到一个值的范围。

让我们假设我们有这个排序的数组:

int[] array = {1,1,1,3,3,9,10}

示例 我们必须找到元素1的范围最小值和最大值。我们很快发现元素1的第一个索引是0,范围max是2.

我们现在知道0到2之间的范围都是值1.如果搜索到的元素是3,我们看到范围是3-4,而对于元素9,范围是6-6。

我已经使用线性搜索来做到这一点,但是如果有更快的方法可以做到这一点会听到吗?

4 个答案:

答案 0 :(得分:3)

您可以使用两种版本的二分查找来找到最左侧和最右侧的位置:

int[] array = { 1, 1, 1, 3, 3, 9, 10 }
int value = ...

int leftmost(int min, int max)
{
    if (min == max) return min
    int mid = (min + max) / 2

    if (array[mid] < value) return leftmost(mid + 1, max)
    else return leftmost(min, mid)
}

int rightmost(int min, int max)
{
    if (min == max) return min
    int mid = (min + max + 1) / 2

    if (array[mid] > value) return rightmost(min, mid - 1)
    else return rightmost(mid, max)
}

只需使用min=0max=array.length-1拨打电话即可。时间复杂度为O(logn)

您将获得这样的输出(0-based索引):

value=1  -> left=0, right=2
value=3  -> left=3, right=4
value=9  -> left=5, right=5
value=10 -> left=6, right=6

答案 1 :(得分:0)

二进制搜索会快得多。您可以对此here

有所了解

答案 2 :(得分:0)

假设一个更大的数组使用二进制然后是线性的。

根据数组的大小,更快的方式可能是线性的。 如果您正在使用7个数字的数组重复进行此搜索,那么您将无法看到很多性能差异。事实上它可能会更慢。

然而,扩展该数组虽然你有更多,你最好先做二进制搜索然后进行线性搜索。

执行一次并抓住范围的底部,然后执行线性以找到范围的终点。

你可以为终点做另一个二进制,但是随着时间的推移,应该使用普通线性函数进行平均洗涤。

答案 3 :(得分:0)

虽然@Arturo Menchaca的答案是正确的,但我认为递归解决方案可能很难理解。因此,对于那些喜欢 迭代 方法的人...

public int[] FindRange(int[] arr, int value)
{
    int[] upperLowerBounds = new int[2];
    int startIndex = 0;
    int endIndex = arr.Length - 1;
    while (startIndex < endIndex)
    {
        int midIndex = (startIndex + endIndex) / 2;
        if (value <= arr[midIndex])
        {
            endIndex = midIndex;
        }
        else
        {
            startIndex = midIndex + 1;
        }
    }

    // saving startIndex in the result array that will be returned...
    upperLowerBounds[0] = startIndex;
    endIndex = arr.Length - 1;
    while (startIndex < endIndex)
    {
        int midIndex = (startIndex + endIndex) / 2 + 1;
        if (value < arr[midIndex])
        {
            endIndex = midIndex  -1;
        }
        else
        {
            startIndex = midIndex;
        }
    }

    upperLowerBounds[1] = endIndex;
    return upperLowerBounds;
}

和单元测试。

[TestCase(new int[] { -4, -3, -3, -3, -3, -3, -3, -3, -3, -3, -1, 1, 6, 8, 10, 10, 10, 10, 10, 10, 25 }, -3, 1, 9)] // search element = -3
[TestCase(new int[] { -4, -3, -1, 1, 6, 8, 10, 10, 10, 10, 10, 10, 25 }, 6, 4, 4)] // search element = 6
[TestCase(new int[] { -4, -3, -1, 1, 6, 8, 10, 10, 10, 10, 10, 10, 25 }, 10, 6, 11)] // search element = 10
[TestCase(new int[] { -4, -3, -3, -3, -3, -3, -3, -3, -3, -3, -1, -1, -1, -1, -1, -1, -1, -1, 1, 6, 8, 10, 10, 10, 10, 10, 10, 25 }, -1, 10, 17)] // search element = -1
[TestCase(new int[] { 1, 1, 1, 3, 3, 9, 10 }, 3, 3, 4)] // search element  = 3
[TestCase(new int[] { 1, 1, 1, 3, 3, 9, 10 }, 1, 0, 2)] // search element = 1
public void FindRangeTest(int[] arr, int value, int expectedStartIndex, int expectedEndIndex)
{
    int[] range = runner.FindRange(arr, value);

    // Assert.
    Assert.AreEqual(expectedStartIndex, range[0]);
    Assert.AreEqual(expectedEndIndex, range[1]);
}

我用过C#和nunit 3.10