哪个对象用于在内存查找数据中进行排序并有间隙

问题描述

我可以在 C# net 5.0 中使用哪个对象将以下信息存储在内存中以获得最快的数据检索?

输入:一个数字(长)

示例简化查找数据:

1 -> 10          : ResultA
300 -> 300       : ResultB
500 -> 10000     : ResultC
235015 -> 235820 : ResultD
...

列表还在继续(大约 300 万行查找数据)

在上面的示例数据中:

Input -> output

5 -> ResultA
300 -> ResultB
400 -> Not found/null
9999 -> ResultC
1000000 -> Not found/null

解决方法

正如 Llama 提到的,正确的方法是使用二分搜索。即使对于数百万个范围,这也应该提供足够的性能,因为对于合理分布的数据,它以 O(log n) 进行缩放。

如果范围不重叠,这样的事情应该可以工作:

    // Assume sorted
        var rangesArray = new[]
        {
            (1,10,"A"),(300,300,"B"),(500,10000,"C"),(235015,235820,"D")
        };
        var rangesList = rangesArray.ToList();
        var toSearchFor = 9999;

        var comparer = new KeyComparer<(int,int,string),int>(p => p.Item1);
        var index = rangesList.BinarySearch((toSearchFor,""),comparer);
        if (index < 0) // negative values mean a exact match could not be found,{
            // Take bitwise complement to get index of the element larger than the toSearchFor
            // remove one get the actual range to check
            index = ~index -1; 
            if (index > 0 && toSearchFor < rangesList[index].Item2 )
            {
                Console.WriteLine($"Found Range {index}");
            }
            else
            {
                Console.WriteLine($"Not Found");
            }
        }
        else
        {
            Console.WriteLine($"Found Exact Range {index}");
        }

如何编写通用的 IComparer

public class KeyComparer<T,TKey> : IComparer<T> where TKey : IComparable<TKey>
{
    private readonly Func<T,TKey> selector;
    public KeyComparer(Func<T,TKey> selector) => this.selector = selector;
    public int Compare(T x,T y) => selector(x).CompareTo(selector(y));
}

如果您有重叠的范围,您可能需要搜索所有较小的索引,或者使用一些更高级的搜索结构。

,

这个方法怎么样?请注意,它仅考虑起始值,因为您说您只有间隙但没有重叠。在那种情况下,它会变得更加复杂。

public static class Program
{
    static void Main(string[] args)
    {
        var random = new Random();
        // Create a list with random items.
        var source = Enumerable.Range(0,1000)
            .Select(_ => new SortableObject { Start = random.Next(100000) })
            .ToList();

        // Sort the list by using a dedicated comparer
        source.Sort(SortableObjectComparer.Default);
        // Define an element to search for
        var searchElement = new SortableObject { Start = 17432 };
        // Get the index of the best matching item
        var found = source.BinarySearch(searchElement,SortableObjectComparer.Default);

        if(found < 0)
        {
            // No exact match,get the best candidates.
            var nextElement = source[~found];
            var beforeElement = source[~found - 1];

            Console.WriteLine($"Element with lower start value: {beforeElement.Value}");
            Console.WriteLine($"Element to search {searchElement.Value}");
            Console.WriteLine($"Element with higher start value: {nextElement.Value}");
        }
        else
        {
            // Exact match
            var matchingElement = source[found];
            Console.WriteLine($"Element matching: {matchingElement.Value}");
            Console.WriteLine($"Element to search {searchElement.Value}");
        }
    }
}

public class SortableObjectComparer : IComparer<SortableObject>
{
    public static readonly IComparer<SortableObject> Default = new SortableObjectComparer();
    public int Compare(SortableObject x,SortableObject y)
    {
        if (ReferenceEquals(x,y))
            return 0;

        if (ReferenceEquals(x,null))
            return -1;

        if (ReferenceEquals(y,null))
            return 1;

        return x.Start.CompareTo(y.Start);
    }
}

public class SortableObject
{
    public long Start { get; set; }
    public long End { get; set; }
    public string Value => $"Result {Start}";
}