2008-12-02 12 views
116

ist Eine Idee, wie überprüft werden kann, ob diese Liste eine Teilmenge eines anderen ist?Überprüfen, ob ein Array eine Teilmenge eines anderen

Insbesondere, ich habe

List<double> t1 = new List<double> { 1, 3, 5 }; 
List<double> t2 = new List<double> { 1, 5 }; 

Wie das t2 zu überprüfen, eine Teilmenge von t1 ist, mithilfe von LINQ?

+0

Wenn die Listen sortiert sind (wie in Ihrem Beispiel), sollte dies in O (n + m) Zeit möglich sein. –

Antwort

217
bool isSubset = !t2.Except(t1).Any(); 
+14

Die Schönheit der Kürze! – JaredPar

+1

Ich habe Erweiterungsmethode erstellt http://geekswithblogs.net/mnf/archive/2011/05/13/issubsetof-list-extension.aspx –

+1

Könnten Sie ein bisschen erklären, wie das funktioniert? –

0

Versuchen Sie, diese

static bool IsSubSet<A>(A[] set, A[] toCheck) { 
    return set.Length == (toCheck.Intersect(set)).Count(); 
} 

Die Idee dabei ist, dass Intersect nur die Werte zurück, die in den beiden Arrays sind. An diesem Punkt, wenn die Länge der resultierenden Menge die gleiche wie die ursprüngliche Menge ist, sind alle Elemente in "set" ebenfalls "check" und daher ist "set" eine Teilmenge von "toCheck" Die Lösung funktioniert nicht, wenn "set" Duplikate enthält. Ich ändere es nicht, weil ich nicht die Stimmen anderer Leute stehlen will.

Hinweis: Ich habe für Camerons Antwort gestimmt.

+0

Wow, ausgezeichnete Lösung. –

+3

Das funktioniert, wenn sie tatsächlich Mengen sind, aber nicht, wenn das zweite "Set" wiederholte Elemente enthält, da es sich tatsächlich um eine Liste handelt. Sie können HashSet verwenden, um sicherzustellen, dass die Semantik festgelegt wurde. – tvanfosson

+0

Das ist über meinen Kopf. Es ist gut zu wissen, dass ich noch viel mehr zu lernen habe. ;) – Stefan

46

Verwenden Sie HashSet anstelle von List, wenn Sie mit Sets arbeiten. Dann können Sie einfach IsSubsetOf()

HashSet<double> t1 = new HashSet<double>{1,3,5}; 
HashSet<double> t2 = new HashSet<double>{1,5}; 

bool isSubset = t2.IsSubsetOf(t1); 

Leider verwenden, dass es nicht LINQ nicht verwendet. :-(

Wenn Sie Listen verwenden, dann @ Jared Lösung mit dem Vorbehalt, arbeitet, die Sie sich wiederholende Elemente entfernen müssen, die vorhanden sind

+2

Genau. Sie möchten eine Mengenoperation, verwenden Sie die für sie bestimmte Klasse. Camerons Lösung ist kreativ, aber nicht so klar/ausdrucksvoll wie das HashSet. – technophile

+2

Ähm ich stimme nicht zu, weil die Frage ausdrücklich "LINQ verwenden" sagt. – JaredPar

+0

Sie haben einen Tippfehler in der 3. Codezeile. –

4

@ Camerons Lösung als Erweiterung Methode:.

public static bool IsSubsetOf<T>(this IEnumerable<T> a, IEnumerable<T> b) 
{ 
    return !a.Except(b).Any(); 
} 

Verbrauch:

bool isSubset = t2.IsSubsetOf(t1); 

(Dies ist ähnlich, aber nicht ganz die gleiche wie die ein @ Michaels Blog posted on)

8

Wenn Sie Komponententests sind, können Sie auch die CollectionAssert.IsSubsetOf Methode verwenden:

Im obigen Fall dies bedeuten würde: Das

CollectionAssert.IsSubsetOf(t2, t1); 
1

ist eine deutlich effizientere Lösung als die anderen hier gepostet, vor allem die oberste Lösung:

bool isSubset = t2.All(elem => t1.Contains(elem)); 

Wenn Sie ein einzelnes Element in t2 finden, das nicht in t1 ist, dann wissen Sie, dass t2 keine Untermenge von t1 ist. Der Vorteil dieser Methode besteht darin, dass sie im Gegensatz zu den Lösungen, die .Except oder .Intersect verwenden, im Gegensatz zu den Lösungen direkt vor Ort bereitgestellt wird, ohne zusätzlichen Speicherplatz zuzuweisen. Außerdem kann diese Lösung brechen, sobald ein einzelnes Element gefunden wird, das die Teilmengenbedingung verletzt, während die anderen die Suche fortsetzen. Unten ist die optimale lange Form der Lösung, die in meinen Tests nur marginal schneller ist als die obige Kurzschriftlösung.

bool isSubset = true; 
foreach (var element in t2) { 
    if (!t1.Contains(element)) { 
     isSubset = false; 
     break; 
    } 
} 

Ich habe einige rudimentäre Performance-Analyse aller Lösungen, und die Ergebnisse sind drastisch. Diese beiden Lösungen sind etwa 100x schneller als die Lösungen .Except() und .Intersect() und verwenden keinen zusätzlichen Speicher.

+0

Das ist genau das!! T2.Except (t1) .Any() 'tut. Linq arbeitet wieder nach vorne. 'Any()' fragt nach 'IEnumerable', wenn mindestens ein Element vorhanden ist. In diesem Szenario gibt 't2.Except (t1)' nur das erste Element von 't2' aus, das nicht in 't1' ist. Wenn das erste Element von "t2" nicht in "t1" ist, wird es am schnellsten beendet, wenn alle Elemente von "t2" in "t1" sind, läuft es am längsten. – abto

+0

Während ich mit einer Art Benchmark herumspielte, fand ich heraus, wenn man 't1 = {1,2,3, ... 9999}' und 't2 = {9999,9998,99997 ... 9000}' nimmt, Sie erhalten folgende Messungen: '! t2.Except (t1) .Any(): 1ms -> t2.All (e => t1.Contains (e)): 702ms'. Und es wird immer schlimmer, je größer der Bereich. – abto

+0

Das ist falsch. t2.Except (t1) ist eine gesetzte Subtraktionsoperation und gibt ALLE verbleibenden Elemente von t2 minus t1 zurück. Es erstellt eine komplett neue Sammlung und benötigt dafür zusätzlichen Speicher. Es gibt nicht nur das erste Element von t2 aus, das nicht in t1 ist, es emittiert ALLE Elemente von t2, die nicht in t1 sind. Um die Menge aller Elemente, die nicht in t1 sind, zurückzugeben, muss sie die gesamte Menge durchlaufen. Wenn zum Beispiel t2 = {1,2,3,4,5,6,7,8} und t1 = {2,4,6,8}, dann liefert t2.Except (t1) {1,3,5 , 7}. – user2325458

0

Aufbauend auf den Antworten von @Cameron und @Neil habe ich eine Erweiterungsmethode geschrieben, die dieselbe Terminologie wie die Enumerable-Klasse verwendet.

/// <summary> 
/// Determines whether a sequence contains the specified elements by using the default equality comparer. 
/// </summary> 
/// <typeparam name="TSource">The type of the elements of source.</typeparam> 
/// <param name="source">A sequence in which to locate the values.</param> 
/// <param name="values">The values to locate in the sequence.</param> 
/// <returns>true if the source sequence contains elements that have the specified values; otherwise, false.</returns> 
public static bool ContainsAll<TSource>(this IEnumerable<TSource> source, IEnumerable<TSource> values) 
{ 
    return !values.Except(source).Any(); 
} 
Verwandte Themen