Finne felles stamfar i et binært tre

stemmer
7

Dette spørsmålet ble stilt til meg i et intervju: Jeg har et binært tre, og jeg må finne felles stamfar (forelder) gitt to tilfeldige noder av det treet. Jeg er også gitt en peker til rotnoden.


Mitt svar er:

Traversere treet separat for begge nodene til du kommer til noden som er forventet. Parallelt mens traversering store elementet og den neste adressen i en lenket liste. Da har vi to lenkede lister med oss. Så prøv å sammenligne de to koblede lister og den siste felles knutepunkt i begge de koblede listene er den overordnede.

Jeg tenker at denne løsningen er riktig, korrigere meg hvis jeg tar feil. Hvis denne løsningen er riktig, kan jeg vet er dette den eneste bedre løsning for denne oppgaven, eller er det noen annen bedre løsning enn dette!

Publisert på 30/05/2011 klokken 10:18
kilden bruker
På andre språk...                            


10 svar

stemmer
2

Gjør et nivå for traversering, og for hver node vi støter vi sjekke sine barn. Hvis de er de angitte tilfeldige noder, så stamnoden er funnet.

EDIT1:

Her er en oversikt

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

OPPDATER

Den forrige algoritmen vil bare finne felles foreldre (direkte stamfar), derfor hvis to tilfeldig valgte noder hvis ikke er et barn av felles foreldre noe svar ville bli funnet.

Den nedenfor algoritmen vil finne felles forfedre, og ikke bare foreldre.

Jeg tror følgende algoritme vil fungere:

Lag en Postorder traversering av det binære treet, og finne for tilfeldig node 1 r1, hvis vi finner det så merker det i en tilstandsvariabel til å være i tilstand en , og holde finne for den andre noden, hvis det blir funnet deretter oppdatere tilstandsvariabel til tilstand to , og slutte å lete mer og tilbake. Staten variable bør bli vedtatt av hver node til sine foreldre (rekursivt). Den første noden som møter den tilstandsvariabel i tilstand to er den felles stamfar.

Gjennomføringen av algoritmen er som følger:

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

Jeg tror dette vil fungere, men jeg er fortsatt å bevise algoritmen er korrekt. Det er en ulempe, som er, hvis en node er et barn av en annen node, så vil det bare skrive ut noden som er forelder til den andre, i stedet for å skrive den overordnede av dem. Hvis en av de tilfeldig node er en stamfar til en annen tilfeldig node så i stedet for å skrive ut stamfar tilfeldig node, vil den skrive ut forelder av det. I tilfelle hvor en av de tilfeldig node er rotnoden, skrives ingenting, så det er alltid stamfar til den andre tilfeldig node, og derfor eksisterer ikke deres felles stamfar. I dette spesielle tilfellet funksjonen vil returnere 0x03i main, og det kan bli oppdaget.

Ettersom denne algoritmen gjør en Postorder traversering derfor krever den O (n) utførelsestid og således O (n) minne. Også som søke stopper så snart begge nodene er funnet, de grunnere nodene raskere søket avsluttes.

OPPDATER

Her er noen modus diskusjoner: Hvordan finne minste felles stamfar to noder i et binært tre?

Svarte 30/05/2011 kl. 10:23
kilden bruker

stemmer
0

@Above, vil dette ikke fungere, fordi du er forutsatt at begge noder er umiddelbar barn av noen bestemt node ...

            8
     10           12
 7             

og jeg ga nodene som 7 og 12, må svaret være 8. Lar liker dette

    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;
     }
Svarte 30/08/2011 kl. 16:29
kilden bruker

stemmer
0

pseudo~~POS=TRUNC:

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

Hvis nodene er definitivt en del av det samme treet, så vil de definitivt har en felles stamfar (selv om det er rot i verste fall). Så det vil alltid terminere og det er ingen feil betingelse for å bekymre seg for.

Den første sløyfe løper n ganger, hvor n er dybden av node1, så det er O (n). Den andre sløyfe løper m ganger, hvor m i dybden av node2. Oppslags til temp liste er (i verste fall) n. Så den andre sløyfen er O (m * n), og den dominerer, så funksjonen kjører i O (m * n).

Hvis man bruker et godt sett datastrukturen (for eksempel en nøkkeltabell) for temp plass i stedet for en liste, kan man kutte oppslag til (typisk) O (1), uten å øke kostnadene ved å legge noder til temp. Det reduserer funksjonen tid til O (m).

Plassbehovet er O (n) uansett.

Siden vi ikke vet n og m på forhånd, la oss sette det i form av det totale antall noder i treet: S. Dersom treet er balansert, deretter n og m er hver avgrenset av log_2 (S), så driftstiden er O (log_2 (S) ^ 2). Log_2 er ganske kraftig, så S måtte bli ganske stor før jeg skulle bekymre seg om kraften i 2. Dersom treet er ikke balansert, så vi mister log_2 (treet kan faktisk utarte til en lenket liste). Så den absolutte verste fall (når en node er roten og den andre er blad av et fullstendig degenerert tre) er O (S ^ 2).

Svarte 30/08/2011 kl. 17:15
kilden bruker

stemmer
6

Sett en peker ved begge av de tilfeldige noder. Finn dybden av hver node ved å gå gjennom til den øvre og telle avstand fra rotnoden. Deretter stiller viseren i begge nodene på nytt. For dypere noden, traversere frem til begge pekere er ved samme dybde. Deretter traversere opp for både noder inntil pekerne peker til den samme node. Det er stamfar node.

Med "traversere opp" jeg mener bare flytte pekeren til den overordnede av den aktuelle noden.

Rediger for å klargjøre: Nøkkelen Tanken er at når begge noder er på samme dybde, kan du finne felles foreldre veldig raskt bare ved enkel traversering. Så du klatre den nederste til begge er på samme dybde, og så traversere opp. Beklager at jeg ikke vet C eller jeg ville skrive kode, men at algoritmen skal svare på spørsmålet ditt.

Redigere igjen: Og min metode går i O (log (n)) tid og O (1) minne.

En annen edit: O (log (n)) i et balansert tre. Verste fall ytelse er O (n) for en ubalansert tre. Takket @DaveCahill

Svarte 30/08/2011 kl. 19:15
kilden bruker

stemmer
1

Dette problemet har blitt svært godt undersøkt og det er kjente algoritmer som kan løse det i lineær tid. Dette notatet beskriver mange ulike tilnærminger du kan bruke til å løse det. Admittedtly er det et forsknings-papir og så algoritmer er litt vanskelig, men noen av de tilnærminger som beskrives er faktisk ganske gjennomførbart.

Svarte 30/08/2011 kl. 19:47
kilden bruker

stemmer
7

Kanskje dumt tilnærming:

Generer banen fra hver node til roten, lagre den som en streng av "L" og "R".

Reversere disse strengene. Ta den lengste felles prefiks - du har nå banen til felles stamfar.

Svarte 30/08/2011 kl. 21:21
kilden bruker

stemmer
0
  1. Forhånds for traversering med mindre noen en av noden er oppfylt og lagre nodene besøkt uptil nå.

  2. Inorder traversering, begynne å spare nodene når noen en (av de to medfølgende noder) node er oppfylt, og lagre listen til neste node er oppfylt.

  3. poste for traversering, begynne å spare nodene når begge nodene hav blitt besøkt ...
               EN         
      BC         
  DefG       
HIJKLMNO     

Anta H og E er to tilfeldige noder.

  1. ABDH
  2. HDIBJE
  3. EBLMENOGCA

Finn den første noden vanlig i alle tre ...

Svarte 15/01/2012 kl. 14:55
kilden bruker

stemmer
3

Jeg tror du kan bare gjøre et søk samtidig i begge noder; det punktet hvor søke divergerer er felles stamfar.

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

Interessant denne tilnærmingen vil skalere til mer enn to noder (sjekk for alle av dem for å være på den venstre siden av tree, etc.)

Svarte 05/02/2012 kl. 05:18
kilden bruker

stemmer
0

hi dette vil returnere laveste stamnoden verdi hvor roten av treet og VAL1, VAL2 -> dataverdier for nodene blir ført

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;      
        }
}
Svarte 25/09/2012 kl. 10:57
kilden bruker

stemmer
0

Her er to tilnærminger i C # (.net) (begge omtalt ovenfor) for referanse:

  1. Rekursiv versjon av finne LCA i binært tre (O (N) - som ved de fleste hver node blir besøkt) (viktigste punktene i oppløsningen er LCA er (a) bare node i binært tre hvor begge elementer befinner seg på hver side av undertrærne (til venstre og høyre) er LCA. (b) og også det spiller ingen rolle hvilken node er tilstede hver side - i utgangspunktet jeg prøvde å holde det info, og åpenbart rekursiv funksjon blitt så forvirrende når jeg skjønte det, ble det svært elegant..

  2. Søke begge noder (O (N)), og holde styr på stier (bruker ekstra plass - så # 1 er trolig overlegen selv om plassen er trolig ubetydelig dersom det binære treet er godt balansert som så ekstra minneforbruket vil være like i O (log (N)).

    slik at banene blir sammenlignet (essentailly lik akseptert svar - men banene blir beregnet ved å anta pekeren node ikke er til stede i den binære trenode)

  3. Bare for gjennomføring ( ikke knyttet til spørsmål ), LCA i BST (O (log (N))

  4. tester

rekursiv:

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

der over private rekursiv versjon startes ved å følge offentlig metode:

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

Løsning ved å holde styr på stier både noder:

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

hvor FindNodeAndPath er definert som

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) - ikke relatert (bare for ferdigstillelse for referanse)

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

enhet tester

[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);
            }
        }
Svarte 14/07/2014 kl. 13:02
kilden bruker

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