Herhangi bir ikili ağaçta iki düğümün en düşük ortak ata nasıl bulunur?


187

Buradaki İkili Ağaç mutlaka bir İkili Arama Ağacı olmayabilir.
Yapı şu şekilde alınabilir -

struct node {
    int data;
    struct node *left;
    struct node *right;
};

Bir arkadaşımla çalışabileceğim maksimum çözüm bu tür bir şeydi -
Şu ikili ağacı düşünün :

İkili ağaç

Düzensiz geçiş verimleri - 8, 4, 9, 2, 5, 1, 6, 3, 7

Ve postorder çapraz verimleri - 8, 9, 4, 5, 2, 6, 7, 3, 1

Örneğin, 8 ve 5 numaralı düğümlerin ortak atalarını bulmak istiyorsak, düzensiz ağaç geçişinde 8 ile 5 arasındaki tüm düğümlerin bir listesini yaparız, bu durumda bu [4, 9 , 2]. Ardından, bu listedeki hangi düğümün sonuncu geçişte en son göründüğünü kontrol ederiz, bu 2'dir. Bu nedenle 8 ve 5 için ortak ata 2'dir.

Bu algoritmanın karmaşıklığı, inorder / postorder geçişleri için O (n) (O (n) olduğuna inanıyorum, adımların geri kalanı yine O (n), çünkü dizilerdeki basit yinelemelerden başka bir şey değildir). Ancak bunun yanlış olması için güçlü bir şans var. :-)

Ama bu çok kaba bir yaklaşım ve bir dava için bozulduğundan emin değilim. Bu soruna başka (muhtemelen daha uygun) bir çözüm var mı?


6
Meraktan, bunun pratik kullanımı nedir?
David Brunelle

19
@David: LCA sorgu yanıtlama oldukça faydalı. LCA + Sonek ağacı = dizeyle ilgili güçlü algoritmalar.

44
Benzer bir soru sorduğumda, röportaj sorusu gibi yorumlarla oylandı. SO'nun ikiliği? :(
some_other_guy

5
Soruda verilen ayrıntılar için @Siddant +1. :)
am

5
@DavidBrunelle LCA hesaplamak için pratik bir uygulama: web sayfalarını oluştururken, özellikle belirli bir DOM öğesi için geçerli olan Basamaklı Stil Sayfalarını (CSS) hesaplarken önemli bir hesaplamadır.
zc22

Yanıtlar:


74

Nick Johnson, üst işaretçileriniz yoksa bir O (n) zaman karmaşıklığı algoritmasının yapabileceğiniz en iyisi olduğu doğrudur.) Bu algoritmanın basit bir özyinelemeli sürümü için O (n) zamanında çalışan Kinding'in yazısındaki koda bakın. .

Ancak, düğümlerinizde üst işaretçiler varsa, iyileştirilmiş bir algoritmanın mümkün olduğunu unutmayın. Söz konusu her iki düğüm için de, düğümden başlayarak ve önden üst öğe ekleyerek kökten düğüme giden yolu içeren bir liste oluşturun.

Örneğinizde 8 için, (adımları gösterir) elde edersiniz: {4}, {2, 4}, {1, 2, 4}

Söz konusu diğer düğümünüz için de aynısını yapın, sonuçta (adımlar gösterilmemiştir): {1, 2}

Şimdi, listenin farklı olduğu ilk öğeyi veya listelerden birinin son öğesini (hangisi önce gelirse) aramak için yaptığınız iki listeyi karşılaştırın.

Bu algoritma, h'nin ağacın yüksekliği olduğu O (h) süresi gerektirir. En kötü durumda O (h), O (n) 'ye eşittir, ancak ağaç dengeli ise, sadece O (log (n))' dir. Ayrıca O (h) boşluğu gerektirir. CEGRD'nin gönderisinde gösterilen kodla yalnızca sabit alan kullanan geliştirilmiş bir sürüm mümkündür


Ağacın nasıl yapılandırıldığına bakılmaksızın, bu, ağaç üzerinde değiştirmeden birçok kez gerçekleştirdiğiniz bir işlem olacaksa, O (n) [doğrusal] zaman hazırlığı gerektiren ancak daha sonra herhangi bir şey bulabileceğiniz başka algoritmalar vardır. çifti sadece O (1) [sabit] zaman alır. Bu algoritmalara başvurmak için Wikipedia'daki en düşük ortak ata sorunu sorun sayfasına bakın . (Bu bağlantıyı aslen için Jason'a teşekkür ederiz)


1
Üst işaretçi verilirse bu işi yapar. Ağaçtaki düğümler soruma verdiğim yapı gibidir - sadece sol / sağ alt işaretçiler, üst işaretçi yok. Üst işaretçi yoksa ve ağaç ikili bir arama ağacı değil ve sadece ikili bir ağaçsa O (log (n)) çözümü var mı?
Siddhant

2
Ebeveyn ve belirli bir düğüm arasındaki yolu bulmanın özel bir yolu yoksa, bunu bulmak ortalama O (n) zaman alacaktır. Bu O (log (n)) zamanına sahip olmayı imkansız hale getirecektir. Ancak, O (n) bir seferlik maliyet, O (1) çift bulması, aradaki ağacı değiştirmeden birçok kez bu işlemi gerçekleştirirseniz, yine de en iyi bahsiniz olabilir. Aksi takdirde, mümkünse üst işaretçiyi eklemelisiniz. Birkaç potansiyel algoritmayı daha hızlı yapabilir, ancak mevcut herhangi bir algoritmanın sırasını değiştirmediğinden eminim. Bu yardımcı olur umarım.
Kevin Cathcart

1
bu yaklaşım O (1) bellek kullanılarak yapılabilir - stackoverflow.com/questions/1594061/…
Tom Sirgedas

@Tom: Bu, listeye dayalı algoritma için bellek karmaşıklığını O (1) ile sınırlamaya yarayacaktır. Açıkçası bu, her bir taraf için düğümlerin derinliklerini elde etmek için ağacın kendisinden bir kez yinelenmesi ve daha sonra ortak ataları bulmak için (kısmi) ikinci kez anlamına gelir. O (h) süresi ve O (1) alanı, ana işaretçiler olması ve O (n) ön hesaplaması yapılmaması durumunda açıkça en uygunudur.
Kevin Cathcart

1
@ALBI O(h)sadece O(log(n))ağaç dengeli ise. Herhangi bir ağaç için, ikili olsun ya da olmasın, üst işaretçiler varsa, bir yapraktan köke giden yolu O(h)zaman içinde belirleyebilir, sadece üst işaretçiyi hzaman zaman takip edebilirsiniz. Bu size yapraktan köke giden yolu verir. Yollar bir yığın olarak saklanıyorsa, yığının tekrarlanması size kökten yaprağa giden yolu verir. Üst işaretçileriniz yoksa ve ağaca özel bir yapınız yoksa, kökten yaprağa giden yolu bulmak O(n)zaman alır.
Kevin Cathcart

108

Düğümden başlayarak veya doğrudan alt öğesi olan rootherhangi bir düğümü bulursanız, aşağı doğru hareket ederek LCA olur. (düzenle - bu , düğümün değeri veya değeri ise olmalıdır , döndürür. Aksi takdirde, biri diğerinden veya diğerinin doğrudan alt öğesi olduğunda başarısız olur .)pqpqpq

Else p, sağ (veya sol) alt ağacında ve qsol (veya sağ) alt ağacında bir düğüm bulursanız , LCA olur.

Sabit kod şöyle görünür:

treeNodePtr findLCA(treeNodePtr root, treeNodePtr p, treeNodePtr q) {

        // no root no LCA.
        if(!root) {
                return NULL;
        }

        // if either p or q is the root then root is LCA.
        if(root==p || root==q) {
                return root;
        } else {
                // get LCA of p and q in left subtree.
                treeNodePtr l=findLCA(root->left , p , q);

                // get LCA of p and q in right subtree.
                treeNodePtr r=findLCA(root->right , p, q);

                // if one of p or q is in leftsubtree and other is in right
                // then root it the LCA.
                if(l && r) {
                        return root;
                }
                // else if l is not null, l is LCA.
                else if(l) {
                        return l;
                } else {
                        return r;
                }
        }
}

Aşağıdaki kod, diğerinin doğrudan alt öğesi olduğunda başarısız olur.

treeNodePtr findLCA(treeNodePtr root, treeNodePtr p, treeNodePtr q) {

        // no root no LCA.
        if(!root) {
                return NULL;
        }

        // if either p or q is direct child of root then root is LCA.
        if(root->left==p || root->left==q || 
           root->right ==p || root->right ==q) {
                return root;
        } else {
                // get LCA of p and q in left subtree.
                treeNodePtr l=findLCA(root->left , p , q);

                // get LCA of p and q in right subtree.
                treeNodePtr r=findLCA(root->right , p, q);

                // if one of p or q is in leftsubtree and other is in right
                // then root it the LCA.
                if(l && r) {
                        return root;
                }
                // else if l is not null, l is LCA.
                else if(l) {
                        return l;
                } else {
                        return r;
                }
        }
}

Kod Çalışıyor


2
zarif bir çözüm, ancak kök == p || root == q => dönüş kökü biti aşırı aktif görünüyor. Kökün p / q olduğu ortaya çıkarsa, ancak aranan diğer düğüm aslında ağaçta değilse?
Ian Durkan

15
Sanırım p veya q ikili ağaçta olmayan bir değer olduğunda bu kod başarısız olur. Haklı mıyım? Örneğin LCA (8,20). ur kodu 8 döndürür. ancak ikili ağaçta 20 mevcut değildir
javaMan

3
Bu çözümün maliyeti nedir? Verimli mi? Hem p hem de q bulduktan sonra bile aramaya devam ediyor gibi görünüyor. Bu, p ve q'nun bir BST olmadığı ve kopyalar içerebileceği için ağaçta benzersiz olmayabilme olasılığı nedeniyle mi?
MikeB

3
@MikeB, bu çözüm kesinlikle O (n), çünkü her düğümü en kötü durumda sadece bir kez geçersiniz. Peter Lee, bu, ana işaretçiler kullanmadan yapabileceğiniz en etkili yöntemdir. Daha iyi bir çözümün var mı?
gsingh2011

8
ilk kusurlu çözüm dikkat dağıtıcı olmayacak şekilde silinmelidir
Zinan Xing

50

İşte JAVA çalışma kodu

public static Node LCA(Node root, Node a, Node b) {
   if (root == null) {
       return null;
   }

   // If the root is one of a or b, then it is the LCA
   if (root == a || root == b) {
       return root;
   }

   Node left = LCA(root.left, a, b);
   Node right = LCA(root.right, a, b);

   // If both nodes lie in left or right then their LCA is in left or right,
   // Otherwise root is their LCA
   if (left != null && right != null) {
      return root;
   }

   return (left != null) ? left : right; 
}

4
Ağaçta bir düğüm olmadığında bu çalışmaz.
Pratik Khadloya

verilen ağaç bir BST ise kodunuzu optimize eder misiniz?
Mona Jalal

1
"Kök a veya b'den biriyse, o zaman LCA'dır." bu doğru olmayabilir. Bu noktada bildiğiniz şey, LCA'yı bulmak için çocuklarından herhangi birini kontrol etmenize gerek olmamasıdır. Bunun nedeni, daha sonra kökün üst öğesi için her iki dalda da (LCA üst öğe) veya bunlardan yalnızca bir tanesinde (bu durumda birinin LCA veya daha büyük bir ata LCA olabilir) eşleşme olup olmadığını kontrol edebilmemizdir. ).
andresp

28

Şimdiye kadar verilen cevaplar özyineleme veya depolar kullanır, örneğin bellekte bir yol.

Çok derin bir ağacınız varsa bu yaklaşımların her ikisi de başarısız olabilir.

İşte bu soruyu ele almam. Her iki düğümün derinliğini (kökten uzaklık) kontrol ettiğimizde, eğer eşitlerlerse, her iki düğümden ortak ataya doğru güvenli bir şekilde yukarı doğru hareket edebiliriz. Derinliklerden biri daha büyükse, diğerinde kalırken daha derin düğümden yukarı doğru hareket etmeliyiz.

İşte kod:

findLowestCommonAncestor(v,w):
  depth_vv = depth(v);
  depth_ww = depth(w);

  vv = v; 
  ww = w;

  while( depth_vv != depth_ww ) {
    if ( depth_vv > depth_ww ) {
      vv = parent(vv);
      depth_vv--;
    else {
      ww = parent(ww);
      depth_ww--;
    }
  }

  while( vv != ww ) {
    vv = parent(vv);
    ww = parent(ww);
  }

  return vv;    

Bu algoritmanın zaman karmaşıklığı: O (n). Bu algoritmanın uzay karmaşıklığı: O (1).

Derinliğin hesaplanması ile ilgili olarak, önce tanımı hatırlayabiliriz: v kök ise, derinlik (v) = 0; Aksi takdirde, derinlik (v) = derinlik (ebeveyn (v)) + 1. Derinliği aşağıdaki gibi hesaplayabiliriz:

depth(v):
  int d = 0;
  vv = v;
  while ( vv is not root ) {
    vv = parent(vv);
    d++;
  }
  return d;

6
İkili ağaçların genellikle ana öğeye referansları yoktur. Bir ebeveyn referans eklemek herhangi bir sorun olmadan yapılabilir, ancak O (n) yardımcı alan olduğunu düşünürdüm.
John Kurlak

Bu çözümde ince bir varsayım var. Bir düğüm diğerinin doğrudan veya dolaylı bir üst öğesiyse (yani, daha derin düğüm sığ düğümden köklenen bir ağaçtaysa), bu çözüm sonuç olarak sığ düğümün üst öğesini döndürür. En düşük ortak atayı nasıl tanımladığınıza bağlı olarak, bu istediğiniz şey olmayabilir. Bazı tanımlar sığ düğümün kendisinin üst öğe olmasını gerektirecektir. Bu durumda, daha sığ düğüm olanı izlemeniz ve bunu geri döndürmeniz gerekir.
Srikanth

8

Bu tür İkili Ağacınızın nasıl yapılandırıldığına bağlıdır. Muhtemelen ağacın kökü verilen istenen yaprak düğümünü bulmanın bir yoluna sahipsiniz - seçtiğiniz dallar ayrılana kadar bunu her iki değere de uygulayın.

Kök verilen istenen yaprağı bulmanın bir yolu yoksa, hem normal operasyonda hem de son ortak düğümü bulmak için tek çözümünüz ağacın kaba kuvvetli bir araştırmasıdır.


8

Bunu şu adreste bulabilirsiniz: - http://goursaha.freeoda.com/DataStructure/LowestCommonAncestor.html

 tree_node_type *LowestCommonAncestor(
 tree_node_type *root , tree_node_type *p , tree_node_type *q)
 {
     tree_node_type *l , *r , *temp;
     if(root==NULL)
     {
        return NULL;
     }

    if(root->left==p || root->left==q || root->right ==p || root->right ==q)
    {
        return root;
    }
    else
    {
        l=LowestCommonAncestor(root->left , p , q);
        r=LowestCommonAncestor(root->right , p, q);

        if(l!=NULL && r!=NULL)
        {
            return root;
        }
        else
        {
        temp = (l!=NULL)?l:r;
        return temp;
        }
    }
}

p mevcutsa ancak q ağaçta hiç değilse kodunuzun nasıl davranacağını söyler misiniz? Benzer şekilde hem p hem de q mevcut değildir. Teşekkürler!!!
çalışılıyor

Zaman açısından büyük O nedir? Sanırım O (n * log (n)), iki yavaş.
Peter Lee


6

İki düğümün ortak atalarını bulmak için: -

  • İkili aramayı kullanarak ağaçta verilen Düğüm1'i bulun ve bu işlemde ziyaret edilen tüm düğümleri A1 diyelim bir diziye kaydedin. Zaman - O (kütük), Uzay - O (kütük)
  • İkili aramayı kullanarak verilen Düğüm2'yi ağaçta bulun ve bu işlemde ziyaret edilen tüm düğümleri A2 diyelim bir diziye kaydedin. Zaman - O (kütük), Uzay - O (kütük)
  • A1 listesi veya A2 listesi boşsa, bir düğüm yoktur, bu nedenle ortak bir ata yoktur.
  • A1 listesi ve A2 listesi boş değilse, eşleşmeyen düğümü bulana kadar listeye bakın. Böyle bir düğümü bulduğunuzda, bundan önceki düğüm ortak atadır.

Bu ikili arama ağacı için işe yarar.


2
Ağacın mutlaka bir BST OLMADIĞINI açıkça belirtti.
Peter Lee

@Peter Lee - Yukarıdaki mantık, basit bir değişiklikle herhangi bir ikili ağaç için bile işe yarar. Belirli düğümlerin ikili araması yerine, doğrusal aramayı uygulayın (yani herhangi bir çapraz geçiş ancak her iki durum için de aynı olmalıdır). Elbette çalışma zamanı O (logn) yerine O (n) olur. Aslında bu algo, ana işaretçi mevcut olmadığında en sağlam olanıdır. Birçokları tarafından verilen rucursif algoritma (viz. 'Codaddict') verilen düğümlerden biri ağaca ait olmadığında çalışmayacaktır)
KGhatak


3

Aşağıdaki özyinelemeli algoritma dengeli bir ikili ağaç için O (log N) içinde çalışacaktır. GetLCA () işlevine geçirilen düğümlerden herhangi biri kökle aynıysa, kök LCA olur ve herhangi bir yeniden yönlendirme gerçekleştirmeye gerek kalmaz.

Test senaryoları. [1] Her iki n1 ve n2 düğümü ağaçtadır ve ana düğümlerinin her iki tarafında bulunur. [2] Ya n1 ya da n2 düğümü köktür, LCA köküdür. [3] Ağaçta sadece n1 veya n2 bulunur, LCA ağaç kökünün sol alt ağacının kök düğümü veya LCA ağaç kökünün sağ alt ağacının kök düğümü olacaktır.

[4] Ne n1 ne de n2 ağaçta değildir, LCA yoktur. [5] Hem n1 hem de n2 yan yana düz bir çizgidedir, LCA ya ağacın köküne yakın olan n1 ya da n2 olacaktır.

//find the search node below root
bool findNode(node* root, node* search)
{
    //base case
    if(root == NULL)
        return false;

    if(root->val == search->val)
        return true;

    //search for the node in the left and right subtrees, if found in either return true
    return (findNode(root->left, search) || findNode(root->right, search));
}

//returns the LCA, n1 & n2 are the 2 nodes for which we are
//establishing the LCA for
node* getLCA(node* root, node* n1, node* n2)
{
    //base case
    if(root == NULL)
        return NULL;

    //If 1 of the nodes is the root then the root is the LCA
    //no need to recurse.
    if(n1 == root || n2 == root)
        return root;

    //check on which side of the root n1 and n2 reside
    bool n1OnLeft = findNode(root->left, n1);
    bool n2OnLeft = findNode(root->left, n2);

    //n1 & n2 are on different sides of the root, so root is the LCA
    if(n1OnLeft != n2OnLeft)
        return root;

    //if both n1 & n2 are on the left of the root traverse left sub tree only
    //to find the node where n1 & n2 diverge otherwise traverse right subtree
    if(n1OnLeft)
        return getLCA(root->left, n1, n2);
    else
        return getLCA(root->right, n1, n2);
}

3

Sadece bütün ağaç en aşağı yürümek root, hem verilen düğümler sürece söylemek pve qaynı alt ağacında (değerleri hem daha küçük veya kök en fazla ikisi büyük olan anlamında) olan Atamız bulunacak sahip olduğu,.

Bu kökten en küçük Ortak Ataya doğru yürür, ağacın geri kalanına bakmaz, bu yüzden aldığı kadar hızlıdır. Bunu yapmanın birkaç yolu.

Yinelemeli, O (1) boşluk

piton

def lowestCommonAncestor(self, root, p, q):
    while (root.val - p.val) * (root.val - q.val) > 0:
        root = (root.left, root.right)[p.val > root.val]
    return root

Java

public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
    while ((root.val - p.val) * (root.val - q.val) > 0)
        root = p.val < root.val ? root.left : root.right;
    return root;
}

taşma durumunda yapardım (root.val - (uzun) p.val) * (root.val - (uzun) q.val)

Recursive

piton

def lowestCommonAncestor(self, root, p, q):
    next = p.val < root.val > q.val and root.left or \
           p.val > root.val < q.val and root.right
    return self.lowestCommonAncestor(next, p, q) if next else root

Java

public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
    return (root.val - p.val) * (root.val - q.val) < 1 ? root :
           lowestCommonAncestor(p.val < root.val ? root.left : root.right, p, q);
}

2
Node *LCA(Node *root, Node *p, Node *q) {
  if (!root) return NULL;
  if (root == p || root == q) return root;
  Node *L = LCA(root->left, p, q);
  Node *R = LCA(root->right, p, q);
  if (L && R) return root;  // if p and q are on both sides
  return L ? L : R;  // either one of p,q is on one side OR p,q is not in L&R subtrees
}

2

Bu ağacı düşünün resim açıklamasını buraya girin

Eğer postorder ve ön geçişi yaparsak ve ilk ortaya çıkan ortak selefi ve halefi bulursak, ortak ataları alırız.

postacı => 0,2,1,5,4,6,3,8,10,11,9,14,15,13,12,7 ön sipariş => 7,3,1,0,2,6,4 , 5,12,9,8,11,10,13,15,14

  • örneğin: 1

8,11'in en az ortak atası

postorder'da ön siparişte 8 & 11'den sonra => 9,14,15,13,12,7, 8 & 11'den önce => 7,3,1,0,2,6,4,5,12,9'a sahibiz

9, postorder'da 8 ve 11'den sonra ve ön siparişte 8 ve 11'den önce ortaya çıkan ilk yaygın sayıdır, dolayısıyla 9 cevaptır

  • örneğin: 2

5,10 en az ortak ata

11.9,14,15,13,12,7 postacı 7,3,1,0,2,6,4 ön siparişte

7, postorder'da 5,10'dan ve ön siparişte 5,10'dan önce ortaya çıkan ilk sayıdır, dolayısıyla 7 cevaptır


2

X düğümü çocuklarına sahip tam ikili ağaç ise 2 * x ve 2 * x + 1 olarak yapmanın daha hızlı bir yolu vardır

int get_bits(unsigned int x) {
  int high = 31;
  int low = 0,mid;
  while(high>=low) {
    mid = (high+low)/2;
    if(1<<mid==x)
      return mid+1;
    if(1<<mid<x) {
      low = mid+1;
    }
    else {
      high = mid-1;
    }
  }
  if(1<<mid>x)
    return mid;
  return mid+1;
}

unsigned int Common_Ancestor(unsigned int x,unsigned int y) {

  int xbits = get_bits(x);
  int ybits = get_bits(y);
  int diff,kbits;
  unsigned int k;
  if(xbits>ybits) {
    diff = xbits-ybits;
    x = x >> diff;
  }
  else if(xbits<ybits) {
    diff = ybits-xbits;
    y = y >> diff;
  }
  k = x^y;
  kbits = get_bits(k);
  return y>>kbits;  
}

O nasıl çalışır

  1. ikili aramayı kullanan O (log (32)) x & y'yi temsil etmek için gereken bitleri almak
  2. x & y'nin ikili gösteriminin ortak öneki ortak atadır
  3. hangisi daha büyük bir bit ile temsil edilirse k >> diff ile aynı bite getirilir
  4. k = x ^ y x & y'nin ortak önekini siler
  5. kalan soneki temsil eden bitleri bul
  6. ortak ata olan ortak öneki almak için x veya y sonek bitleri ile kaydırın.

Bu işe yarar çünkü temel olarak her iki sayı eşit olana kadar daha büyük sayıyı yinelemeli olarak ikiye böl. Bu sayı ortak atadır. Bölme, etkin bir şekilde sağa kayma eylemidir. Bu yüzden en yakın atayı bulmak için iki sayının ortak önekini bulmamız gerekiyor


2

Scala'da şunları yapabilirsiniz:

  abstract class Tree
  case class Node(a:Int, left:Tree, right:Tree) extends Tree
  case class Leaf(a:Int) extends Tree

  def lca(tree:Tree, a:Int, b:Int):Tree = {
    tree match {
      case Node(ab,l,r) => {
        if(ab==a || ab ==b) tree else {
          val temp = lca(l,a,b);
          val temp2 = lca(r,a,b);
          if(temp!=null && temp2 !=null)
            tree
          else if (temp==null && temp2==null)
            null
          else if (temp==null) r else l
        }

      }
      case Leaf(ab) => if(ab==a || ab ==b) tree else null
    }
  }

1
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if(root==null || root == p || root == q){
            return root;
        }
        TreeNode left = lowestCommonAncestor(root.left,p,q);
        TreeNode right = lowestCommonAncestor(root.right,p,q);
        return left == null ? right : right == null ? left : root;
    }

0

İşte bunu yapmanın C ++ yolu. Algoritmayı anlamak için mümkün olduğunca kolay tutmaya çalıştım:

// Assuming that `BinaryNode_t` has `getData()`, `getLeft()` and `getRight()`
class LowestCommonAncestor
{
  typedef char type;    
  // Data members which would behave as place holders
  const BinaryNode_t* m_pLCA;
  type m_Node1, m_Node2;

  static const unsigned int TOTAL_NODES = 2;

  // The core function which actually finds the LCA; It returns the number of nodes found
  // At any point of time if the number of nodes found are 2, then it updates the `m_pLCA` and once updated, we have found it!
  unsigned int Search (const BinaryNode_t* const pNode)
  {
    if(pNode == 0)
      return 0;

    unsigned int found = 0;

    found += (pNode->getData() == m_Node1);
    found += (pNode->getData() == m_Node2);

    found += Search(pNode->getLeft()); // below condition can be after this as well
    found += Search(pNode->getRight());

    if(found == TOTAL_NODES && m_pLCA == 0)
      m_pLCA = pNode;  // found !

    return found;
  }

public:
  // Interface method which will be called externally by the client
  const BinaryNode_t* Search (const BinaryNode_t* const pHead,
                              const type node1,
                              const type node2)
  {
    // Initialize the data members of the class
    m_Node1 = node1;
    m_Node2 = node2;
    m_pLCA = 0;

    // Find the LCA, populate to `m_pLCANode` and return
    (void) Search(pHead);
    return m_pLCA;
  }
};

Bu nasıl kullanılır:

LowestCommonAncestor lca;
BinaryNode_t* pNode = lca.Search(pWhateverBinaryTreeNodeToBeginWith);
if(pNode != 0)
  ...

0

En Düşük Ortak Ata'yı bulmanın en kolay yolu aşağıdaki algoritmayı kullanmaktır:

Kök düğümünü inceleyin

değer1 ve değer2, kök düğümdeki değerden kesinlikle daha azsa 
    Sol alt ağacı inceleyin
değer1 ve değer2 kök düğümdeki değerden kesinlikle büyükse 
    Sağ alt ağacı inceleyin
Başka
    dönüş kökü
public int LCA(TreeNode root, int value 1, int value 2) {
    while (root != null) {
       if (value1 < root.data && value2 < root.data)
           return LCA(root.left, value1, value2);
       else if (value2 > root.data && value2 2 root.data)
           return LCA(root.right, value1, value2);
       else
           return root
    }

    return null;
} 

6
BST DEĞİL!
Peter Lee

0

Bir çözüm buldum

  1. Sırasını al
  2. Ön siparişi al
  3. Postorder al

3 geçişe bağlı olarak, LCA'nın kim olduğuna karar verebilirsiniz. LCA itibaren her iki düğümün mesafesini bulun. Cevap bu iki mesafeyi ekleyin.


0

İşte düşündüğüm şey,

  1. Yumruk düğümünün rotasını bulun, arr1'e kaydedin.
  2. 2 düğüm için rotayı bulmaya başlayın, bunu yaparken kökten arr1'e kadar her değeri kontrol edin.
  3. değerin farklı olduğu zaman, çıkış. Eski eşleşen değer LCA'dır.

Karmaşıklık: adım 1: O (n), adım 2 =% 0 (n), toplam =% 0 (n).


0

İşte referans için c # (.net) (her ikisi de yukarıda tartışılmıştır) iki yaklaşım:

  1. İkili ağaçta LCA bulmanın özyinelemeli versiyonu (O (N) - en çok her düğüm ziyaret edildiğinde) (çözümün ana noktaları LCA'dır (a) her iki öğenin de alt ağaçların her iki tarafında bulunduğu ikili ağaçtaki düğümdür (sol . ve sağ) LCA olan b () ve ayrıca düğüm mevcut yan ya hangi önemli değil - başlangıçta ben bu bilgi tutmaya çalıştı ve açıkçası özyinelemeli fonksiyon kafa karıştırıcı gelirginleşmesi ben bunu anlayınca, çok şık oldu..

  2. Her iki düğümü (O (N)) aramak ve yolları takip etmek (ekstra alan kullanır - bu nedenle, ikili ağaç iyi dengelenmişse alanın muhtemelen ihmal edilebilir olduğu düşünülürse bile, ekstra bellek tüketimi sadece O (log (N)).

    böylece yollar karşılaştırılır (esas olarak kabul edilen cevaba benzer - ancak yollar ikili ağaç düğümünde işaretçi düğümün mevcut olmadığı varsayılarak hesaplanır)

  3. Sadece tamamlanması için ( soru ile ilgili değil ), BST'de LCA (O (log (N))

  4. Testler

özyineli:

private BinaryTreeNode LeastCommonAncestorUsingRecursion(BinaryTreeNode treeNode, 
            int e1, int e2)
        {
            Debug.Assert(e1 != e2);
            
            if(treeNode == null)
            {
                return null;
            }
            if((treeNode.Element == e1)
                || (treeNode.Element == e2))
            {
                //we don't care which element is present (e1 or e2), we just need to check 
                //if one of them is there
                return treeNode;
            }
            var nLeft = this.LeastCommonAncestorUsingRecursion(treeNode.Left, e1, e2);
            var nRight = this.LeastCommonAncestorUsingRecursion(treeNode.Right, e1, e2);
            if(nLeft != null && nRight != null)
            {
                //note that this condition will be true only at least common ancestor
                return treeNode;
            }
            else if(nLeft != null)
            {
                return nLeft;
            }
            else if(nRight != null)
            {
                return nRight;
            }
            return null;
        }

Yukarıdaki özel özyinelemeli sürüm aşağıdaki genel yöntemle çağrılır:

public BinaryTreeNode LeastCommonAncestorUsingRecursion(int e1, int e2)
        {
            var n = this.FindNode(this._root, e1);
            if(null == n)
            {
                throw new Exception("Element not found: " + e1);
            }
            if (e1 == e2)
            {   
                return n;
            }
            n = this.FindNode(this._root, e2);
            if (null == n)
            {
                throw new Exception("Element not found: " + e2);
            }
            var node = this.LeastCommonAncestorUsingRecursion(this._root, e1, e2);
            if (null == node)
            {
                throw new Exception(string.Format("Least common ancenstor not found for the given elements: {0},{1}", e1, e2));
            }
            return node;
        }

Her iki düğümün yollarını takip ederek çözüm:

public BinaryTreeNode LeastCommonAncestorUsingPaths(int e1, int e2)
        {
            var path1 = new List<BinaryTreeNode>();
            var node1 = this.FindNodeAndPath(this._root, e1, path1);
            if(node1 == null)
            {
                throw new Exception(string.Format("Element {0} is not found", e1));
            }
            if(e1 == e2)
            {
                return node1;
            }
            List<BinaryTreeNode> path2 = new List<BinaryTreeNode>();
            var node2 = this.FindNodeAndPath(this._root, e2, path2);
            if (node1 == null)
            {
                throw new Exception(string.Format("Element {0} is not found", e2));
            }
            BinaryTreeNode lca = null;
            Debug.Assert(path1[0] == this._root);
            Debug.Assert(path2[0] == this._root);
            int i = 0;
            while((i < path1.Count)
                && (i < path2.Count)
                && (path2[i] == path1[i]))
            {
                lca = path1[i];
                i++;
            }
            Debug.Assert(null != lca);
            return lca;
        }

burada FindNodeAndPath şu şekilde tanımlanır:

private BinaryTreeNode FindNodeAndPath(BinaryTreeNode node, int e, List<BinaryTreeNode> path)
        {
            if(node == null)
            {
                return null;
            }
            if(node.Element == e)
            {
                path.Add(node);
                return node;
            }
            var n = this.FindNodeAndPath(node.Left, e, path);
            if(n == null)
            {
                n = this.FindNodeAndPath(node.Right, e, path);
            }
            if(n != null)
            {
                path.Insert(0, node);
                return n;
            }
            return null;
        }

BST (LCA) - ilgili değil (yalnızca başvuru için tamamlanması için)

public BinaryTreeNode BstLeastCommonAncestor(int e1, int e2)
        {
            //ensure both elements are there in the bst
            var n1 = this.BstFind(e1, throwIfNotFound: true);
            if(e1 == e2)
            {
                return n1;
            }
            this.BstFind(e2, throwIfNotFound: true);
            BinaryTreeNode leastCommonAcncestor = this._root;
            var iterativeNode = this._root;
            while(iterativeNode != null)
            {
                if((iterativeNode.Element > e1 ) && (iterativeNode.Element > e2))
                {
                    iterativeNode = iterativeNode.Left;
                }
                else if((iterativeNode.Element < e1) && (iterativeNode.Element < e2))
                {
                    iterativeNode = iterativeNode.Right;
                }
                else
                {
                    //i.e; either iterative node is equal to e1 or e2 or in between e1 and e2
                    return iterativeNode;
                }
            }
            //control will never come here
            return leastCommonAcncestor;
        }

Birim Testleri

[TestMethod]
        public void LeastCommonAncestorTests()
        {
            int[] a = { 13, 2, 18, 1, 5, 17, 20, 3, 6, 16, 21, 4, 14, 15, 25, 22, 24 };
            int[] b = { 13, 13, 13, 2, 13, 18, 13, 5, 13, 18, 13, 13, 14, 18, 25, 22};
            BinarySearchTree bst = new BinarySearchTree();
            foreach (int e in a)
            {
                bst.Add(e);
                bst.Delete(e);
                bst.Add(e);
            }
            for(int i = 0; i < b.Length; i++)
            {
                var n = bst.BstLeastCommonAncestor(a[i], a[i + 1]);
                Assert.IsTrue(n.Element == b[i]);
                var n1 = bst.LeastCommonAncestorUsingPaths(a[i], a[i + 1]);
                Assert.IsTrue(n1.Element == b[i]);
                Assert.IsTrue(n == n1);
                var n2 = bst.LeastCommonAncestorUsingRecursion(a[i], a[i + 1]);
                Assert.IsTrue(n2.Element == b[i]);
                Assert.IsTrue(n2 == n1);
                Assert.IsTrue(n2 == n);
            }
        }

0

Sözde kodla ilgilenen biri (üniversite ev işleri için) burada bir tanesidir.

GETLCA(BINARYTREE BT, NODE A, NODE  B)
IF Root==NIL
    return NIL
ENDIF

IF Root==A OR root==B
    return Root
ENDIF

Left = GETLCA (Root.Left, A, B)
Right = GETLCA (Root.Right, A, B)

IF Left! = NIL AND Right! = NIL
    return root
ELSEIF Left! = NIL
    Return Left
ELSE
    Return Right
ENDIF

0

Bu daha önce cevaplanmış olmasına rağmen, C programlama dili kullanarak bu soruna yaklaşımım budur. Kod bir ikili arama ağacı göstermesine rağmen (insert () söz konusu olduğunda), ancak algoritma bir ikili ağaç için de çalışır. Fikir, düzensiz geçişte A düğümünden B düğümüne uzanan tüm düğümleri gözden geçirmek, sipariş sırası geçişinde bunlara ilişkin indeksleri aramaktır. Post order traversalde maksimum indeksi olan düğüm, en düşük ortak atadır.

Bu, ikili bir ağaçtaki en düşük ortak atayı bulmak için bir işlev uygulamak için çalışan bir C kodudur. Ben de tüm yardımcı fonksiyonlar vb sağlamak, ama hızlı anlamak için CommonAncestor () atlamak.

#include <stdio.h>
#include <malloc.h>
#include <stdlib.h>
#include <math.h>

static inline int min (int a, int b)
{
    return ((a < b) ? a : b);
}
static inline int max (int a, int b)
{
    return ((a > b) ? a : b);
}

typedef struct node_ {
    int value;
    struct node_ * left;
    struct node_ * right;
} node;

#define MAX 12

int IN_ORDER[MAX] = {0};
int POST_ORDER[MAX] = {0};

createNode(int value) 
{
    node * temp_node = (node *)malloc(sizeof(node));
    temp_node->left = temp_node->right = NULL;
    temp_node->value = value;
    return temp_node;
}

node *
insert(node * root, int value)
{
    if (!root) {
        return createNode(value);
    }

    if (root->value > value) {
        root->left = insert(root->left, value);
    } else {
        root->right = insert(root->right, value);
    }

    return root;
}


/* Builds inorder traversal path in the IN array */
void
inorder(node * root, int * IN)
{
    static int i = 0;

    if (!root) return;

    inorder(root->left, IN);
    IN[i] = root->value;
    i++;
    inorder(root->right, IN);
}

/* Builds post traversal path in the POST array */

void
postorder (node * root, int * POST)
{
    static int i = 0;

    if (!root) return;

    postorder(root->left, POST);
    postorder(root->right, POST);
    POST[i] = root->value;
    i++;
}


int
findIndex(int * A, int value)
{
    int i = 0;
    for(i = 0; i< MAX; i++) {
        if(A[i] == value) return i;
    }
}
int
CommonAncestor(int val1, int val2)
{
    int in_val1, in_val2;
    int post_val1, post_val2;
    int j=0, i = 0; int max_index = -1;

    in_val1 = findIndex(IN_ORDER, val1);
    in_val2 = findIndex(IN_ORDER, val2);
    post_val1 = findIndex(POST_ORDER, val1);
    post_val2 = findIndex(POST_ORDER, val2);

    for (i = min(in_val1, in_val2); i<= max(in_val1, in_val2); i++) {
        for(j = 0; j < MAX; j++) {
            if (IN_ORDER[i] == POST_ORDER[j]) {
                if (j > max_index) {
                    max_index = j;
                }
            }
        }
    }
    printf("\ncommon ancestor of %d and %d is %d\n", val1, val2, POST_ORDER[max_index]);
    return max_index;
}
int main()
{
    node * root = NULL; 

    /* Build a tree with following values */
    //40, 20, 10, 30, 5, 15, 25, 35, 1, 80, 60, 100
    root = insert(root, 40);
    insert(root, 20);
    insert(root, 10);
    insert(root, 30);
    insert(root, 5);
    insert(root, 15);
    insert(root, 25);
    insert(root, 35);
    insert(root, 1);
    insert(root, 80);
    insert(root, 60);
    insert(root, 100);

    /* Get IN_ORDER traversal in the array */
    inorder(root, IN_ORDER);

    /* Get post order traversal in the array */
    postorder(root, POST_ORDER);

    CommonAncestor(1, 100);


}

0

Bir yaklaşım daha olabilir. Ancak cevaplarda önerilenden daha verimli değildir.

  • N1 düğümü için bir yol vektörü oluşturun.

  • N2 düğümü için ikinci bir yol vektörü oluşturun.

  • Bu ayarlanmış düğümleri ima eden yol vektörü, söz konusu düğüme ulaşmak için geçecektir.

  • Her iki yol vektörünü karşılaştırın. Uyuşmadıkları dizin, bu dizindeki düğümü döndürür - 1. Bu, LCA'yı verir.

Bu yaklaşımın eksileri:

Yol vektörlerini hesaplamak için ağacı iki kez hareket ettirmeniz gerekir. Yol vektörlerini saklamak için ek O (h) boşluğuna ihtiyaç vardır.

Ancak bunun uygulanması ve anlaşılması da kolaydır.

Yol vektörünü hesaplama kodu:

private boolean findPathVector (TreeNode treeNode, int key, int pathVector[], int index) {

        if (treeNode == null) {
            return false;
        }

        pathVector [index++] = treeNode.getKey ();

        if (treeNode.getKey () == key) {
            return true;
        }
        if (findPathVector (treeNode.getLeftChild (), key, pathVector, index) || 
            findPathVector (treeNode.getRightChild(), key, pathVector, index)) {

            return true;        
        }

        pathVector [--index] = 0;
        return false;       
    }

0

Böyle dene

node * lca(node * root, int v1,int v2)
{

if(!root) {
            return NULL;
    }
    if(root->data == v1 || root->data == v2) {
        return root;}
    else
    {
        if((v1 > root->data && v2 < root->data) || (v1 < root->data && v2 > root->data))
        {
            return root;
        }

        if(v1 < root->data && v2 < root->data)
        {
            root = lca(root->left, v1, v2);
        }

        if(v1 > root->data && v2 > root->data)
        {
            root = lca(root->right, v1, v2);
        }
    }
return root;
}

0

Ham yol:

  • Her düğümde
    • X = Düğümün sol tarafında n1, n2'den birinin olup olmadığını bulun
    • Y = Düğümün sağ tarafında n1, n2 öğelerinden birinin olup olmadığını bulun
      • düğümün kendisi n1 ise || n2, genelleme amacıyla solda veya sağda bulunabilir.
    • Hem X hem de Y doğruysa, Düğüm CA'dır

Yukarıdaki yöntemle ilgili sorun, "find" ı birden çok kez yapacağımızdır, yani her bir düğümün birden çok kez geçilmesi olasılığı vardır. Eğer bilgiyi tekrar işlemeyecek şekilde kaydedebilirsek, bu problemin üstesinden gelebiliriz (dinamik programlamayı düşünün).

Bu yüzden her düğümü bulmak yerine, daha önce bulunmuş olan şeylerin kaydını tutarız.

Daha iyi yol:

  • Belirli bir düğüm için left_set (sol alt ağaçta n1 | n2 bulundu) veya sağ_set'in derinlik ilkesi olup olmadığını kontrol ederiz. (NOT: Kökün kendisine n1 | n2 ise left_set olma özelliğini veriyoruz)
  • Hem left_set hem de right_set ise, düğüm bir LCA olur.

Kod:

struct Node *
findCA(struct Node *root, struct Node *n1, struct Node *n2, int *set) {
   int left_set, right_set;
   left_set = right_set = 0;
   struct Node *leftCA, *rightCA;
   leftCA = rightCA = NULL;

   if (root == NULL) {
      return NULL;
   }
   if (root == n1 || root == n2) {
      left_set = 1;
      if (n1 == n2) {
         right_set = 1;
      }
   }

   if(!left_set) {
      leftCA = findCA(root->left, n1, n2, &left_set);
      if (leftCA) {
         return leftCA;
      }
   }
   if (!right_set) {
      rightCA= findCA(root->right, n1, n2, &right_set);
      if(rightCA) {
         return rightCA;
      }
   }

   if (left_set && right_set) {
      return root;
   } else {
      *set = (left_set || right_set);
      return NULL;
   }
}

0

Her iki düğümün de ağaçta olduğundan emin olmak için Önce Genişlik Araması'nı kodlayın. Ancak bundan sonra LCA aramasıyla ilerleyin. Geliştirmek için herhangi bir öneriniz varsa lütfen yorum yapın. Muhtemelen onları ziyaret etti olarak işaretleyebilir ve ikinci düğümü iyileştirmek için bıraktığımız belirli bir noktada aramayı yeniden başlatabiliriz (ZİYARETLİ değilse)

public class searchTree {
    static boolean v1=false,v2=false;
    public static boolean bfs(Treenode root, int value){
         if(root==null){
           return false;
     }
    Queue<Treenode> q1 = new LinkedList<Treenode>();

    q1.add(root);
    while(!q1.isEmpty())
    {
        Treenode temp = q1.peek();

        if(temp!=null) {
            q1.remove();
            if (temp.value == value) return true;
            if (temp.left != null) q1.add(temp.left);
            if (temp.right != null) q1.add(temp.right);
        }
    }
    return false;

}
public static Treenode lcaHelper(Treenode head, int x,int y){

    if(head==null){
        return null;
    }

    if(head.value == x || head.value ==y){
        if (head.value == y){
            v2 = true;
            return head;
        }
        else {
            v1 = true;
            return head;
        }
    }

    Treenode left = lcaHelper(head.left, x, y);
    Treenode right = lcaHelper(head.right,x,y);

    if(left!=null && right!=null){
        return head;
    }
    return (left!=null) ? left:right;
}

public static int lca(Treenode head, int h1, int h2) {
    v1 = bfs(head,h1);
    v2 = bfs(head,h2);
    if(v1 && v2){
        Treenode lca = lcaHelper(head,h1,h2);
        return lca.value;
    }
    return -1;
}
}

0

Bir üst düğüm olmadan, çapraz geçişli çözümün size O (n) zaman karmaşıklığı kazandıracağından emin olabilirsiniz.

Geçiş yaklaşımı Diyelim ki A ve B düğümü için LCA bulduğunuzda, en basit yaklaşım önce kökten A'ya yolu almak ve sonra kökten B'ye yolu elde etmektir. ve A ve B'nin en düşük ortak atası olan son ortak düğümü bulun.

Özyinelemeli çözüm Bir başka yaklaşım özyinelemeyi kullanmaktır. İlk olarak, hem sol hem de sağ ağaçtan (varsa) LCA alabiliriz. A veya B'den biri kök düğüm ise, kök LCA'dır ve özyinelemenin bitiş noktası olan kökü döndürürüz. Ağacı alt ağaçlara ayırmaya devam ederken, sonunda A ve B'ye çarpacağız.

Alt problem çözümlerini birleştirmek için, LCA (sol ağaç) bir düğüm döndürürse, hem A hem de B'nin sol ağaçta olduğunu ve döndürülen düğümün nihai sonuç olduğunu biliyoruz. Hem LCA (sol) hem de LCA (sağ) boş olmayan düğümleri döndürürse, A ve B'nin sırasıyla sol ve sağ ağaçta olduğu anlamına gelir. Bu durumda, kök düğüm en düşük ortak düğümdür.

Ayrıntılı analiz ve çözüm için En Düşük Ortak Ata'yı kontrol edin .


0

Buradaki çözümlerden bazıları kök düğüme referans olduğunu varsayarken, bazıları ağacın bir BST olduğunu varsayar. rootDüğüm ve ağaca referans vermeden hashmap kullanarak çözümümü paylaşmak BST veya BST olmayan olabilir:

    var leftParent : Node? = left
    var rightParent : Node? = right
    var map = [data : Node?]()

    while leftParent != nil {
        map[(leftParent?.data)!] = leftParent
        leftParent = leftParent?.parent
    }

    while rightParent != nil {
        if let common = map[(rightParent?.data)!] {
            return common
        }
        rightParent = rightParent?.parent
    }

0

Çözüm 1: Yinelemeli - Daha Hızlı

  • Fikir, kökten başlayarak ağacı hareket ettirmektir. Verilen p ve q anahtarlarından herhangi biri root ile eşleşiyorsa, her iki anahtarın da var olduğunu varsayarak kök LCA olur. Kök anahtarların hiçbiriyle eşleşmezse, sol ve sağ alt ağaç için geri çekilir.
  • Sol alt ağacında bir anahtar ve sağ alt ağacında diğer anahtar bulunan düğüm LCA'dır. Her iki anahtar da sol alt ağaçta bulunuyorsa, sol alt ağaç da LCA'ya sahiptir, aksi takdirde LCA sağ alt ağaçta yer alır.
  • Zaman Karmaşıklığı: O (n)
  • Alan Karmaşıklığı: O (h) - özyinelemeli çağrı yığını için
class Solution 
{
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q)
    {
        if(root == null || root == p  || root == q)
            return root;

        TreeNode left = lowestCommonAncestor(root.left, p, q);
        TreeNode right = lowestCommonAncestor(root.right, p, q);

        if(left == null)
            return right;
        else if(right == null)
            return left;
        else
            return root;    // If(left != null && right != null)
    }
}

Çözüm 2: Yinelemeli - Üst işaretçileri kullanma - Daha yavaş

  • Boş bir karma tablo oluşturun.
  • P ve tüm atalarını karma tablosuna ekleyin.
  • Karma tablosunda q'nun veya atalarından herhangi birinin olup olmadığını kontrol edin, evetse ilk varolan atası döndürün.
  • Zaman Karmaşıklığı: O (n) - En kötü durumda, ikili ağacın tüm düğümlerini ziyaret ediyor olabiliriz.
  • Alan Karmaşıklığı: O (n) - Hash-table, ancestor_set ve kuyruk gibi ana göstergenin kullanıldığı alanın her biri O (n) olur.
class Solution
{
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q)
    {
        HashMap<TreeNode, TreeNode> parent_map = new HashMap<>();
        HashSet<TreeNode> ancestors_set = new HashSet<>();
        Queue<TreeNode> queue = new LinkedList<>();

        parent_map.put(root, null);
        queue.add(root);

        while(!parent_map.containsKey(p) || !parent_map.containsKey(q))
        {
            TreeNode node = queue.poll();

            if(node.left != null)
            {
                parent_map.put(node.left, node);
                queue.add(node.left);
            }
            if(node.right != null)
            {
                parent_map.put(node.right, node);
                queue.add(node.right);
            }
        }

        while(p != null)
        {
            ancestors_set.add(p);
            p = parent_map.get(p);
        }

        while(!ancestors_set.contains(q))
            q = parent_map.get(q);

        return q;
    }
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.