Nuova modifica: Lascerò le ricerche binarie aggiuntive di seguito, in quanto saranno utili per gli altri, ed ecco un'opzione finale che è ciò che penso in realtà. Il tuo delegato dovrebbe restituire un numero positivo se l'elemento che sta cercando è "minore di" quello specificato, un numero negativo se è "maggiore di" quello specificato e 0 se è giusto.
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;
}
Vecchio edit: Lascio la risposta originale al di sotto, ma qui ci sono altre due opzioni.
Il primo prende una proiezione dai dati di origine a un tipo di chiave e specifica la chiave da trovare. Il confronto si è solo fatto in modo predefinito per quel tipo di chiave:
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;
}
Il secondo prende un Func, invece, per confrontare un elemento dalla lista con la chiave che stiamo cercando. Il codice è quasi esattamente la stessa, ovviamente - è solo il confronto che cambia:
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;
}
Questi sono entrambi non testato, ma fare almeno compilare :)
risposta originale:
È possibile utilizzare List<T>.BinarySearch
con un IComparer<T>
.Non è necessario scrivere la propria implementazione di IComparer<T>
- I've got on in MiscUtil che crea undelegato. Ciò indica solo le prime tre condizioni, ma la ricerca binaria risolverà l'ultima dal resto.
In effetti, il codice è così breve che potrei anche incollarlo qui (sans commenti, ammetto).
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);
}
}
Così si potrebbe fare qualcosa di simile:
var comparer = new ComparisonComparer<Person>((p1, p2) => p1.ID.CompareTo(p2.ID));
int index = list.BinarySearch(employee, comparer);
MiscUtil ha anche un ProjectionComparer
potreste essere interessati a - basta specificare una proiezione, proprio come in OrderBy
con LINQ - ma in realtà dipende il tuo caso d'uso.
Mi piace la tua ultima risposta. Penso che ci sia un errore di battitura in esso T <-> TSource – BCS
Oops. Correggerà. –
Hey Jon potresti spiegare perché stai tornando ~ min alla fine del metodo? Non sono abbastanza sicuro di quale sia il significato di lanciare i bit in questo caso. –