二进制多重搜索

时间:2018-11-30 11:41:26

标签: c# binary-search

我想获得(8)在这样的数组中的所有位置:(3,5,6,7,8,8,9,33,34,45)。但是我的代码只返回一个位置,而忘记了第二个位置:

这是我的二进制搜索代码:

private static int BinarySearch(int[] array, int item)
{
    int left = 0;
    int right = array.Length - 1;

    while (left <= right)
    {
        var middle = (left + right) / 2;

        if (array[middle] == item)
            return middle;

        if (item < array[middle])
            right = middle - 1;
        else
            left = middle + 1;
    }

    return -1;
}

3 个答案:

答案 0 :(得分:3)

您想要的与C++ "equal_range()" method相同。

如果您查看标准的C ++实现,它们将使用“ lower_bound()”查找低值,并使用“ upper_bound”查找高值。它这样做是为了对普通二进制搜索找到的索引进行“扫描”,以确保它始终在O(Log(N))时标内工作。线性搜索范围可以退化为O(Log(N))运算,然后是O(N)运算。

这是C ++的lower_bound()upper_bound()equal_range()的C#实现:

public static class BoundedSearch
{
    /// <summary>Same as C++'s equal_range()</summary>

    public static Tuple<int, int> EqualRange<T>(IList<T> values, T target) where T : IComparable<T>
    {
        int lowerBound = LowerBound(values, target, 0, values.Count);
        int upperBound = UpperBound(values, target, lowerBound, values.Count);

        return new Tuple<int, int>(lowerBound, upperBound);
    }

    /// <summary>Same as C++'s lower_bound()</summary>

    public static int LowerBound<T>(IList<T> values, T target, int first, int last) where T : IComparable<T>
    {
        int left  = first;
        int right = last;

        while (left < right)
        {
            int mid = left + (right - left) / 2;
            var middle = values[mid];

            if (middle.CompareTo(target) < 0)
                left = mid + 1;
            else
                right = mid;
        }

        return left;
    }

    /// <summary>Same as C++'s upper_bound()</summary>

    public static int UpperBound<T>(IList<T> values, T target, int first, int last) where T : IComparable<T>
    {
        int left = first;
        int right = last;

        while (left < right)
        {
            int mid = left + (right - left) / 2;
            var middle = values[mid];

            if (middle.CompareTo(target) > 0)
                right = mid;
            else
                left = mid + 1;
        }

        return left;
    }
}       

(注意:此代码使用旧的Tuple<>类返回范围。如果您使用的是C#和.Net的最新版本,则可以将其更改为返回正确的元组,例如public static (int LowerBound, int UpperBound) EqualRange<T>(...) )。

答案 1 :(得分:2)

正如马特在评论中暗示的那样,您可以返回IEnumerable<int>,并返回值:

private static IEnumerable<int> BinarySearch(int[] array, int item)
{
    int left = 0;
    int right = array.Length - 1;

    while (left <= right)
    {
        if (array[left] == item)
            yield return left;

        if (left == right)
            break;

        if (array[right] == item)
            yield return right;

        left++;
        right--;
    }
}

您将按以下方式使用它:

static void Main(string[] args)
{
    var items = new int[] { 3, 5, 6, 7, 8, 8, 9, 33, 34, 45, 8 };

    foreach (var item in BinarySearch(items, 8))
    {
        Console.WriteLine(item);
    }
}

或具体实现一个数组或列表:

static void Main(string[] args)
{
    var items = new int[] { 3, 5, 6, 7, 8, 8, 9, 33, 34, 45, 8 };

    var results = BinarySearch(items, 8).ToArray();
}

答案 2 :(得分:0)

您要返回int。那只是一个位置。您可能需要更改代码以返回数组:int[]也在您的地方return middle;您应该搜索middle之前和之后的所有匹配值。为了简单起见,这可能是一个线性搜索,或者,如果您期望潜在的大量匹配值,它可能是二进制形式。