Megtaláljuk a közös őse egy bináris fa

szavazat
7

Ezt a kérdést nekem egy interjúban: Van egy bináris fa, és azt kell, hogy megtalálják a közös ős (szülő) adott két véletlen csomópont, hogy a fa. Én is kap egy mutatót a gyökér.


A válaszom:

Áthalad a fa külön-külön a csomópontok, amíg el nem éri a csomópont, ami várható. Ezzel párhuzamosan, miközben áthalad tárolja az elemet és a következő címet egy kapcsolt listán. Aztán van két kapcsolt listák velünk. Így próbálja összehasonlítva a két kapcsolt listák és az utolsó közös csomópont mind a kapcsolt listák a szülő.

Gondolok, hogy ez a megoldás helyes, helyes, ha tévedek. Ha ez a megoldás helyes, akkor tudom, hogy ez az egyetlen jobb megoldás erre a feladatra, vagy van-e más jobb megoldás, mint ez!

A kérdést 30/05/2011 11:18
a forrás felhasználó
Más nyelveken...                            


10 válasz

szavazat
2

Készíts egy szinten annak érdekében, bejárás, és minden egyes csomóponthoz találkozunk ellenőrizzük a gyerekek. Ha ezek a nyújtott véletlenszerű csomópontok, majd az ős csomópont található.

Edit1:

Itt egy vázlat

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

UPDATE

Az előző algoritmus csak megtalálni a közös szülők (közvetlen őse), ezért ha két véletlenszerűen kiválasztott csomópont, ha nem a gyermek a szülő közös nincs válasz lenne megtalálható.

Az alábbi algoritmus fogja találni a közös ősök, és nem csak a szülők.

Azt hiszem, a következő algoritmust fog működni:

Készítsen postorder bejárás a bináris fa, és megtalálja a véletlen 1 csomópont r1, ha úgy gondoljuk, majd jelölje be állapotváltozóként lenni állam egyik , és folyamatosan találni a második csomópont, ha talál, akkor frissíti az állam változó állam két és ne keressen tovább, és visszatér. Az állam változó kell átadni a minden csomópont a szülők (rekurzívan). Az első csomópont, amely találkozik az állapotváltozó a állapotban két a közös ős.

A végrehajtás az algoritmus a következő:

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

Azt hiszem, hogy ez működni fog rendesen, bár én még mindig bizonyítani az algoritmus helyességét. Van egy hátránya, ami, ha egy csomópont egy gyermek egy másik csomópont, akkor kiírja csak a csomópont, amely a szülő a másik, nyomtatás helyett a szülő őket. Ha az egyik véletlen csomópont egy őse egy másik véletlen csomópont ezután nyomtatás helyett az előd véletlen csomópont, kiírja a szülő is. Abban az esetben, ahol az egyik véletlen csomópont a gyökér csomópont, akkor nyomtasson semmit, mert mindig az őse a többi random csomópont, ezért a közös őse nem létezik. Ebben a speciális esetben a függvény visszatérési 0x03in mainés lehet kimutatni.

Mivel ez az algoritmus nem egy postorder bejárás ezért igényel O (n) végrehajtási ideje, és így O (n) memóriájába. Szintén a keresés leáll, amint a két csomópont található, a sekélyebb a csomópontok, annál gyorsabb a keresés véget ér.

UPDATE

Íme néhány mód beszélgetések: Hogyan találjuk meg a legkisebb közös őse két csomópont minden bináris fát?

Válaszolt 30/05/2011 11:23
a forrás felhasználó

szavazat
0

@Above, ez nem fog működni, mert feltételezzük, hogy a két csomópont közvetlen gyerek bizonyos speciális csomópont ...

            8
     10           12
 7             

és adtam a csomópontok például a 7 és 12, választ kell 8. Lets do ilyen

    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;
     }
Válaszolt 30/08/2011 17:29
a forrás felhasználó

szavazat
0

pszeudokódját:

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

Ha a csomópontok Egyértelműen része ugyanazon a fán, akkor majd biztosan van egy közös őse (még ha ez a gyökér a legrosszabb esetben). Így mindig megszűnik, és nincs hiba feltétel aggódni.

Az első hurok fut n-szer, ahol n a mélysége Node1, így O (n). A második körben fut m-szer, ahol m mélységben node2. A kikeresési be temp lista (a legrosszabb) n. Tehát a második hurok O (m * n), és ez dominál, így a funkció lépésszáma O (m * n).

Ha egy jó sor adatszerkezet (például egy hash tábla) az ideiglenes tárhely a lista helyett, akkor vágott a keresést, hogy (általában) O (1), anélkül, hogy növelné a hozzá csomópontok temp. Ez csökkenti a funkció ideje O (m).

A helyigény O (n) mindkét irányban.

Mivel nem tudjuk, hogy n és m időben, mondjuk úgy tekintve az összes a fa csúcsainak: S. Ha a fa kiegyensúlyozott, akkor n és m egymástól által határolt log_2 (S), így a futtatási idő O (log_2 (S) ^ 2). Log_2 elég erős, így S volna, hogy elég nagy, mielőtt én aggódni a hatalom 2. Ha a fa nem kiegyensúlyozott, akkor elveszítjük a log_2 (a fa talán valóban fajulhat egy láncolt lista). Tehát a legrosszabb esetben (ha az egyik csomópont a gyökér, a másik pedig a levél egy teljesen degenerált fa) O (S ^ 2).

Válaszolt 30/08/2011 18:15
a forrás felhasználó

szavazat
6

Állítsa be a mutató mindkét véletlen csomópontokat. Keresse meg a mélysége egyes csomópontok mozgással a felső, illetve hogy a távolság a gyökér csomópontot. Ezután állítsa a mutatót mindkét csomópont újra. A mélyebb csomópont áthaladva egészen mind pointerek azonos mélységben. Ezután áthalad fel mindkét csomópontja, amíg a mutató ugyanarra a csomópontra. Ez az ős csomópontot.

A „keresztezik up” Én csak azt jelenti, vigye a mutatót a szülő az aktuális csomópont.

Szerkesztés, hogy tisztázza: A lényeg az, hogy amikor a két csomópont azonos mélységben, megtalálja a közös szülő nagyon gyorsan csak egyszerű bejárás. Szóval mászni az alsó, míg mindkettő azonos mélységben, majd áthalad fel. Bocs én nem igazán tudom, C vagy azt írni kódot, de ez algoritmust választ a kérdésére.

Szerkesztése újra: És a módszer lépésszáma O (log (n)) idő és O (1) memória.

Egy másik szerkesztés: O (log (n)) kiegyensúlyozott fa. A legrosszabb teljesítmény O (n) egy kiegyensúlyozatlan fa. Hála @DaveCahill

Válaszolt 30/08/2011 20:15
a forrás felhasználó

szavazat
1

Ez a probléma már nagyon jól tanult, és vannak ismert algoritmusok, amelyek képesek megoldani azt a lineáris időben. Ez a dokumentum ismerteti számos különböző megközelítés segítségével megoldani. Admittedtly ez egy tudományos tanulmány, így az algoritmusok egy kicsit trükkös, de néhány megközelítések leírt valójában nagyon megvalósítható.

Válaszolt 30/08/2011 20:47
a forrás felhasználó

szavazat
7

Talán buta megközelítés:

Generálása az út a minden egyes csomópont a gyökér, nehezen tárolható, mint egy sor „L” és „R”.

Fordított szóra. Vegyük a leghosszabb közös előtag - most már az utat a közös ős.

Válaszolt 30/08/2011 22:21
a forrás felhasználó

szavazat
0
  1. Pre érdekében bejárás, kivéve, ha bármelyik 1 csomópont teljesül, és mentse a csomópontok látogatott uptil most.

  2. Inorder bejárás, kezdje el a megtakarítást a csomópontokat, ha bármelyik 1 (a két nyújtott csomópontok) csomópont teljesül, és mentse el a listát, amíg a következő csomópontot teljesül.

  3. elhelyezhet érdekében bejárás, kezdje el a megtakarítást a csomópontokat, amikor mind a csomópontok hav látogatták ...
               A         
      időszámításunk előtt         
  DEFG       
HIJKLMNO     

Tegyük fel, és H és E két véletlenszerű csomópont.

  1. ABDH
  2. HDIBJE
  3. EBLMENOGCA

Keresse meg az első csomópont közös mindhárom ...

Válaszolt 15/01/2012 15:55
a forrás felhasználó

szavazat
3

Azt hiszem, akkor is csak egy keresést egyidejűleg mindkét csomópont; a pont, ahol a keresési eltér a közös őse.

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

Érdekes ez a megközelítés skála kettőnél több csomópontot (ellenőrizze az összes számukra, hogy a bal oldali tree, stb)

Válaszolt 05/02/2012 06:18
a forrás felhasználó

szavazat
0

hi ez vissza fog térni a legalacsonyabb őse csomópont értéket, ahol gyökér fa és ért1, ért2 -> adatértékeit csomópontok kerülnek át

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;      
        }
}
Válaszolt 25/09/2012 11:57
a forrás felhasználó

szavazat
0

Itt két megközelítése c # (.NET) (mindkét fent tárgyalt) referencia:

  1. Rekurzív változata találni LCA bináris fa (O (N) - mint a legtöbb minden egyes csomópont látogatott) (fő pontjai a megoldás LCA (a) csak csomópont bináris fa, ahol mindkét elem tartózkodnak mindkét oldalán a részfákat (balra és jobbra) LCA. (b) és azt is, hogy nem számít, melyik csomópont van jelen mindkét oldalán - kezdetben próbáltam tartani, hogy info, és természetesen a rekurzív függvény annyira zavaró. egyszer rájöttem, ez lett nagyon elegáns.

  2. Keresés egyaránt csomópontok (O (N)), és nyomon követi a pályák (használ extra helyet - úgy, # 1 talán superior is gondolt a tér valószínűleg elhanyagolható, ha a bináris fa kiegyensúlyozott mint akkor extra memória fogyasztás is csak O (log (N)).

    úgy, hogy a pályák összehasonlítjuk (essentailly hasonló elfogadott választ - de az utak úgy számítjuk ki, feltételezve, hogy a mutató csomópont nincs jelen a bináris fa csomópont)

  3. Csak a befejezése ( nem kapcsolódó kérdésre ), LCA BST (O (log (N))

  4. vizsgálatok

rekurzív:

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

ahol a fenti privát rekurzív változat által hivatkozott következő nyilvános mód:

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

Megoldás úgy, hogy követjük utak mindkét csomópont:

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

ahol FindNodeAndPath definiáljuk

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) - nem kapcsolódik (csak a befejezés a referencia)

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

Unit tesztek

[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);
            }
        }
Válaszolt 14/07/2014 14:02
a forrás felhasználó

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