Kiểm tra xem một mảng có phải là tập con của một mảng khác không


145

Bất kỳ ý tưởng về cách kiểm tra xem danh sách đó là một tập hợp con khác?

Cụ thể, tôi có

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

Làm cách nào để kiểm tra t2 là tập con của t1, sử dụng LINQ?


Nếu các danh sách được sắp xếp (như trong ví dụ của bạn), điều này sẽ có thể trong thời gian O (n + m).
Đại tá hoảng loạn

Câu trả lời:


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

1
Tôi đã tạo phương thức mở rộng geekswithbloss.net/mnf/archive/2011/05/13/ Khăn
Michael Freidgeim

@Bul Ikana Làm việc với mã này rất đơn giản, phương thức mở rộng gọi bên trong là Equals và GetHashCode của các phương thức lớp đối tượng bị ghi đè nếu không có IEqualityComparer cung cấp cho công việc.
Mrinal Kamboj

2
Nếu các danh sách có độ dài n và m, thì độ phức tạp thời gian của thuật toán này là bao nhiêu?
Đại tá hoảng loạn

2
Sẽ rất tuyệt nếu điều này được thực hiện theo phương pháp linq có tên là Contains ALL
Sebastian Patten

60

Sử dụng Hashset thay vì List nếu làm việc với các bộ. Sau đó, bạn có thể chỉ cần sử dụng IsSubsetOf ()

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

bool isSubset = t2.IsSubsetOf(t1);

Xin lỗi vì nó không sử dụng LINQ. :-(

Nếu bạn cần sử dụng danh sách, thì giải pháp của @ Jared sẽ hoạt động với lời cảnh báo rằng bạn sẽ cần xóa mọi phần tử lặp lại tồn tại.


3
Chính xác. Bạn muốn một hoạt động thiết lập, sử dụng lớp được thiết kế cho họ. Giải pháp của Cameron là sáng tạo, nhưng không rõ ràng / biểu cảm như Hashset.
Technophile

2
Tôi không đồng ý vì câu hỏi đặc biệt nói "sử dụng LINQ".
JaredPar

9
@JaredPar: Vậy thì sao? Có phải tốt hơn là chỉ cho ai đó con đường đúng hơn con đường họ muốn đi không?
Jonathan Allen

Một danh sách duy trì thứ tự của nó nhưng một bộ thì không. Nếu thứ tự là quan trọng, điều này sẽ cho kết quả không chính xác.
UuDdLrLrSs

11

Nếu bạn đang kiểm tra đơn vị, bạn cũng có thể sử dụng phương thức CollectionAssert.IsSubsetOf :

CollectionAssert.IsSubsetOf(subset, superset);

Trong trường hợp trên, điều này có nghĩa là:

CollectionAssert.IsSubsetOf(t2, t1);

7

Đây là một giải pháp hiệu quả hơn đáng kể so với các giải pháp khác được đăng ở đây, đặc biệt là giải pháp hàng đầu:

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

Nếu bạn có thể tìm thấy một phần tử duy nhất trong t2 không có trong t1, thì bạn biết rằng t2 không phải là tập con của t1. Ưu điểm của phương pháp này là nó được thực hiện tại chỗ, không phân bổ không gian bổ sung, không giống như các giải pháp sử dụng .Except hoặc .Intersect. Hơn nữa, giải pháp này có thể phá vỡ ngay khi tìm thấy một yếu tố duy nhất vi phạm điều kiện tập hợp con, trong khi các giải pháp khác tiếp tục tìm kiếm. Dưới đây là hình thức dài tối ưu của giải pháp, chỉ nhanh hơn một chút trong các thử nghiệm của tôi so với giải pháp tốc ký trên.

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

Tôi đã làm một số phân tích hiệu suất thô sơ của tất cả các giải pháp, và kết quả rất quyết liệt. Hai giải pháp này nhanh hơn khoảng 100 lần so với các giải pháp .Except () và .Intersect () và không sử dụng thêm bộ nhớ.


Đó chính xác !t2.Except(t1).Any()là những gì đang làm. Linq đang làm việc trở lại. Any()đang hỏi IEnumerablenếu có ít nhất một yếu tố. Trong kịch bản t2.Except(t1)này chỉ phát ra phần tử đầu tiên t2không có trong đó t1. Nếu phần tử đầu tiên t2không có trong t1nó kết thúc nhanh nhất, nếu tất cả các phần tử t2nằm trong t1nó chạy lâu nhất.
năm14

Trong khi chơi xung quanh với một số loại điểm chuẩn, tôi phát hiện ra, khi bạn thực hiện t1={1,2,3,...9999}t2={9999,9998,99997...9000}, bạn có được các phép đo sau : !t2.Except(t1).Any(): 1ms -> t2.All(e => t1.Contains(e)): 702ms. Và nó càng tệ hơn thì phạm vi càng lớn.
năm14

2
Đây không phải là cách Linq hoạt động. t2.Except (t1)đang trở về IEnumerablekhông a Collection. Nó chỉ phát ra tất cả các mục có thể nếu bạn lặp hoàn toàn trên nó, ví dụ bằng cách ToArray ()hoặc ToList ()hoặc sử dụng foreachmà không vi phạm bên trong. Tìm kiếm linq hoãn thực thi để đọc thêm về khái niệm đó.
abto

1
Tôi hoàn toàn nhận thức được cách thức thực thi hoãn lại hoạt động trong Linq. Bạn có thể trì hoãn việc thực hiện tất cả những gì bạn muốn, nhưng khi bạn muốn xác định xem t2 có phải là tập con của t1 hay không, bạn sẽ cần phải lặp lại toàn bộ danh sách để tìm ra nó. Không có xung quanh thực tế đó.
dùng2325458

2
Hãy lấy ví dụ từ nhận xét của bạn t2={1,2,3,4,5,6,7,8} t1={2,4,6,8} t2.Except(t1)=> phần tử đầu tiên của t2 = 1 => chênh lệch 1 đến t1 là 1 (được kiểm tra so với {2,4,6,8}) => Except()phát ra phần tử đầu tiên 1 => Any()lấy phần tử => Any()kết quả là true => không kiểm tra thêm các phần tử trong t2.
abto

6

@ Giải pháp của Cameron như một phương pháp mở rộng:

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

Sử dụng:

bool isSubset = t2.IsSubsetOf(t1);

(Điều này tương tự, nhưng không hoàn toàn giống với bài đăng trên blog của @ Michael)


0

Dựa trên các câu trả lời từ @Cameron và @Neil, tôi đã viết một phương thức mở rộng sử dụng thuật ngữ tương tự như lớp 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

Ở đây chúng tôi kiểm tra xem nếu có bất kỳ phần tử nào có trong danh sách con (tức là t2) không có trong danh sách cha (tức là t1). Nếu không có phần tử nào tồn tại thì danh sách này là tập hợp con của phần tử khác

ví dụ:

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

-1

Thử cái này

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

Ý tưởng ở đây là Intersect sẽ chỉ trả về các giá trị trong cả Mảng. Tại thời điểm này nếu độ dài của tập kết quả giống với tập hợp ban đầu, thì tất cả các phần tử trong "tập hợp" cũng nằm trong "kiểm tra" và do đó "tập hợp" là tập hợp con của "toCheck"

Lưu ý: Giải pháp của tôi không hoạt động nếu "bộ" có trùng lặp. Tôi không thay đổi vì tôi không muốn lấy cắp phiếu bầu của người khác.

Gợi ý: Tôi đã bỏ phiếu cho câu trả lời của Cameron.


4
Điều này hoạt động nếu chúng thực sự là các tập hợp, nhưng không phải nếu "tập hợp" thứ hai chứa các phần tử lặp lại vì nó thực sự là một danh sách. Bạn có thể muốn sử dụng Hashset <double> để đảm bảo rằng nó đã đặt ngữ nghĩa.
tvanfosson

không hoạt động khi cả hai Mảng có các phần tử, không nằm trong Mảng khác.
da_berni
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.