2015-09-17 11 views
5

Tôi có hai bộ (std :: đặt từ <set>) trong đó tôi muốn biết kích thước của giao lộ. Tôi có thể sử dụng std :: set_intersection từ <algorithm>, nhưng tôi phải cung cấp cho nó một bộ lặp đầu ra để sao chép giao lộ vào một số vùng chứa khác.Cách tính kích thước giao điểm của hai bộ STL trong C++

Một cách đơn giản sẽ là

set<int> s1{1,2,3,4,5}; 
    set<int> s2{4,5,6,7,8,9,0,1}; 

    vector<int> v; 

    set_intersection(
     s1.begin(), s1.end(), s2.begin(), s2.end(), 
     inserter(v, v.begin())); 

sau đó v.size() cung cấp cho các kích thước của giao lộ. Tuy nhiên, giao lộ cũng sẽ được lưu trữ, mặc dù chúng tôi không làm gì với nó.

Để tránh điều đó, tôi cố gắng thực hiện một lớp đầu ra iterator giả, mà chỉ đếm, nhưng nó không gán:

template<typename T> 
class CountingOutputIterator { 
private: 
    int* counter_; 
    T dummy_; 
public: 
    explicit CountingOutputIterator(int* counter) :counter_(counter) {} 
    T& operator*() {return dummy_;} 
    CountingOutputIterator& operator++() { // ++t 
    (*counter_)++; 
    return *this; 
    } 
    CountingOutputIterator operator++(int) { // t++ 
    CountingOutputIterator ret(*this); 
    (*counter_)++; 
    return ret; 
    } 
    bool operator==(const CountingOutputIterator& c) { 
    return counter_ == c.counter_; // same pointer 
    } 
    bool operator!=(const CountingOutputIterator& c) { 
    return !operator==(c); 
    } 
}; 

sử dụng mà chúng tôi có thể làm

set<int> s1{1,2,3,4,5}; 
    set<int> s2{4,5,6,7,8,9,0,1}; 

    int counter = 0; 
    CountingOutputIterator<int> counter_it(&counter); 
    set_intersection(
     s1.begin(), s1.end(), s2.begin(), s2.end(), counter_it); 

sau đó bộ đếm giữ kích thước của giao lộ.

Tuy nhiên, đây là mã nhiều hơn nữa. Câu hỏi của tôi là:

1) Có cách nào (thư viện) tiêu chuẩn hoặc thủ thuật tiêu chuẩn để có được kích thước giao lộ mà không lưu trữ toàn bộ giao lộ không? 2) Không phụ thuộc vào việc có hay không, là cách tiếp cận với trình lặp giả định tùy chỉnh hay không?

+1

Dường như quá phức tạp để chỉ xác định số phần tử phổ biến. Tại sao không chỉ sử dụng một vòng lặp? – Aldehir

+0

Rất lạ, điểm của việc biết kích thước khi bạn không bao giờ thực sự sử dụng giao lộ là gì? Bạn đang nghĩ về điều này rõ ràng? [Đọc này] (http://meta.stackexchange.com/questions/66377/what-is-the-xy-problem). –

+0

Thay vì một trình vòng lặp tùy chỉnh, nó sẽ đơn giản hơn để tạo một "thùng chứa" tùy chỉnh có một thành viên 'insert()' đếm và sử dụng 'insert_iterator' với điều đó. –

Trả lời

15

Nó không khó để viết một vòng lặp mà di chuyển qua hai bộ tìm kiếm phù hợp với các yếu tố, hoặc bạn có thể làm được điều này, đó là đơn giản hơn nhiều so với một iterator tùy chỉnh:

struct Counter 
{ 
    struct value_type { template<typename T> value_type(const T&) { } }; 
    void push_back(const value_type&) { ++count; } 
    size_t count = 0; 
}; 

template<typename T1, typename T2> 
size_t intersection_size(const T1& s1, const T2& s2) 
{ 
    Counter c; 
    set_intersection(s1.begin(), s1.end(), s2.begin(), s2.end(), std::back_inserter(c)); 
    return c.count; 
} 
+0

Rất tốt, cảm ơn! – doetoe

+0

Đối với tôi để biên dịch nó (g ++ 4.8.4) Tôi đã phải làm cho Counter như một struct một mẫu của T, và lồng một typedef cho value_type bên trong nó: using value_type = T; – doetoe

+0

Ah vâng, tốt. Tôi đã cập nhật câu trả lời với một sửa chữa thay thế mà vẫn có nghĩa là 'Counter' không cần phải là một mẫu: xác định một' value_type' có thể được xây dựng từ bất cứ thứ gì. –

3

Bạn có thể làm điều này:

auto common = count_if(begin(s1), end(s1), [&](const auto& x){ return s2.find(x) != end(s2); }); 

Đó không phải là cách tối ưu hiệu quả nhưng phải đủ nhanh đối với hầu hết các mục đích.

+0

không nên có so sánh với s2.end()? –

+0

@ Cheersandhth.-Alf yep, oops. Đã sửa. – mattnewport

+0

Độ phức tạp tồi tệ hơn - 'O (n log n)' đối với 'O (n)'. – Ishamael

1

Không khó để viết một hàm thực hiện điều này. This cho thấy cách set_intersection được thực hiện [mặc dù việc thực hiện thực tế có thể đương nhiên được tinh tế khác nhau]

Vì vậy, chúng ta có thể chỉ mất mã, và sửa đổi nó một chút:

template <class InputIterator1, class InputIterator2> 
    size_t set_intersection_size (InputIterator1 first1, InputIterator1 last1, 
           InputIterator2 first2, InputIterator2 last2) 
{ 
    size_t result = 0; 
    while (first1!=last1 && first2!=last2) 
    { 
    if (*first1<*first2) ++first1; 
    else if (*first2<*first1) ++first2; 
    else { 
     result++; 
     ++first1; ++first2; 
    } 
    } 
    return result; 
} 

Mặc dù kinh nghiệm của tôi, khi bạn muốn biết có bao nhiêu người trong giao lộ, bạn thường sớm hay muộn cũng muốn biết những yếu tố nào.

+0

Đã sửa cố định. Đã ra khỏi đất nước ... –

2

Bạn có thể đơn giản hóa việc sử dụng cách tiếp cận của bạn một chút công bằng:

struct counting_iterator 
{ 
    size_t count; 
    counting_iterator& operator++() { ++count; return *this; } 
    // other iterator stuff 
}; 

size_t count = set_intersection(
    s1.begin(), s1.end(), s2.begin(), s2.end(), counting_iterator()).count; 
+0

Điểm tốt, cảm ơn. Tôi đã bắt đầu như thế, nhưng sau đó tôi nghĩ rằng tôi sẽ không có quyền truy cập vào iterator như được thông qua để set_intersection. Nhưng tất nhiên một bản sao chỉ đơn giản là quay trở lại. – doetoe

Các vấn đề liên quan