SortedList<K, V> 键的二分查找
Posted
技术标签:
【中文标题】SortedList<K, V> 键的二分查找【英文标题】:Binary Search on Keys of SortedList<K, V> 【发布时间】:2011-08-31 09:41:30 【问题描述】:我需要为线性插值编写一些代码,并且我正在尝试找出最有效的方法来搜索 SortedList<K, V>
的键以查找围绕我的目标键的上下键。
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<T>
一次是有意义的。之后就很容易使用List<T>
的内置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<K> 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<T>.BinarySearch()
?
我不是很熟悉.. ListList<T>
,但他只有IList<T>
,所以你的解决方案实际上是一个很好的建议。【参考方案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> 键的二分查找的主要内容,如果未能解决你的问题,请参考以下文章