.net SortedList〈K,V〉上是否存在下界函数?

yftpprvb  于 2022-12-14  发布在  .NET
关注(0)|答案(6)|浏览(96)

Is there a Lower Bound function on a SortedList<K ,V> ? The function should return the first element equal to or greater than the specified key. Is there some other class that supports this?
Guys - please read the question once again. I do not need a function that returns the key if it is present. I'm interested in scenario when there is no exact key matching.

I'm interested in O(log n) time. It means that I do not have a problem with foreach loop, but rather would like to have an efficient way of doing this.

I have done some tests on this.
Linq statements are not optimized by neither the compiler nor runtime machine, so they walk through all collection elements and are slow O(n). Based on Mehrdad Afshari answer, here is a Binary Search that works in O(log n) on the Keys collection:

public static int FindFirstIndexGreaterThanOrEqualTo<T>(
            this IList<T> sortedCollection, T key
        ) where T : IComparable<T> {
    int begin = 0;
    int end = sortedCollection.Count;
    while (end > begin) {
        int index = (begin + end) / 2;
        T el = sortedCollection[index];
        if (el.CompareTo(key) >= 0)
            end = index;
        else
            begin = index + 1;
    }
    return end;
}
ars1skjm

ars1skjm1#

Binary search the SortedList.Keys collection.
Here we go. This is O(log n):

private static int BinarySearch<T>(IList<T> list, T value)
{
    if (list == null)
        throw new ArgumentNullException("list");
    var comp = Comparer<T>.Default;
    int lo = 0, hi = list.Count - 1;
    while (lo < hi) {
            int m = (hi + lo) / 2;  // this might overflow; be careful.
            if (comp.Compare(list[m], value) < 0) lo = m + 1;
            else hi = m - 1;
    }
    if (comp.Compare(list[lo], value) < 0) lo++;
    return lo;
}

public static int FindFirstIndexGreaterThanOrEqualTo<T,U>
                          (this SortedList<T,U> sortedList, T key)
{
    return BinarySearch(sortedList.Keys, key);
}
qlckcl4x

qlckcl4x2#

我会选择LINQ(假设您使用的是C#3),但是使用带 predicate 的FirstOrDefault重载:

first = sortedList.FirstOrDefault(x => x >= theObjectForComparison);

(许多其他Enumerable方法也可以使用 predicate ,这是一个很好的快捷方式)

llmtgqce

llmtgqce3#

Not aware of one, but it's a simple LINQ statement:

first = sortedList.Where(x => x >= theObjectForComparison).FirstOrDefault();

first will either be the first object that passes the comparison or default(T) (which is normally null ).

Edit

DaveW's version:

first = sortedList.FirstOrDefault(x => x >= theObjectForComparison);

does the same job but could potentially be faster, you'd have to test it though.

flvlnr44

flvlnr444#

或者你可以写自己的扩展方法来做这件事。注意,所有这些函数并不保证都是一个序列。

emeijp43

emeijp435#

public static TValue? LowerBound<TKey, TValue>(this SortedList<TKey, TValue> list, TKey key) where TKey : notnull, IComparable<TKey>
{
    var comparer = list.Comparer;

    #region Short-Circuits

    if (list.Count == 0) //empty list
        return default;
    if (list.ContainsKey(key)) 
       return list[key]; //"The function should return the first element equal"

    if (comparer.Compare(list.Keys[list.Keys.Count - 1], key) < 0)
        return default; // if all elements are smaller, return default

    if (comparer.Compare(list.Keys[0], key) > 0)
        return list[list.Keys[0]]; //if the first element is greater, return it

    #endregion Short-Circuits

    int range = list.Count - 1; //the range between of first and last element to check
    int itemIndex = 1;          //the default value of index is 1 since 0 was already checked above
    while (range > 0)
    {
        int halfRange = range / 2;               //cut the search range in half
        int indexTmp = itemIndex + halfRange;    //set the current item to compare
        if (comparer.Compare(list.Keys[indexTmp], key) < 0)
        {
            //the current item is less than the given key
            itemIndex = indexTmp + 1;   //set the current item to the item after the compared item
            range = (range - halfRange - 1); //move the remaining range to the right
        }
        else
        {
            //the current item is greater than the given key (we have already checked equal above in short-circuits)
            range = halfRange; //move the remaining range to the left
        }
    }
    return list[list.Keys[itemIndex]];
}
zujrkrfu

zujrkrfu6#

根据SortedList的实现,希望这会更快。

public static int FindFirstIndexGreaterThanOrEqualTo<K, V>(
        this SortedList<K, V> sortedCollection, K key
    ) where V : new()
{
    if (sortedCollection.ContainsKey(key))
    {
        return sortedCollection.IndexOfKey(key);
    }
    sortedCollection[key] = new V();
    int retval = sortedCollection.IndexOfKey(key);
    sortedCollection.Remove(key);
    return retval;
}

相关问题