7

Nota: Questo è problema 4,3 da Cracking Intervista Coding 5th EditionQuesto algoritmo dovrebbe essere eseguito in O (n)?

Problema: Dato un ordinato (ordine crescente) array, scrivere un algoritmo per creare un albero binario di ricerca con l'altezza minima

Ecco il mio algoritmo, scritto in Java per fare questo problema

public static IntTreeNode createBST(int[] array) { 
     return createBST(array, 0, array.length-1); 
    } 
    private static IntTreeNode createBST(int[] array, int left, int right) { 
     if(right >= left) { 
      int middle = array[(left + right)/2; 
      IntTreeNode root = new IntTreeNode(middle); 
      root.left = createBST(array, left, middle - 1); 
      root.right = createBST(array, middle + 1, right); 
      return root; 
     } else { 
      return null; 
     } 
    } 

ho controllato questo codice contro l'autore del ed è quasi identico.
Tuttavia, sto attraversando un periodo difficile con l'analisi della complessità temporale di questo algoritmo.
So che non funzionerebbe in O (logn) come Binary Search perché non stai facendo la stessa quantità di lavoro ad ogni livello di ricorsione. E.G al primo livello, 1 unità di lavoro, 2 ° livello - 2 unità di lavoro, 3 ° livello - 4 unità di lavoro, fino a registrare (n) livello - n unità di lavoro.

Quindi in base al largo che, il numero di passi questo algoritmi prende sarebbe superiore delimitata da questa espressione matematica

enter image description here

che dopo aver visto Infinite geometric series, ho valutato a enter image description here

o 2n che sarebbe in O (n)

Siete d'accordo con il mio lavoro qui e che questo algoritmo sarebbe eseguito in O (n) o mi sono perso qualcosa o effettivamente eseguito in O (nlogn) o qualche altra classe di funzione?

+0

Sì, è O (n). Lo farei una risposta se avessi un'idea più chiara di ciò che costituisce una buona prova di complessità. – Beta

+0

@Beta Come sei stato in grado di dire senza una prova? – committedandroider

+1

Ho visto che l'algoritmo si chiama due volte, ogni volta su n/2 elementi, con O (1) lavoro extra e un elemento rimosso. Non penso che sia una dimostrazione rigorosa (come in "Sia' f (n) 'e' g (n) 'siano funzioni tali che ..."), ma è stato sufficiente che io potessi vederlo nella mia testa, come un pezzo di corda tagliato a pezzi e disposto senza sovrapposizioni. – Beta

risposta

5

A volte è possibile semplificare i calcoli calcolando la quantità di tempo per articolo nel risultato anziché risolvere le relazioni di ricorrenza. Questo trucco si applica qui. Inizia modificando il codice a questa forma, ovviamente, equivalente:

private static IntTreeNode createBST(int[] array, int left, int right) { 
    int middle = array[(left + right)/2; 
    IntTreeNode root = new IntTreeNode(middle); 
    if (middle - 1 >= left) { 
     root.left = createBST(array, left, middle - 1); 
    } 
    if (right >= middle + 1) { 
     root.right = createBST(array, middle + 1, right); 
    } 
    return root; 
} 

Ora ogni chiamata a createBST crea direttamente 1 nodo. Poiché nella struttura finale ci sono n nodi, ci devono essere n chiamate totali a createBST e poiché ogni chiamata esegue direttamente una quantità costante di lavoro, la complessità temporale complessiva è O (n).

+0

Mi picchia. Mi sono semplicemente seduto per scrivere lo stesso argomento. –

+0

Grazie a questa parte dell'argomento aveva senso - "dato che ci sono n nodi nell'albero finale, ci devono essere n chiamate totali per creareBST". Tuttavia, in che modo la modifica di tale codice mostra che ogni chiamata esegue una quantità costante di lavoro? Una chiamata potrebbe ancora effettuare due chiamate, che sono della stessa dimensione, ma più piccole della chiamata originale. Quelle chiamate della stessa dimensione si stanno riducendo ogni volta, quindi ogni chiamata non sta facendo una quantità costante di lavoro a causa della diminuzione della proprietà della dimensione della chiamata? – committedandroider

+0

@committedandroider Ho modificato il codice in modo che le chiamate per creareBST ei nodi del risultato siano uno a uno. Nel codice originale, alcune chiamate per creareBST hanno restituito null. L'argomento può essere adattato al codice originale, ma è un po 'più confuso: ad esempio, si può sostenere che ci sono al massimo N chiamate per creare BST che creano nodi e al massimo 2 chiamate che restituiscono null. –

1

Se e quando si confonde in ricorsione, sostituire la chiamata ricorsiva (mentalmente, ovviamente) come un ciclo. Ad esempio, nella funzione precedente, è possibile immaginare le chiamate ricorsive all'interno di un "ciclo while". Dato che ora è un ciclo while eseguito fino al momento in cui tutti gli n nodi sono attraversati, la complessità è O (n).

+0

Ma questo non funzionerebbe con qualcosa con quicksort. Visitate tutti gli elementi ma il tempo di esecuzione sarebbe O (n log n), non O (n). Come riformulerai il tuo metodo per questa situazione? Mi piace quello che hai fatto con il ciclo while. – committedandroider

+0

Sì, ammetto che è difficile immaginare loop per algoritmi come l'ordinamento rapido che aumentano logaritmicamente. Tuttavia, l'idea funziona perfettamente per tutte le funzioni che aumentano esponenzialmente come O (n^2), O (n^3), ecc. Abbiamo solo bisogno di usare cicli annidati. –