bir ikili ağaçta ortak atası bulma

oy
7

Ben bir ikili ağaç var ve o ağacın iki rasgele düğümler verilen ortak bir atası (üst) bulmak zorunda: Bu soru bir röportajda bana istendi. Ayrıca kök düğüme bir işaretçi bana veriliyor.


Benim cevabım ise:

Eğer beklenen düğümü ulaşana kadar düğümler her ikisi için ayrı ayrı ağaç Traverse. Bağlantılı bir listede depo elemanı ve bir sonraki adresi geçme ise Paralel. Öyleyse biz iki bağlı listeler var. Yani hem bağlı listelerde üst iki bağlantılı listeleri ve son ortak düğümü olduğunu karşılaştırarak deneyin.

Ben, bu çözüm doğru olduğunu düşünerek yanlış varsa düzeltin ediyorum. Bu çözüm bu bu görev için sadece daha iyi bir çözümdür veya bundan daha başka daha iyi bir çözüm yoktur Bildiğiniz, doğruysa!

Oluştur 30/05/2011 saat 11:18
kaynak kullanıcı
Diğer dillerde...                            


10 cevaplar

oy
2

Bir seviye malzeme geçişi olun ve her düğüm için biz onun çocukları kontrol karşılaşıyoruz. bunlar sağlanan rastgele düğümleri, o zaman ata düğümü bulunur.

Edit1:

İşte bir taslak olduğunu

struct _node {
   my_type data;
   struct _node *left;
   struct _node *right;
}

q = queue_create ();
queue_insert (q, head);
temp = head;
while (!empty (q))
{
    temp = queue_remove (q);
 if (
      (temp->left == my_random_node_1) && (head->right == my_random_node_2) ||
      (temp->left == my_random_node_2) && (head->right == my_random_node_1)
    )
    {
       /* temp is the common parent of the two target notes */
       /* Do stuffs you need to do */
    }

    /* Enqueue the childs, so that in successive iterations we can
     * check them, by taking out from the queue
     */
    push (q, temp->left);
    push (q, temp->right);
}

GÜNCELLEŞTİRME

cevap yok bulunacağını ortak ebeveynin çocuk değilseniz önceki algoritma sadece bu nedenle eğer iki rastgele seçilen düğümlerin ortak ebeveynler (doğrudan atası) bulabilirsiniz.

Aşağıdaki algoritma ortak ataları ve sadece anne bulacaksınız.

Aşağıdaki algoritma çalışır düşünüyorum:

İkili ağacın bir postorder geçişi yapın ve rastgele düğüm 1 bulmak r1biz o zaman olmak bir devlet değişkeni işaretlemek bulursanız, devlet bir , ikincisi düğüm için bulmaya devam sonra devlet değişkeni güncellemek bulursa, durumu iki ve daha arama durdurmak ve geri dönün. Durum değişkeni kendi ebeveynleri (yinelemeli) her düğüm tarafından geçirilmelidir. Devlet değişken karşılaştığı ilk düğüm durum, iki ortak atası.

aşağıdaki gibi algoritmanın uygulanması:

int postorder (node *p, int r1, int r2)
{
  int x = 0; /* The state variable */
  if (p->data == TERMINAL_VAL)
    return x;

  /* 0x01 | 0x02 = 0x03 threfore 
   * state one is when x = 0x01 or x = 0x02
   * state two is when x = 0x03
   */
  if (p->data == r1)
    x |= 0x01;
  else if (p->data == r2)
    x |= 0x02;

  /* if we have x in state two, no need to search more
   */
  if (x != 0x03)
    x |= postorder (p->left, r1, r2);
  if (x != 0x03)
    x |= postorder (p->right, r1, r2);

  /* In this node we are in state two, print node if this node
   * is not any of the two nodes r1 and r2. This makes sure that
   * is one random node is an ancestor of another random node
   * then it will not be printed instead its parent will be printed
   */
  if ((x == 0x03) && (p->data != r1) && (p->data != r2))
  {
   printf ("[%c] ", p->data);
   /* set state variable to 0 if we do not want to print 
    * the ancestors of the first ancestor 
    */
   x = 0;
  }

  /* return state variable to parent
   */    
  return x;
}

Ben algoritmanın doğruluğunu kanıtlamak için hala olsam da bu, düzgün çalışacaktır düşünüyorum. Bir düğüm başka düğüm, bir çocuk ise, o zaman onların yerine üst baskı, diğeri üstüdür sadece düğüm yazdırır, biri dezavantajı vardır. Rasgele düğümün biri bir başka rasgele düğümün atası sonra yerine atası rastgele düğümü baskı ise, bunun üst yazdırılır. Rasgele düğümün bir kök düğüm olduğu durumda, her zaman diğer rasgele düğümün atası gibi, hiçbir şey yazdırır ve bu nedenle ortak atası yok. Bu özel durumda fonksiyon dönecektir 0x03içinde mainve tespit edilebilir.

Bu algoritma, bu nedenle bir postorder geçişi dönerken, böylece O (n) işlem süresini kısaltır ve O (n) bellek gerektirir. Ayrıca arama kısa sürede hem düğümleri bulunur durur durmaz, düğümler daha sığ çabuk arama sona erer.

GÜNCELLEŞTİRME

: İşte bazı mod tartışmaları nasıl herhangi bir ikili ağaçta iki düğüm düşük ortak atası bulunur?

Cevap 30/05/2011 saat 11:23
kaynak kullanıcı

oy
0

Her iki düğüm bazı belirli düğümün hemen çocuk istediğini varsayıyoruz çünkü @Above, bu, işe yaramaz ...

            8
     10           12
 7             

ve ben 7 ve 12 olarak düğümleri verdi cevabı 8. böyle yapalım olmalı

    find(root, d1, d2, n1=null, n2=null)
     {
          if(n1 && n2) return;
          if(!root) return;

          else  if(root -> d == d1 ) n1 = root;
          else  if(root -> d == d2 ) n2 = root;                     
          find(root->left, d1, d2, n1, n2);
          find(root->right, d1, d2, n1, n2);
     }

     LCA(root, d1, d2)
     {
         node *n1=null, *n2=null;
         find(root, d1, d2, n1, n2);
         if(n1 == null || n2 == null )error 'nodes not present' exit(0);
         findIntersect(n1, n2); 
     }
     findInterSect(node *n1, node *n2)
     {
        l1 = length(n1);
        l2 = length(n2);
        node *g = n2, *l = n1;
        diff = abs(l1 - l2);
        if(l1>l2) g = n1 l =n2 
        while(diff) g = g->parent; diff--;
        // now both nodes are at same level
        while(g != l) g= g->parent, l = l->parent;
     }
Cevap 30/08/2011 saat 17:29
kaynak kullanıcı

oy
0

pseudocode:

node *FindCommonAncestor(node *root, node *node1, node *node2) {
  node *current = node1;
  node_list temp_list;
  temp_list.add(current);
  while (current != root) {
    current = current.parent;
    temp_list.add(current);
  }
  current = node2;
  while (current not in temp_list) {
    current = current.parent;
  }
  return current;
}

düğümleri kesinlikle aynı ağacın parçası ise, o zaman kesinlikle (en kötü durumda kök olsa bile) ortak bir atası olacak. Bu yüzden her zaman sona erer ve endişelenecek hiçbir hata durumu var.

İlk döngü çalışır n kere n node1 derinliği, bu yüzden O (n) var. İkinci döngü m süreleri, node2 derinliklerinde m çalışır. geçici listesine arama (en kötü) n. Yani ikinci halka O (m * n) ve hakim, bu fonksiyon, O (m, * n) 'de çalışır.

Bir liste yerine geçici alan için iyi bir dizi veri yapısı (örneğin, bir karma tablo) kullanmak ise, (1), sıc düğümlerin eklenmesi maliyetini arttırmadan (tipik olarak) O'ya arama kesebilir. Bu O (m) için fonksiyon süresini azaltır.

yer gereksinimi O (n) ya da bir yöntemdir.

biz n biliyoruz ve vaktinden m olmadığından, en ağacında toplam düğüm sayısı açısından sorayım: bu yüzden, S. ağaç sonra, dengeli ise n ve m her log_2 (S) tarafından sınırlandırılmış olan uygulama süresi O (log_2 (S) ^ 2) 'dir. S ağaç dengeli değilse ben 2. gücünden dert ediyorum önce oldukça büyük almak zorunda bu yüzden Log_2 o zaman log_2 (ağaç aslında bir bağlantılı liste halinde dejenere olabilir) kaybetmek, oldukça güçlüdür. (Bir düğüm kök ve diğeri tamamen dejenere ağacın yaprak olduğunda) Yani mutlak kötü durum O (S ^ 2) 'dir.

Cevap 30/08/2011 saat 18:15
kaynak kullanıcı

oy
6

Rastgele düğüm hem de bir işaretçi olarak ayarlayın. Üste geçme ve kök düğümünden mesafe sayılarak her düğümün derinliği bulun. Sonra tekrar, her iki düğüm de işaretçisini. Her iki işaretçileri aynı derinlikte olana kadar derin düğümü için, yukarı çapraz. işaretçileri aynı düğüm işaret edinceye kadar, her iki düğüm için hareket. Bu ata düğümdür.

Derken sadece geçerli düğümün ebeveyne işaretçiyi taşımak demek "yukarı çapraz".

Düzenleme netleştirmek için: anahtar fikri hem düğümler aynı derinlikte olduğu zaman, sadece basit geçişi ile çok hızlı bir şekilde ortak ebeveyn bulabilirsiniz olmasıdır. Yani aynı derinlikte hem kadar bir alt tırmanmaya ve sonra yukarı çapraz. Maalesef Gerçekten C bilmiyorum ya da ben kod yazmak istiyorum, ama bu algoritma sorunuza cevap olmalıdır.

Yeniden düzenleme: Ve yöntemi O (log (n)) zaman ve O (1) bellekte çalışır.

Başka düzenleme: O (log (n)) dengeli bir ağaç. En kötü durum performans dengesiz bir ağacın için O (n) 'dir. Teşekkür @DaveCahill

Cevap 30/08/2011 saat 20:15
kaynak kullanıcı

oy
1

Bu sorun, çok iyi çalışılmış ve doğrusal zamanda bunu çözebilir bilinen algoritmalar vardır. Bu yazıda bunu çözmek için kullanabileceğiniz birçok farklı yaklaşımları açıklar. Admittedtly bir araştırma kağıt ve böylece algoritmaları biraz zor, ama açıklar yaklaşımların bazıları aslında oldukça mümkün bulunmaktadır.

Cevap 30/08/2011 saat 20:47
kaynak kullanıcı

oy
7

Belki saçma yaklaşım:

"L" ve "R" bir dizi olarak depolamak, kök Her bir düğümden yolu oluşturur.

bu dizeleri Ters. En uzun ortak önek atın - Şimdi atadan gelme yoluna sahip.

Cevap 30/08/2011 saat 22:21
kaynak kullanıcı

oy
0
  1. düğümün herhangi 1 olmadıkça Ön sipariş geçişi araya geldi ve düğümleri şimdi uptil ziyaret tasarruf edilir.

  2. Bir sonraki düğüm ulaşılana kadar herhangi bir düğüm karşılanmaktadır (verilen iki düğüm) 1. ve listesini kaydetmek, geçiş Inorder, düğümleri tasarrufu başlayın.

  3. , Sipariş geçişi sonrası her iki düğüm ziyaret hav zaman düğümleri kaydetmeye başlar ...
               bir         
      M.Ö         
  DEFG       
HIJKLMNO     

Varsayalım H ve E, iki rasgele düğümlerdir.

  1. ABDH
  2. HDIBJE
  3. EBLMENOGCA

üçünde de ortak birinci düğümü bulun ...

Cevap 15/01/2012 saat 15:55
kaynak kullanıcı

oy
3

Ben sadece her iki düğüm için aynı anda bir arama yapabileceğini düşünüyorum; Arama uzaklaşan noktadır ortak atası.

commonAncestor tree a b:
  value := <value of node 'tree'>
  if (a < value) && (b < value)
  then commonAncestor (left tree) a b
  else if (a > value) && (b > value)
  then commonAncestor (right tree) a b
  else tree

İlginçtir bu yaklaşım ikiden fazla düğümler (sol tarafta olmak hepsini kontrol etmek için ölçek olur tree, vs.)

Cevap 05/02/2012 saat 06:18
kaynak kullanıcı

oy
0

düğümler için> veri değerleri kabul edilmektedir - hi Bu ağaç ve val1 kökü, val2 en düşük ata düğümü değeri döndürür

int CommonAncestor(node *root, int val1,int val2) 
{

    if(root == NULL || (! root->left && ! root->right  )
        return false;

        while(root)
        {
            if(root->data < val1 && root->data < val2)
             {
                root = root->left;
             }
             else if(root->data > val1 && root->data > val2)
            {
                root= root->right;
            }
            else
              return root->data;      
        }
}
Cevap 25/09/2012 saat 11:57
kaynak kullanıcı

oy
0

Burada referans c # (net) (her ikisi de yukarıda tarif edilmektedir) de iki yaklaşım vardır:

  1. Ikili ağaç (O (N) - en fazla her bir düğüm ziyaret edilen gibi) de LCA bulma Yinelemeli versiyonu (çözümün temel noktalar LCA olduğu , (a) her iki eleman sol (Alt ağaçlardan her iki tarafında bulunan burada ikili ağaç tek düğüm . ve sağ) LCA olan b () ve ayrıca mevcut iki tarafında olan düğüm fark etmez - başlangıçta ben bu bilgi tutmaya çalıştı ve açıkçası özyinelemeli fonksiyon öylesine kafa karıştırıcı hale ben bunu anlayınca, çok şık oldu..

  2. Her iki düğüm (O (N)) Arama ve yolları (takip ekstra alan kullanır - bu yüzden, 1. hatta ikili ağaç sonra ekstra bellek tüketimi sadece olacak hem de dengeli olması durumunda uzay muhtemelen önemsiz düşünce muhtemelen üstündür O (log (N)).

    böylece yolları karşılaştırılmıştır olduğu (kabul edilen cevap essentailly - benzer ancak yollar işaretçi düğümü varsayılarak hesaplanır ikili ağaç bezinde mevcut değildir)

  3. Sadece tamamlanması için ( soru ile ilgili değildir ), LCA BST içinde (O (log (K))

  4. Testler

yinelemeli:

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;
        }

nerede özel özyinelemeli sürümü üzerinde aşağıdaki toplu 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 yolları 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 olarak 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;
        }

TSİ (LCA) - (referans sadece tamamlanması için) ilgili olmayan

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);
            }
        }
Cevap 14/07/2014 saat 14:02
kaynak kullanıcı

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more