Analisi della complessità: ricerca di membri comuni di matrici non ordinate

3

Ho esaminato le precedenti interviste tecniche che ho avuto (ne ho ricevuto un altro in arrivo).

Il problema

Ad ogni modo, una domanda che ho avuto è stata ...

Given 2 unsorted arrays how would you find all of the common objects between them?

Dire che ho array A e B. Il peggiore dei casi A e B sono entrambi di dimensione n.

Pensiero iniziale

Inizialmente il mio pensiero era quello di iterare A e fare una ricerca lineare attraverso B.

La complessità per questo è O(n) * O(n) = O(n^2) .

Ordinamento prima

Tuttavia, mi stavo chiedendo se sarebbe stato meglio prima ordinare B.

L'uso di un ordinamento rapido (o di un tipo di unione) su B è O(n log(n)) . Questo è fatto una volta.

Ora puoi ripetere A O(n) e fare una ricerca binaria su B O(log(n)) per ogni A.

Quindi la complessità è (sort) O(n log(n)) + (iterate A) O(n) * (search B) O(log(n)) che semplifica fino a O(n log(n)) .

La mia domanda è. Ho ragione con questo? Sono molto nuovo all'analisi della complessità, quindi volevo controllare che non stia facendo niente di stupido.

Soluzione migliore

La soluzione migliore è quindi ordinare un array prima di iterare l'altro? Puoi ordinare entrambi gli array e poi iterare ma non stai migliorando O (n log (n).

C'è un altro modo migliore per avvicinarti a questo?

    
posta Fogmeister 23.02.2015 - 12:54
fonte

3 risposte

1

Non penso che ci sia una risposta migliore per la worst case complessità nel caso generale. Probabilmente potresti solo migliorare casi specifici in cui l'input è in qualche modo limitato (diciamo i numeri tra 1 e N). In tal caso potresti usare qualcosa come Ordinamento digitale .

L'idea di Karl Bielefeldt di convertire gli array in insiemi non risolve il problema, lo nasconde solo poiché i set intersecanti (nel caso generale) vengono eseguiti in O (n ^ 2). Ad esempio, ecco l'implementazione retainAll () di Java:

public boolean retainAll(Collection<?> paramCollection) {
        int i = 0;
        Iterator localIterator = iterator();
        while (localIterator.hasNext()) {
            if (!(paramCollection.contains(localIterator.next())))
                ;
            localIterator.remove();
            i = 1;
        }

        return i;
    }

Notare l'iterazione sulla raccolta in arrivo. Per ogni elemento, il metodo chiama contains , che scorre sugli elementi del set corrente:

public boolean contains(Object paramObject) {
        Iterator localIterator = iterator();
        if (paramObject == null)
            while (true) {
                if (!(localIterator.hasNext()))
                    break label53;
                if (localIterator.next() == null)
                    return true;
            }
        while (localIterator.hasNext()) {
            if (paramObject.equals(localIterator.next()))
                return true;
        }
        label53: return false;
    }

La npinti soluzione di usare un HashSet è ugualmente problematica. Un HashSet non garantisce O (1) tempo di recupero nel peggiore dei casi. Infatti, se gli hash di tutti gli elementi N si scontrano, stai osservando O (n) recuperi / inserti. Questo ci riporta a O (n ^ 2) nel peggiore dei casi.

    
risposta data 23.02.2015 - 14:14
fonte
2

Non puoi fare meglio di O(n) , dato che devi esaminare ogni elemento almeno una volta per assicurarti che ci sia una corrispondenza o meno. La mia prima scelta sarebbe quella di convertire gli array in insiemi, che è O(n) , quindi prendere la loro intersezione, che è O(n) sul set più piccolo. In python:

set(array1) & set(array2)

Se devi farlo sul posto, che a volte è una restrizione in questo tipo di esercizi, la tua soluzione è piuttosto buona.

    
risposta data 23.02.2015 - 13:18
fonte
2

Perché non usare un set di hash? La maggior parte delle implementazioni del metodo add() , che viene utilizzato per iniettare elementi nel set, restituisce un valore booleano che indica se un elemento è stato inserito o meno. L'inserimento restituirà false se l'articolo era già lì. Quindi il tuo codice sarà simile a:

HashSet<int> set = {}
Array<int> items1 = ...
Array<int> items2 = ...

foreach (item in items1)
    set.add(item)

foreach(item in items2)
    if(set.add(item) == false)
        out >> item is duplicate

Ciò produrrebbe una complessità temporale di 2n , poiché i set di hash hanno un tempo di recupero pari a O(1) .

Se ricordo bene, O(2n) (che si riduce a O(n) ) sarebbe inferiore a O(n log(n)) . Ciò presuppone che non è necessario ottimizzare in termini di spazio.

    
risposta data 23.02.2015 - 13:18
fonte

Leggi altre domande sui tag