2015-09-17 6 views
5

mi viene data due set (std :: set da <set>) di cui mi piacerebbe conoscere la dimensione dell'intersezione . Potrei usare std :: set_intersection da <algorithm>, ma devo fornire un iteratore di output per copiare l'intersezione in qualche altro contenitore.come calcolare le dimensioni di un incrocio di due insiemi STL in C++

Un modo semplice sarebbe

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

dopodiché V.SIZE() fornisce la dimensione dell'intersezione. Tuttavia, l'intersezione dovrà essere memorizzata, anche se non facciamo nulla con esso.

Per evitare questo, ho cercato di implementare una classe di uscita iteratore manichino, che conta solo, ma non assegna:

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

usando che potremmo fare

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); 

dopo di che contatore tiene la dimensione dell'intersezione.

Questo è molto più codice tuttavia. Le mie domande sono:

1) Esiste un metodo standard (libreria) o un trucco standard per ottenere la dimensione dell'intersezione senza memorizzare l'intera intersezione? 2) Indipendentemente dal fatto che ci sia o meno, l'approccio con il fittizio iteratore personalizzato è buono?

+1

Sembra troppo complicato per l'identificazione del numero di elementi comuni. Perché non usare solo un loop? – Aldehir

+0

Molto strano, qual è il punto di sapere la dimensione quando non utilizzerai mai l'incrocio? Stai pensando a questo chiaramente? [Leggi questo] (http://meta.stackexchange.com/questions/66377/what-is-the-xy-problem). –

+0

Piuttosto che un iteratore personalizzato, sarebbe più semplice creare un "contenitore" personalizzato che abbia un membro 'insert()' che conta e usa 'insert_iterator' con quello. –

risposta

15

Non è difficile scrivere un ciclo che si muove attraverso le due serie alla ricerca di elementi corrispondenti, oppure si potrebbe fare questo, che è molto più semplice di un iteratore personalizzato:

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

Molto bello, grazie! – doetoe

+0

Per compilarlo (g ++ 4.8.4) ho dovuto fare in Counter una struct come template di T, e annidare un typedef per value_type al suo interno: using value_type = T; – doetoe

+0

Ah si, buon punto. Ho aggiornato la risposta con una soluzione alternativa che ancora significa che 'Counter' non ha bisogno di essere un modello: definisce un' value_type' che può essere costruito da qualsiasi cosa. –

3

Si potrebbe fare questo:

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

Non è ottimamente efficiente, ma dovrebbe essere abbastanza veloce per la maggior parte scopi.

+0

non dovrebbe esserci un confronto con s2.end()? –

+0

@ Cheersandhth.-Alf sì, oops. Fisso. – mattnewport

+0

Che ha una complessità peggiore - 'O (n log n)' contro 'O (n)'. – Ishamael

1

Non è molto difficile scrivere una funzione che lo faccia. This mostra come set_intersection è fatto [anche se l'attuazione effettiva può naturalmente essere leggermente diversa]

Così abbiamo potuto solo prendere quel codice, e modificarlo un po ':

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; 
} 

Anche se nella mia esperienza, quando vuoi sapere quanti sono nell'intersezione, in genere prima o poi vuoi sapere anche quali elementi.

+0

Errori di battitura corretti. Sono stato fuori dal paese ... –

2

È possibile semplificare l'utilizzo di il tuo approccio un bel po ':

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

Buon punto, grazie. Avevo iniziato così, ma poi pensavo che non avrei avuto accesso all'iteratore come passato a set_intersection. Ma ovviamente una copia viene semplicemente restituita. – doetoe

Problemi correlati