Quanto segue presuppone che gli insiemi siano memorizzati come un contenitore ordinato (come fa std :: set).
C'è un algoritmo comune per unire due elenchi ordinati per produrre un terzo. L'idea è che quando guardi le teste dei due elenchi, puoi determinare qual è il più basso, estrarlo e aggiungerlo alla coda dell'output, quindi ripetere.
Ci sono varianti che rilevano il caso in cui le due teste sono uguali e trattano questo in modo speciale. Impostare intersezioni e unioni sono esempi di questo.
Con una differenza asimmetrica impostata, il punto chiave è che per A-B, quando si estrae la testa di B, la si scartano. Quando estrai la testata di A, la aggiungi all'ingresso a meno che la testa di B sia uguale, nel qual caso lo estrai anche tu e scartalo entrambi.
Sebbene questo approccio sia progettato per strutture di dati ad accesso sequenziale (e archiviazione su nastro, ecc.), A volte è molto utile fare la stessa cosa per una struttura di dati ad accesso casuale, purché sia ragionevolmente efficiente accedervi comunque in modo sequenziale. E non devi necessariamente estrarre le cose per davvero - puoi invece fare copia e passo.
Il punto chiave è che si passano gli input in sequenza, sempre guardando il valore restante più basso successivo, in modo che (se gli input non hanno duplicati) saranno gli elementi corrispondenti. Quindi sai sempre se il tuo prossimo valore più basso da gestire è un oggetto da A senza corrispondenza in B, e l'elemento in B senza corrispondenza in A, o un elemento uguale in A e B.
Più in generale, l'algoritmo per la differenza dell'insieme dipende dalla rappresentazione dell'insieme. Ad esempio, se il set è rappresentato come un vettore di bit, il precedente sarebbe troppo complesso e lento: dovresti semplicemente eseguire il loop dei vettori eseguendo operazioni bit a bit. Se il set è rappresentato come un hashtable (come nel tr1 unordered_set), quanto sopra è sbagliato in quanto richiede input ordinati.
Se si dispone del proprio codice binario che si sta utilizzando per i set, una buona opzione è quella di convertire entrambi gli alberi in elenchi collegati, lavorare sugli elenchi, quindi convertire l'elenco risultante in un albero perfettamente bilanciato. La differenza tra le liste collegate è molto semplice e le due conversioni sono riutilizzabili per altre operazioni simili.
EDIT
della complessità - usando questi algoritmi di unione simile ordine è O (n), a condizione che si può fare i attraversamenti in ordine a O (n). Anche la conversione in una lista e viceversa è O (n) poiché ognuno dei tre passaggi è O (n) - albero-elenco, differenza tra le serie e lista-albero.
Tree-to-list fondamentalmente esegue un attraversamento in profondità, decostruendo l'albero mentre procede. È un trucco per rendere questo iterativo, memorizzare lo "stack" nei nodi gestiti in parte - cambiare un puntatore sinistro-figlio in un puntatore padre appena prima di passare al figlio sinistro. Questa è una buona idea se l'albero può essere grande e sbilanciato.
Convertire una lista in un albero comporta fondamentalmente un attraversamento in profondità di un albero immaginario (basato sulla dimensione, noto dall'inizio) che lo costruisce per davvero mentre si va. Se un albero ha 5 nodi, ad esempio, puoi dire che la radice sarà il nodo 3. Ricorri per costruire un sottoalbero sinistro a due nodi, quindi prendi l'elemento successivo dall'elenco per quella radice, quindi ricorri per costruire un due -nodo sottostruttura destra.
La conversione da elenco a albero non deve essere implementata in modo iterativo: la ricorsività va bene perché il risultato è sempre perfettamente bilanciato. Se non riesci a gestire la profondità di ricorsione del log n, quasi sicuramente non puoi gestire l'albero completo.
Non esiste una cosa come C-STL. Intendi C++? –
Lo so. Volevo solo chiarire che non volevo soluzioni basate su STL. – Aamir
Dato che STL è un C++ - solo cosa, basta dire che stai usando C e lascia perdere; se la risposta di qualcuno avesse raccomandato STL, sarebbero stati downvoted (e meritatamente). –