SortedList<K, V> 键的二分查找

Posted

技术标签:

【中文标题】SortedList<K, V> 键的二分查找【英文标题】:Binary Search on Keys of SortedList<K, V> 【发布时间】:2011-08-31 09:41:30 【问题描述】:

我需要为线性插值编写一些代码,并且我正在尝试找出最有效的方法来搜索 SortedList&lt;K, V&gt; 的键以查找围绕我的目标键的上下键。

SortedList<int, double> xyTable = new SortedList<int, double>()

    1, 10, 2, 20, 3, 30, 4,40
;

double targetX = 3.5;

搜索列表并确定 3.5 介于 3 和 4 之间的最有效方法是什么?我有一个适用于整数的方法/作弊(暂时将目标键插入列表然后找到索引),但我想我会问专业人士,以便我可以生成高质量的代码。

谢谢。

【问题讨论】:

排序后的声音非常适合二分搜索 An example of log(n) lowerbound search 【参考方案1】:

二分搜索可以在列表中提供不错的性能。但是SortedList 上的 Keys 属性是IList 类型,而BinarySearch 是在List 上定义的。幸运的是,您可以在这个相关问题中找到对 IList 的二分搜索实现:

How to perform a binary search on IList<T>?

【讨论】:

这是我最喜欢的答案 :) 应该内置在 SortedList 上。 如果您想做的不仅仅是在排序列表中查找 现有 元素,例如iterating over the head or the tail of a SortedList,请务必使用Antoine Aubry's version,而不是公认的解决方案,如果元素不存在,则仅返回 -1【参考方案2】:

在我的例子中,源 SortedList 没有太大变化,因为它被用作查找表。所以在这种情况下,将SortedList 转换为List&lt;T&gt; 一次是有意义的。之后就很容易使用List&lt;T&gt;的内置BinarySearch方法了...

double targetX = 3.5;

// Assume keys are doubles, may need to convert to doubles if required here.
// The below line should only be performed sparingly as it is an O(n) operation.
// In my case I only do this once, as the list is unchanging.
List<double> keys = xyTable.Keys.ToList();

int ipos = keys.BinarySearch(targetX);

if (ipos >= 0)

    // exact target found at position "ipos"

else

    // Exact key not found: BinarySearch returns negative when the 
    // exact target is not found, which is the bitwise complement 
    // of the next index in the list larger than the target.
    ipos = ~ipos;
    if (ipos >= 0 && ipos < keys.Count)
    
        if (ipos > 0)
        
            // target is between positions "ipos-1" and "ipos"
        
        else
        
            // target is below position "ipos"
        
    
    else
    
        // target is above position "ipos"
    

【讨论】:

目标低于位置“ipos” - 你的意思是它低于数组中的最低键吗? 投反对票,因为有人要求二分搜索对性能感兴趣。 ToList 然而,这是一个多余且缓慢的 O(n) 操作,性能(CPU 和内存消耗)直接在 IList&lt;K&gt; Keys 上工作会更好,如 ColinE's answer 所示,它还链接到一个问题copy-and-pasteable answer. @EugeneBeresovsky 感谢您的评论 - 我添加了关于这一事实的评论。就我而言,正如我在顶部提到的那样,我正在执行二进制搜索的列表是不变的,因此我只在静态构造方法中执行了一次 ToList()。【参考方案3】:
public class Bounds

    int lower;
    int upper;

    public Bounds(int lower, int upper)
    
       this.lower = lower;
       this.upper = upper;
    


public Bounds BinarySearch(List<int> keys, double target)

    // lower boundary case returns the smallest key as the lower and upper bounds
    if (target < keys[0])
        return new Bounds(0, 0);

    else if (target < keys[1])
        return new Bounds(0, 1);

    // upper boundary case returns the largest key as the lower and upper bounds
    else if (target > keys[keys.Length - 1])
        return new Bounds(keys.Length - 1, keys.Length - 1);

    else if (target > keys[keys.Length - 2])
        return new Bounds(keys.Length - 2, keys.Length - 1);

    else
        return BinarySearch(keys, target, 0, keys.Length - 1);



// 'keys' is a List storing all of the keys from your SortedList.
public Bounds BinarySearch(List<int> keys, double target, int lower, int upper)

    int middle = (upper + lower)/2;

    // target is equal to one of the keys
    if (keys[middle] == target)
        return new Bounds(middle - 1, middle + 1);

    else if (keys[middle] < target && keys[middle + 1] > target)
        return new Bounds(middle, middle + 1);

    else if (keys[middle] > target && keys[middle - 1] < target)
        return new Bounds(middle - 1, middle);

    if (list[middle] < target)
         return BinarySearch(list, target, lower, upper/2 - 1);

    if (list[middle] > target)
         return BinarySearch(list, target, upper/2 + 1, upper);

这可能有效..我没有测试它。如果没有,希望它足够接近,您可以通过细微的调整来使用它。这是一个奇怪的问题,所以我处理了所有的边界情况,所以我不必考虑当范围缩小到 2 个元素或更少时算法会做什么。

【讨论】:

为什么不使用List&lt;T&gt;.BinarySearch() 我不是很熟悉.. List.BinarySearch() 是否足以找到他要找的东西? 如果他有List&lt;T&gt;,但他只有IList&lt;T&gt;,所以你的解决方案实际上是一个很好的建议。【参考方案4】:

来自 MSDN,

SortedList 对象的元素根据创建 SortedList 时指定的特定 IComparer 实现或根据键本身提供的 IComparable 实现按键排序。 索引序列基于排序序列。添加元素时,它会以正确的排序顺序插入到 SortedList 中,并且索引会相应调整。当一个元素被删除时,索引也会相应地调整。因此,特定键/值对的索引可能会随着从 SortedList 中添加或删除元素而改变。

*****此方法使用二分查找算法;因此,此方法是 O(log n) 操作,其中 n 为 Count.*****

从 .NET Framework 2.0 开始,此方法使用集合对象的 Equals 和对 item 的 CompareTo 方法来确定 item 是否存在。在早期版本的 .NET Framework 中,此确定是通过对集合中的对象使用 item 参数的 Equals 和 CompareTo 方法进行的。

换句话说,SortedList 中的 IndexOfKey 方法实际上已经使用了 binarySearch 算法,因此在您的情况下,无需将表单从 SortedList 转换为 List。

希望对你有帮助..

【讨论】:

IndexOfKey 只找到完全匹配。问题很明显,这不是我们所需要的,他们需要“围绕我的目标键的上下键”。 由于 IndexOfKey 正在执行 BinarySearch 算法,我很好奇为什么微软不会实现更有用的 BinarySearch 代替(或两者)。除非我的大脑缺少某些东西,否则似乎很容易?

以上是关于SortedList<K, V> 键的二分查找的主要内容,如果未能解决你的问题,请参考以下文章

Java基础知识--HashMap的理解

Java HashMap用法

acwing 264 作诗

Python 3.2 Lambda 语法错误 [重复]

为啥 .NET 中没有 SortedList<T>? [关闭]

HashMap TreeMap ConcurrentHashMap 源码