ตรวจสอบว่าอาร์เรย์เป็นชุดย่อยของอีกชุดหรือไม่


145

ความคิดเกี่ยวกับวิธีการตรวจสอบว่ารายการนั้นเป็นส่วนย่อยของอีก

โดยเฉพาะฉันมี

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

วิธีตรวจสอบว่า t2 เป็นชุดย่อยของ t1 โดยใช้ LINQ อย่างไร


หากมีการเรียงรายการ (ตามตัวอย่างของคุณ) สิ่งนี้ควรเป็นไปได้ในเวลา O (n + m)
พันเอก Panic

คำตอบ:


255
bool isSubset = !t2.Except(t1).Any();

1
ฉันสร้างวิธีส่วนขยายแล้วgeekswithblogs.net/mnf/archive/2011/05/13/ …
Michael Freidgeim

@Bul Ikana การทำงานของรหัสนี้เป็นเรื่องง่ายวิธีการขยายภายในเรียก Equals และ GetHashCode ของวิธีการแทนที่คลาสอ็อบเจ็กต์ถ้าไม่มี IEqualityComparer ที่จัดไว้ให้สำหรับงาน
Mrinal Kamboj

2
หากรายการมีความยาว n และ m ความซับซ้อนของเวลาของอัลกอริทึมนี้คืออะไร
พันเอก Panic

2
จะดีถ้าสิ่งนี้ถูกต้มลงไปเป็นวิธี linq ที่เรียกว่า
Sebastian Patten

60

ใช้ HashSet แทน List ถ้าทำงานกับชุด จากนั้นคุณสามารถใช้IsSubsetOf ()

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

bool isSubset = t2.IsSubsetOf(t1);

ขออภัยที่ไม่ใช้ LINQ :-(

หากคุณต้องการใช้รายการวิธีแก้ปัญหาของ @ Jared จะทำงานร่วมกับข้อแม้ที่คุณจะต้องลบองค์ประกอบที่มีอยู่ซ้ำ ๆ


3
เผง คุณต้องการชุดปฏิบัติการใช้คลาสที่ออกแบบมาสำหรับพวกเขา โซลูชันของ Cameron นั้นมีความคิดสร้างสรรค์ แต่ไม่ชัดเจน / แสดงออกอย่าง HashSet
technophile

2
อืมฉันไม่เห็นด้วยเพราะคำถามบอกว่า "ใช้ LINQ" โดยเฉพาะ
JaredPar

9
@ JaredPar: งั้นเหรอ? มันไม่ดีกว่าที่จะแสดงให้ใครบางคนเห็นวิธีที่ถูกต้องกว่าวิธีที่พวกเขาต้องการที่จะไป?
Jonathan Allen

รายการรักษาคำสั่งซื้อ แต่ชุดไม่ได้ หากคำสั่งซื้อมีความสำคัญสิ่งนี้จะให้ผลลัพธ์ที่ไม่ถูกต้อง
UuDdLrLrSs

11

หากคุณทำการทดสอบหน่วยคุณสามารถใช้เมธอดCollectionAssert.IsSubsetOf :

CollectionAssert.IsSubsetOf(subset, superset);

ในกรณีข้างต้นสิ่งนี้จะหมายถึง:

CollectionAssert.IsSubsetOf(t2, t1);

7

นี่เป็นโซลูชันที่มีประสิทธิภาพมากกว่าที่อื่น ๆ ที่โพสต์ไว้ที่นี่โดยเฉพาะโซลูชันยอดนิยม:

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

หากคุณสามารถหาองค์ประกอบเดียวใน t2 ที่ไม่ได้อยู่ใน t1 คุณจะรู้ว่า t2 ไม่ใช่ชุดย่อยของ t1 ข้อดีของวิธีนี้คือทำทุกอย่างในสถานที่โดยไม่ต้องจัดสรรพื้นที่เพิ่มเติมซึ่งแตกต่างจากโซลูชันที่ใช้. ยกเว้นหรือ. Intersect นอกจากนี้วิธีนี้จะสามารถแตกได้ทันทีที่พบองค์ประกอบหนึ่งที่ละเมิดเงื่อนไขส่วนย่อยในขณะที่คนอื่น ๆ ยังคงค้นหา ด้านล่างเป็นรูปแบบที่ยาวที่สุดของการแก้ปัญหาซึ่งเร็วกว่าเล็กน้อยในการทดสอบของฉันมากกว่าวิธีการจดชวเลขข้างต้น

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

ฉันทำการวิเคราะห์ประสิทธิภาพพื้นฐานของโซลูชันทั้งหมดและผลลัพธ์นั้นรุนแรงมาก โซลูชันทั้งสองนี้เร็วกว่าโซลูชัน. ยกเว้น () และ .Intersect () ประมาณ 100x และไม่ต้องใช้หน่วยความจำเพิ่มเติม


นั่นคือสิ่งที่!t2.Except(t1).Any()กำลังทำอยู่ Linq กำลังทำงานกลับไปกลับมา Any()กำลังถามIEnumerableว่ามีองค์ประกอบอย่างน้อยหนึ่งองค์ประกอบหรือไม่ ในสถานการณ์t2.Except(t1)นี้ปล่อยองค์ประกอบแรกt2ที่ไม่ได้อยู่ในองค์ประกอบt1เท่านั้น หากองค์ประกอบแรกของt2ไม่อยู่ในt1นั้นจะเสร็จเร็วที่สุดหากองค์ประกอบทั้งหมดt2อยู่ในt1นั้นจะทำงานได้นานที่สุด
abto

ในขณะที่การเล่นรอบกับการจัดเรียงของมาตรฐานบางอย่างที่ผมพบเมื่อคุณใช้เวลาt1={1,2,3,...9999}และคุณจะได้รับการตรวจวัดดังต่อไปนี้:t2={9999,9998,99997...9000} !t2.Except(t1).Any(): 1ms -> t2.All(e => t1.Contains(e)): 702msและยิ่งแย่ลงยิ่งช่วงกว้างยิ่งขึ้น
abto

2
นี่ไม่ใช่วิธีการทำงานของ Linq t2.Except (t1)ส่งคืนไม่ได้เป็นIEnumerable Collectionมันจะปล่อยไอเท็มที่เป็นไปได้ทั้งหมดหากคุณวนซ้ำอย่างสมบูรณ์เช่นToArray ()หรือโดยการToList ()ใช้หรือforeachไม่ใช้ภายใน ค้นหาlinq deferred executionเพื่ออ่านเพิ่มเติมเกี่ยวกับแนวคิดนั้น
abto

1
ฉันรู้ดีว่าการดำเนินการที่เลื่อนเวลาออกไปทำงานใน Linq คุณสามารถเลื่อนการประมวลผลทั้งหมดที่คุณต้องการได้ แต่เมื่อคุณต้องการตรวจสอบว่า t2 เป็นชุดย่อยของ t1 หรือไม่คุณจะต้องวนซ้ำรายการทั้งหมดเพื่อให้เข้าใจได้ ไม่มีการรับข้อเท็จจริงที่
user2325458

2
ให้นำตัวอย่างจากความคิดเห็นของคุณt2={1,2,3,4,5,6,7,8} t1={2,4,6,8} t2.Except(t1)=> องค์ประกอบแรกของ t2 = 1 => ความแตกต่างของ1ถึง t1 คือ1 (ตรวจสอบกับ {2,4,6,8}) => Except()ปล่อยองค์ประกอบแรก1 => Any()รับองค์ประกอบ => Any()ผลลัพธ์ใน true => ไม่มีการตรวจสอบองค์ประกอบเพิ่มเติมใน t2
abto

6

วิธีการแก้ปัญหา @ Cameron เป็นวิธีการขยาย:

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

การใช้งาน:

bool isSubset = t2.IsSubsetOf(t1);

(นี่คล้ายกัน แต่ไม่เหมือนกับที่โพสต์บนบล็อกของ @ Michael)


0

การสร้างคำตอบจาก @Cameron และ @Neil ฉันได้เขียนวิธีการขยายที่ใช้คำศัพท์เดียวกันกับคลาส Enumerable

/// <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();
}

0

ที่นี่เราตรวจสอบว่าหากมีองค์ประกอบใด ๆ ที่อยู่ในรายการลูก (เช่นt2) ซึ่งไม่ได้อยู่ในรายชื่อผู้ปกครอง (เช่นt1) หากไม่มีใครอยู่แล้วรายการนั้นเป็นส่วนย่อยของอีก

เช่น:

bool isSubset = !(t2.Any(x => !t1.Contains(x)));

-1

ลองสิ่งนี้

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

แนวคิดที่นี่คือ Intersect จะคืนค่าที่อยู่ในทั้งสองอาร์เรย์เท่านั้น ณ จุดนี้หากความยาวของชุดผลลัพธ์เป็นเหมือนชุดเดิมองค์ประกอบทั้งหมดใน "set" จะอยู่ใน "check" ด้วยดังนั้น "set" จึงเป็นชุดย่อยของ "toCheck"

หมายเหตุ: โซลูชันของฉันไม่ทำงานหาก "set" ซ้ำกัน ฉันไม่เปลี่ยนเพราะฉันไม่ต้องการขโมยคะแนนของคนอื่น

คำแนะนำ: ฉันโหวตให้คำตอบของ Cameron


4
มันใช้งานได้ถ้ามันถูกเซตแน่นอน แต่ไม่ใช่ถ้า "set" ตัวที่สองมีองค์ประกอบที่ซ้ำกันเพราะมันเป็นรายการจริงๆ คุณอาจต้องการใช้ HashSet <double> เพื่อให้แน่ใจว่ามันมีการตั้งค่าความหมาย
tvanfosson

ไม่ทำงานเมื่อทั้งสองอาร์เรย์มีองค์ประกอบซึ่งไม่ได้อยู่ในอาร์เรย์อื่น
da_berni
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.