2010-10-20 27 views
5

Herkese selamlıyor: Bir ikili arama ağacında iki düğümün en düşük ortak atalarını bulmak için aşağıdaki algoritmayı okurum.Bu algoritmanın uzay karmaşıklığı neden O (1)

/* A binary tree node has data, pointer to left child 
    and a pointer to right child */ 
struct node 
{ 
    int data; 
    struct node* left; 
    struct node* right; 
}; 

struct node* newNode(int); 

/* Function to find least comman ancestor of n1 and n2 */ 
int leastCommanAncestor(struct node* root, int n1, int n2) 
{ 
/* If we have reached a leaf node then LCA doesn't exist 
If root->data is equal to any of the inputs then input is 
not valid. For example 20, 22 in the given figure */ 
if(root == NULL || root->data == n1 || root->data == n2) 
return -1; 

/* If any of the input nodes is child of the current node 
we have reached the LCA. For example, in the above figure 
if we want to calculate LCA of 12 and 14, recursion should 
terminate when we reach 8*/ 
if((root->right != NULL) && 
    (root->right->data == n1 || root->right->data == n2)) 
    return root->data; 
if((root->left != NULL) && 
(root->left->data == n1 || root->left->data == n2)) 
return root->data; 

if(root->data > n1 && root->data < n2) 
    return root->data; 
if(root->data > n1 && root->data > n2) 
    return leastCommanAncestor(root->left, n1, n2); 
if(root->data < n1 && root->data < n2) 
    return leastCommanAncestor(root->right, n1, n2); 
}  

Not fonksiyonu n1, n2 daha küçük olduğunu varsayar Yukarıdaki. Zaman karmaşıklığı: O (n) alan karmaşıklığı: Q (1)

bu algoritma bu yüzden, yinelemeli işlev çağrısını yürütmesini zaman, işlev bağımsız değişkenleri ve diğer ilgili kayıt yığın itilir biliyoruz özyinelemelidir Fazladan boşluk gereklidir, öte yandan, tekrarlanan derinlik ağacın büyüklüğü veya yüksekliği ile ilgilidir, diyelim ki, O (n) olmak daha mantıklı mıdır?

Burada herhangi bir açıklama için teşekkürler!

+0

yığın genellikle _O (log n) = alanı geçmeyecektir. – Svante

cevap

4

Algoritmanın bu yinelemeli uygulamasının ihtiyaç duyulan yığın alanı nedeniyle O (n) boşluğunu gerektirdiğini söyleyebilmenize rağmen, sadece kuyruk yinelemesini kullanır, yani O (1) boşluğunu kullanmak için yeniden kullanılabilir döngü:

int leastCommanAncestor(struct node* root, int n1, int n2) 
    while (1) 
    { 
    /* If we have reached a leaf node then LCA doesn't exist 
    If root->data is equal to any of the inputs then input is 
    not valid. For example 20, 22 in the given figure */ 
    if(root == NULL || root->data == n1 || root->data == n2) 
    return -1; 

    /* If any of the input nodes is child of the current node 
    we have reached the LCA. For example, in the above figure 
    if we want to calculate LCA of 12 and 14, recursion should 
    terminate when we reach 8*/ 
    if((root->right != NULL) && 
     (root->right->data == n1 || root->right->data == n2)) 
     return root->data; 
    if((root->left != NULL) && 
    (root->left->data == n1 || root->left->data == n2)) 
    return root->data; 

    if(root->data > n1 && root->data < n2) 
     return root->data; 
    if(root->data > n1 && root->data > n2) 
     root = root->left; 
    else if(root->data < n1 && root->data < n2) 
     root = root->right; 
    } 
} 

(bu else değişmeden anlam tutmak için ilave edilmesi gerekir.) Örnek (ağaç kabaca dengeli olduğu zaman)

+0

Derleyici bunu benim için yapar mı? Küçük bir şansın var mı? Artı, not ettiğiniz diğer şey tamam, ama başka bir şey yapmak da yanlış değil, değil mi? – Tracy

+0

En iyi bahsiniz, derleyicinizin belgelerine bakmaktır - bana göre, eğer kuyruk arama optimizasyonu varsa, gururla bundan bahsedecektir. Hızlı bir google, gcc'nin 3.4'ten beri sahip olduğunu öne sürer. Re 'else': bu gereklidir, çünkü aksi takdirde son' '' '' '' '' '' '' '' '' '' '' '' '' '' '' 'yanlış *' '(' 'root'''na bakıldığında bir çökmeye neden olan NULL olabilir. erişilir). –

10

Bu algoritma, kuyruk özlemini içerir. Sorunuzun bağlamında, arayanın yığın çerçevesi, arayan tarafından yeniden kullanılabilir. Başka bir deyişle, iç içe geçmiş tüm işlev çağrışımları dizisi, sonucu bir kova tugayı gibi geçirir. Sonuç olarak, sadece bir yığın çerçeve aslında gereklidir.

Daha fazla bilgi için bkz. Wikipedia'da Tail Call.

+0

+1, ancak çoğu C ve C++ derleyicilerinin yalnızca çok sınırlı kuyruk çağrısı optimizasyonu veya hiç yapmadığını ve bu özel durumu mutlaka optimize etmeyeceklerini unutmayın. –

+0

Büyük makale Tail Call Optimization! –

+0

bu yüzden kuyruk özleminden kaynaklanıyor, çok teşekkürler! – Tracy