2011-05-23 28 views
14

我需要編寫一些線性插值的代碼,我試圖找出最有效的方式來搜索圍繞我的目標鍵的上下鍵的SortedList<K, V>的鍵。排序列表的鍵上的二進制搜索<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之間?我有一個方法/作弊的整數(臨時插入目標鍵到列表中,然後找到索引),但我想我會問專業人員,所以我可以產生高質量的代碼。

謝謝。

+3

排序聽起來很完美的二進制搜索 – Marc 2011-05-23 19:24:52

+1

[日誌(n)的一個例子下界搜索(HTTP://計算器。com/questions/594518/is-there-a-lower-bound-function-in-c-on-a-sortedlist) – digEmAll 2011-05-23 19:35:52

回答

6

二分查找爲您在列表中提供良好的性能。但SortedList上的Keys屬性類型爲IList,而BinarySearch定義爲List。幸運的是,你可以在此相關的問題找到IList二進制搜索的實現:

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

+0

這是我在這裏最喜歡的答案:)應該已經構建在SortedList上。 – nawfal 2014-06-14 09:33:16

+1

如果你想要做的不僅僅是在排序列表中查找* existing *元素,比如[迭代SortedList的頭部或尾部](http://stackoverflow.com/a/31447218/709537),請確保要使用[Antoine Aubry的版本](http://stackoverflow.com/a/2948872/709537),而不是接受的解決方案,在不存在元素的情況下它只返回'-1'。 – 2015-07-17 00:29:15

1
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); 
} 

這可能work..I沒有測試出來。如果沒有,希望它足夠接近,你可以稍微調整使用它。這是一個奇怪的問題,所以我處理了所有的邊界情況,所以我不必考慮當範圍降低到2個元素或更少時算法會執行什麼操作。

+0

爲什麼不使用'List .BinarySearch()'? – svick 2011-05-23 20:41:27

+0

我不是很熟悉它..將列表 .BinarySearch()足以找到他正在尋找什麼? – alexD 2011-05-23 20:45:34

+0

它會是,如果他認爲'列表',但他只有'IList ',所以你的解決方案實際上是一個很好的建議。 – svick 2011-05-23 20:47:05

4

在我的情況下,源SortedList沒有太大的變化,因爲它被用作查找表。所以在這種情況下,將SortedList轉換爲List<T>一次是有意義的。之後,它是很容易使用內置的List<T>二分查找方法...

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" 
    } 
} 
+0

好東西,這是我要給你的答案。 :) – Haney 2014-06-11 13:35:19

+0

*目標低於位置「ipos」* - 你的意思是它低於陣列中的最低鍵? – 2015-07-03 12:15:30

+2

Downvoted,因爲有人要求二進制搜索對性能感興趣。但是,ToList是一個多餘且緩慢的O(n)操作,如[ColinE's answer]所示(直接在IList Keys上運行),性能(CPU和內存消耗)更好(http:// stackoverflow .com/a/6101989/709537),它也鏈接到[複製和粘貼的答案](http://stackoverflow.com/a/2948872/709537)的問題。 – 2015-07-17 00:33:39

2

從MSDN,

的一個排序列表對象由按鍵排序元素或者根據特定的IComparer在創建SortedList時指定的實現,或根據密鑰本身提供的IComparable實現。 索引序列基於排序順序。當添加一個元素時,它會以正確的排序順序插入到SortedList中,並且索引會相應地進行調整。當元素被移除時,索引也會相應地進行調整。因此,特定鍵/值對的索引可能隨着元素從SortedList添加或刪除而改變。

*****此方法使用二進制搜索算法;因此,這種方法是O(log n)的操作,其中n是計數。*****

與.NET Framework 2.0開始,該方法使用該集合的對象的Equals和的CompareTo方法關於項目,以確定是否存在項目。在早期版本的.NET Framework中,通過使用集合中對象的item參數的Equals和CompareTo方法進行此確定。

換句話說,SortedList中的IndexOfKey方法實際上已經使用了binarySearch算法,因此在您的情況下不需要將表單SortedList轉換爲List。

希望它可以幫助..

+1

IndexOfKey僅查找完全匹配。問題很明顯,並非如此,他們需要「圍繞我的目標鍵的上下鍵」。 – Soonts 2016-04-24 21:58:45