Kiểm tra xem tất cả các giá trị trong danh sách có phải là duy nhất không


90

Tôi có một danh sách nhỏ các byte và tôi muốn kiểm tra xem chúng đều có các giá trị khác nhau. Ví dụ, tôi có cái này:

List<byte> theList = new List<byte> { 1,4,3,6,1 };

Cách tốt nhất để kiểm tra xem tất cả các giá trị có khác biệt hay không?


2
Vì đây là một câu hỏi điển hình về phòng học, tôi sẽ trả lời bằng một câu hỏi. Bạn sẽ làm như thế nào nếu nó được sắp xếp?
— ctrl-alt-delor

Câu trả lời:


168
bool isUnique = theList.Distinct().Count() == theList.Count();

Chỉ tò mò: yêu cầu về không gian và thời gian của việc này là gì?
— dtb

10
@dtb phải nằm trong khoảng O (N) . Tất nhiên, coi đây là một "danh sách nhỏ", nó sẽ nhanh như chớp với hầu hết mọi thuật toán. IMO này chiến thắng ở khả năng đọc và tính ngắn gọn, và vì tốc độ không phải là một vấn đề, điều đó làm cho nó trở nên hoàn hảo.
— Tim S.

2
Điều này là hiệu quả hơn so với nó có thể
— Jodrell

74

Đây là một cách tiếp cận khác hiệu quả hơn Enumerable.Distinct+ Enumerable.Count(hơn thế nữa nếu chuỗi không phải là một loại tập hợp). Nó sử dụng một HashSet<T>để loại bỏ các bản sao, rất hiệu quả trong việc tra cứu và có một thuộc tính đếm:

var distinctBytes = new HashSet<byte>(theList);
bool allDifferent = distinctBytes.Count == theList.Count;

hoặc cách tiếp cận khác - tinh tế và hiệu quả hơn -:

var diffChecker = new HashSet<byte>();
bool allDifferent = theList.All(diffChecker.Add);

HashSet<T>.Addtrả về falsenếu phần tử không thể được thêm vì nó đã có trong HashSet. Enumerable.Alldừng ở "false" đầu tiên.


1
quá đơn giản và rõ ràng, tại sao tôi không nghĩ về nó trước :) Tôi đã sử dụng một lớp lót này trong bài kiểm tra đơn vị để xác nhận 10 triệu phần tử được tạo bởi mã tuyệt vời của tôi thực sự là duy nhất Assert.IsTrue(samples.Add(AwesomeClass.GetUnique()));. Họ đã và đang :) 1 cho bạn Tim :)
— grapkulec

1
tôi đã cố gắng trả lời của bạn cho câu hỏi này, nhưng nó không được thưa ông làm việc: stackoverflow.com/questions/34941162/...
— Learning-Overthinker-Confused

Phải được điều này:bool allDifferent = theList.All(s => diffChecker.Add(s))
— mike nelson

2
Không, không cần thiết. Trong trường hợp này bạn có thể vượt qua các đại biểu trực tiếp
— Tim Schmelter

1
@ AndréReichelt - Tôi vừa mở mã của bạn và kịch bản thứ ba ( List.All(HashSet.Add)) dường như nhanh hơn nhiều so với hai kịch bản còn lại trong hầu hết các trường hợp
— Kyle Delaney

6

Được rồi, đây là phương pháp hiệu quả nhất mà tôi có thể nghĩ đến khi sử dụng .Net chuẩn

using System;
using System.Collections.Generic;

public static class Extension
{
    public static bool HasDuplicate<T>(
        this IEnumerable<T> source,
        out T firstDuplicate)
    {
        if (source == null)
        {
            throw new ArgumentNullException(nameof(source));
        }

        var checkBuffer = new HashSet<T>();
        foreach (var t in source)
        {
            if (checkBuffer.Add(t))
            {
                continue;
            }

            firstDuplicate = t;
            return true;
        }

        firstDuplicate = default(T);
        return false;
    }
}

Về cơ bản, điểm của việc liệt kê toàn bộ dãy hai lần là gì nếu tất cả những gì bạn muốn làm là tìm bản sao đầu tiên.

Tôi có thể tối ưu hóa điều này nhiều hơn bằng cách đặt cách viết hoa đặc biệt cho các chuỗi phần tử trống và đơn lẻ nhưng điều đó sẽ mất đi khả năng đọc / bảo trì với mức tăng tối thiểu.



Tôi đã thử nghiệm 3 giải pháp ở đây và đây thực sự là giải pháp hiệu quả nhất trên trang này. Tuy nhiên, một vài lỗi chính tả trong đó (ví dụ:sequence nên có source). Nhưng hoạt động tốt khi những điều này đã được sửa
— mike nelson

@mikenelson, điều đó sẽ tốt hơn
— Jodrell

2
Để dễ đọc, tôi nghĩ rằng nó nên if (!checkBuffer.Add(t)) { firstDuplicate = t; return true }ở trong vòng lặp.
— tia

2

Logic tương tự để Distinctsử dụng GroupBy:

var isUnique = theList.GroupBy(i => i).Count() == theList.Count;

Điều này hữu ích nếu bạn muốn kiểm tra tính duy nhất đối với một thuộc tính theList.GroupBy(o => o.SomeProperty).Count() == theList.Count;trong khi Distinction () không cho phép điều đó.
— Rev1.0

1

Người ta cũng có thể làm: Sử dụng Hashset

var uniqueIds = new HashSet<long>(originalList.Select(item => item.Id));

            if (uniqueIds.Count != originalList.Count)
            {
            }

0

Có nhiều giải pháp.

Và không nghi ngờ gì nữa, những bức ảnh đẹp hơn với việc sử dụng LINQ như "juergen d" và "Tim Schmelter" đã đề cập.

Tuy nhiên, nếu bạn không có "Độ phức tạp" và tốc độ, giải pháp tốt nhất sẽ là tự thực hiện nó. Một trong những giải pháp sẽ là tạo một mảng kích thước N (đối với byte là 256). Và lặp lại mảng và trên mỗi lần lặp sẽ kiểm tra chỉ số số phù hợp nếu giá trị là 1 nếu nó có, điều đó có nghĩa là tôi đã tăng chỉ số mảng và do đó mảng không khác biệt, nếu không tôi sẽ tăng ô mảng và tiếp tục kiểm tra .


2
bạn có thể sử dụng một vector bit với 256 bit = 32 byte = 8 số nguyên. Nhưng Big O = O (n) của bạn vẫn sẽ giống như sử dụng Hashet được đề xuất trong câu trả lời khác.
— BrokenGlass

Đây là O (n) nên có thể nhanh nhất, (hãy kiểm tra nó). Việc kiểm tra sẽ được tính khi bạn thực hiện hay khi kết thúc là nhanh nhất? Tôi nghi ngờ rằng cuối cùng sẽ cải thiện trường hợp xấu nhất, nhưng khi bạn đi có thể cải thiện trường hợp trung bình và tốt nhất). Nếu không có bản sao thì đây sẽ là hiệu suất trong trường hợp xấu nhất. Ngoài ra đối với các loại dữ liệu lớn hơn, điều này sẽ không hoạt động tốt, đối với loại 16bit, bạn sẽ phải sử dụng 64k bộ đếm, 64k bit (8k byte), nhưng đối với bất kỳ thứ gì lớn hơn, việc sử dụng bộ nhớ sẽ bắt đầu trở nên ngớ ngẩn. Tuy nhiên tôi thích câu trả lời này cho các giá trị 8bit.
— ctrl-alt-delor

1
@TamusJRoyce nếu bạn muốn lưu trữ 4294967296 khả năng, bạn cần 4GB chứ không phải 42MB (hoặc 512MB trong số bạn sử dụng mặt nạ bit)
— tigrou

Không chắc tôi đang nghĩ gì. "Phân bổ 42MB + bộ nhớ để chứa tất cả 4294967296 khả năng. Và sử dụng bộ đếm nhóm đơn giản. Hoặc thậm chí sử dụng xor che bit và kiểm tra xem có bit nào bị thay đổi từ true thành false hay không. 42MB + / 8 = 5MB + Chi phí có vẻ quá lớn với phần cứng ngày nay. Nhưng một ngày nào đó điều này có thể mang lại giá trị. " không thực sự là một bình luận có liên quan. Hashset sẽ là tốt nhất. Nếu bạn đang xử lý các mảng cực lớn, bạn mong đợi bộ nhớ cực lớn. Nhưng trong một trường hợp cạnh kỳ lạ như vậy, một trò đùa với thuật toán CRC sẽ tốt hơn. Ánh xạ nó thành một đa thức. Nếu đóng, hãy đánh giá. Xin cảm ơn @tigrou!
— TamusJRoyce

0

Và một giải pháp khác, nếu bạn muốn tìm các giá trị trùng lặp.

var values = new [] { 9, 7, 2, 6, 7, 3, 8, 2 };

var sorted = values.ToList();
sorted.Sort();
for (var index = 1; index < sorted.Count; index++)
{
    var previous = sorted[index - 1];
    var current = sorted[index];
    if (current == previous)
        Console.WriteLine(string.Format("duplicated value: {0}", current));
}

Đầu ra:

duplicated value: 2
duplicated value: 7

http://rextester.com/SIDG48202


0

Tôi kiểm tra xem IEnumerable (aray, list, v.v.) có phải là duy nhất như sau không:

var isUnique = someObjectsEnum.GroupBy(o => o.SomeProperty).Max(g => g.Count()) == 1;
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 cookie và Chính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.