Как использовать BinarySearch для списка <T>

Давайте начнем с этой перегрузки списка BinarySearch:

public int BinarySearch(T item, IComparer<T> comparer);

Хорошо известно, что список должен быть отсортирован с соответствующим IComparer перед использованием BinarySearch. Но тогда: для поиска списка вам нужно будет указать элемент Т. Это довольно неожиданно, когда используется для поиска элементов в списке на основе свойств этих элементов (например, с использованием Linq или делегатов/предикатов). Потому что, когда у меня уже есть свой элемент Т, мне не нужно его искать!

Теперь я реализовал код С++ на С# и увидел, что программист на С++ использовал бинарные поиски в стиле С++ везде в своем коде следующим образом. Сначала он сделал новый элемент Т и дал этому элементу Т свойства, которые он искал. Затем он просмотрел список с ним, чтобы найти индекс элемента в списке с теми же свойствами. Конечно, С++-сопоставитель был адаптирован к этим свойствам.

Итак, это совсем другой способ поиска элементов в списке. BinarySearch создает фиктивный элемент T и ищет индекс, с помощью которого он может получить реальный элемент T в списке. С точки зрения Linq это кажется неестественным.

Мои вопросы:

Я дал правильное описание идеи BinarySearch?

Как вы думаете, можно ли использовать поиск стиля Linq с BinarySearch, не делая сначала фиктивный элемент T?

Ответ 1

Did I give a correct description of the idea behind BinarySearch?
Да.

Do you think it is possible to use a Linq style search with BinarySearch without making a dummy T item first?
Не в этом текущая форма. Вы могли бы использовать обертку, которая создавала бы для вас манекен T, она работала бы только для определенных Ts (с беззаметными конструкторами и т.д.).

Ответ 2

На самом деле в LINQ ничего подобного нет, но вы можете создавать свои собственные расширения. В этом примере вы можете передать не фиктивный элемент, а, например, просто свойство, используемое в исходном компараторе:

public static int FindFirstIndexGreaterThanOrEqualTo<TElement, TKey>(
                this IList<TElement> keySortedCollection,
                TKey key,
                Func<TElement, TKey> keySelector,
                IComparer<TKey> keyComparer)
{
    int begin = 0;
    int end = keySortedCollection.Count;
    while (end > begin)
    {
        int index = (begin + end) / 2;
        TElement el = keySortedCollection[index];
        TKey currElKey = keySelector(el);
        if (keyComparer.Compare(currElKey, key) >= 0)
            end = index;
        else
            begin = index + 1;
    }
    return end;
}

public static int FindFirstIndexGreaterThanOrEqualTo<TElement, TKey>(
                this IList<TElement> keySortedCollection,
                TKey key,
                Func<TElement, TKey> keySelector)
{
    return FindFirstIndexGreaterThanOrEqualTo(keySortedCollection,
                                               key,
                                               keySelector,
                                               Comparer<TKey>.Default);
}

С помощью этих методов вы можете дать сравнение, которое является подмножеством того, которое вы первоначально использовали для сортировки коллекции.

Очевидно, что когда вы используете подмножество исходного сравнения, вы не можете быть уверены в том, что найдете один индекс. Таким образом, эти методы возвращают нисходящий бинарный поиск.