2009-02-02 16 views
6

Belirli bir öğeyi değil, belirli bir koşulu karşılayan bir öğe için aramak istediğim bir List<T> var. Ben doğrudur 4 koşulların hangi test edebilirsiniz listedeki bir öğe Verilen:Temsilci koşulunu kullanarak bir C# listesi ikili araması

  • istediğiniz öğeye istediğiniz öğeye sağ
  • olmalıdır sol
  • olmalıdır bunun istenen madde
  • olduğunu
  • istenen liste fonksiyonları hızlı bir bakış kimse kullanabileceğim bir işlev kapalı bilir, merak ediyorum bu yüzden cesaret verici değildi listede

olamaz?

Düzenleme: Bu yüzden doğru

Edit sıralanacaktır bilinmektedir bir yerel geçici listesi: binarySearch neredeyse sağ görünüyor ama benim durumumda ben ile karşılaştırmak için bir öğe yok. Jon Skeet'in çözümünü kullanırdım ve bir tartışmayı göz ardı ederdim, ama ben her zaman aynı arg olmak için güvenebileceğime emin değilim.

cevap

19

Yeni düzenleme: Diğerleri için yararlı olacağından, aşağıdaki fazladan ikili aramaları aşağıda bırakacağım ve işte tam olarak istediğinizi düşündüğüm son bir seçenek. Aradığınız öğe "belirtilenden daha az", belirtilmiş olandan "büyük" ise negatif bir sayı ve doğruysa 0 olmalıdır.

public static int BinarySearchForMatch<T>(this IList<T> list, 
    Func<T,int> comparer) 
{ 
    int min = 0; 
    int max = list.Count-1; 

    while (min <= max) 
    { 
     int mid = (min + max)/2; 
     int comparison = comparer(list[mid]); 
     if (comparison == 0) 
     { 
      return mid; 
     } 
     if (comparison < 0) 
     { 
      min = mid+1; 
     } 
     else 
     { 
      max = mid-1; 
     } 
    } 
    return ~min; 
} 

Eski düzenleme: aşağıda orijinal cevabını bırakacağım, ama burada diğer iki seçenek vardır.

İlki, kaynak verilerden bir anahtar türüne bir yansıtma alır ve bulmak için anahtarı belirler. Karşılaştırma kendisi sadece bu anahtar türü için varsayılan şekilde yapılır:

public static int BinarySearchBy<TSource,TKey>(this IList<TSource> list, 
    Func<TSource,TKey> projection, TKey key) 
{ 
    int min = 0; 
    int max = list.Count-1; 

    while (min <= max) 
    { 
     int mid = (min + max)/2; 
     TKey midKey = projection(list[mid]); 
     int comparison = Comparer<TKey>.Default.Compare(midKey, key); 
     if (comparison == 0) 
     { 
      return mid; 
     } 
     if (comparison < 0) 
     { 
      min = mid+1; 
     } 
     else 
     { 
      max = mid-1; 
     } 
    } 
    return ~min; 
} 

ikinci aradığımız anahtarla listeden bir öğe karşılaştırmak yerine bir Func sürer. Kod elbette, neredeyse tamamen aynıdır - bu değişikliği sadece karşılaştırma var: Bu ikisi test edilmemiştir

public static int BinarySearchBy<TSource,TKey>(this IList<TSource> list, 
    Func<TSource,TKey,int> comparer, TKey key) 
{ 
    int min = 0; 
    int max = list.Count-1; 

    while (min <= max) 
    { 
     int mid = (min + max)/2; 
     int comparison = comparer(list[mid], key); 
     if (comparison == 0) 
     { 
      return mid; 
     } 
     if (comparison < 0) 
     { 
      min = mid+1; 
     } 
     else 
     { 
      max = mid-1; 
     } 
    } 
    return ~min; 
} 

, ama en azından derlemek mi :)

Orjinal cevap:

List<T>.BinarySearch'u IComparer<T> ile kullanabilirsiniz.IComparer<T> kendi uygulamanızı yazmak zorunda değilsiniz - MiscUtil numaralı telefondan, Comparison<T> temsilcisinden bir IComparer<T> oluşturur. Bu sadece ilk üç koşulu gösterir, ancak ikili arama geri kalanından sonuncusu çalışır. Aslında, bu kadar kısa bir kod, ben de buraya yapıştırıp yapıştırabilirim (sans yorumlar).

public sealed class ComparisonComparer<T> : IComparer<T> 
{ 
    readonly Comparison<T> comparison; 

    public ComparisonComparer(Comparison<T> comparison) 
    { 
     if (comparison == null) 
     { 
      throw new ArgumentNullException("comparison"); 
     } 
     this.comparison = comparison; 
    } 

    public int Compare(T x, T y) 
    { 
     return comparison(x, y); 
    } 
} 

Yani böyle bir şey yapmak olabilir:

var comparer = new ComparisonComparer<Person>((p1, p2) => p1.ID.CompareTo(p2.ID)); 
int index = list.BinarySearch(employee, comparer); 

MiscUtil da olan bir ProjectionComparer ilginizi olabilir - sadece sadece LINQ ile OrderBy olduğu gibi bir projeksiyon belirtmek - ama gerçekten bağlıdır kullanım durumunuz.

+0

En yeni cevabınızı beğendim. İçinde bir yazım hatası olduğunu düşünüyorum T <-> TSource – BCS

+0

Oops. Düzeltecek. –

+0

Hey Jon, yöntemin sonunda neden en az geri döndüğünüzü açıklayabilir misiniz? Bu durumda bitleri çevirmenin öneminin ne olduğundan emin değilim. –

1

Bu şartlardan emin misiniz? Normalde bu liste sıralanırsa çalışır, bu durumda belki de ilk olarak bir SortedList<TKey, TValue> kullanmak istersiniz.

Her iki durumda da, C# 3.0 veya daha sonraki bir sürümü varsayarak, muhtemelen .Where() yöntemini isteyebilirsiniz. Durumunuzu bir Lambda olarak yazın ve çerçevenin aramayı yapmasına izin verin. Koleksiyona uygun bir arama tekniği kullanmalıdır.

+0

Hiçbir anahtar (değer anahtardır) olmadığı için SortedList Ödülü olur. – BCS

+0

Doğrusal bir tarama nerede - ne aradığınızı ve ne tür bir sıralama düzeni olduğunu bilebilir. –

+0

Kod re: Lambda (LINQ'de hiç bakmadım/bakmadım.) – BCS

1

Nesneniz için özel bir karşılaştırıcıda uygulamak isteyebilirsiniz (garip olarak adlandırılan karşılaştırma C# dokümanlar).

+0

Bunu yapmak için kod, tam arama benim kendi yapmak için kod ile aynıdır. – BCS