एक द्विआधारी पेड़ में आम पूर्वज ढूँढना

वोट
7

यह सवाल एक साक्षात्कार में मुझसे पूछा गया था: मैं एक द्विआधारी पेड़ है और मैं आम पूर्वज (पैरेंट) कि पेड़ के दो यादृच्छिक नोड्स दिया लगाना होगा। मैं भी रूट नोड के लिए सूचक दिया हूँ।


मेरा जवाब है:

दोनों नोड्स के लिए अलग से पेड़ पार जब तक तुम नोड कि उम्मीद है तक पहुँचने। जबकि दुकान एक लिंक्ड सूची में तत्व और अगले पता traversing समानांतर। फिर हम हमारे साथ दोनों आपस में जुड़े सूचियों की है। तो दोनों जुड़े हुए सूचियों में है माता-पिता दोनों आपस में जुड़े सूचियों और पिछले आम नोड की तुलना की कोशिश करो।

मैं, सोच रहा हूँ कि इस समाधान सही है मुझे ठीक कर लें मैं गलत कर रहा हूँ। यदि यह समाधान मैं जानता हूँ कि हो सकता है यह इस कार्य के लिए केवल बेहतर समाधान है या वहाँ इस के अलावा कोई अन्य बेहतर समाधान है सही है, तो!

30/05/2011 को 11:18
का स्रोत उपयोगकर्ता
अन्य भाषाओं में...                            


10 जवाब

वोट
2

एक स्तर आदेश ट्रावर्सल करें, और प्रत्येक नोड के लिए हम मुठभेड़ हम अपने बच्चों की जाँच करें। वे प्रदान की यादृच्छिक नोड्स रहे हैं, तो पूर्वज नोड पाया जाता है।

EDIT1:

यहाँ एक रूपरेखा है

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

अद्यतन करें

पिछले एल्गोरिथ्म केवल आम माता-पिता (प्रत्यक्ष पूर्वज) है, इसलिए यदि दो बेतरतीब ढंग से चुना नोड्स पाते हैं आम माता-पिता की संतान कोई जवाब नहीं पाया जा होगा नहीं कर रहे हैं होगा।

नीचे दिए गए एल्गोरिथ्म आम पूर्वजों और केवल माता-पिता नहीं मिलेगा।

मुझे लगता है कि निम्नलिखित कलन विधि काम करेंगे:

द्विआधारी पेड़ की एक क्रंमोत्तर चंक्रमण करें, और यादृच्छिक नोड 1 के लिए लगता है r1, यदि हम पाते यह तो एक राज्य चर में चिह्नित में होने की राज्य एक है, और दूसरा नोड के लिए लग रहा रखने के लिए, तो राज्य चर करने के लिए अद्यतन पाया राज्य दो , और अधिक खोज कर बंद करो और वापस जाएँ। राज्य चर अपने माता-पिता (रिकर्सिवली) के लिए हर नोड द्वारा पारित किया जाना चाहिए। प्रथम नोड जिसमें राज्य चर का सामना करना पड़ता राज्य दो आम पूर्वज है।

एल्गोरिथ्म के कार्यान्वयन इस प्रकार है:

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

मुझे लगता है कि यह सही ढंग से काम करेंगे, हालांकि मैं अभी भी कर रहा हूँ एल्गोरिथ्म के सही होने को साबित करने के। वहाँ एक खामी है, जो है, अगर एक नोड एक और नोड का एक बच्चा है, तो यह बजाय उनमें से माता-पिता मुद्रण का केवल नोड जो एक दूसरे की मूल है प्रिंट होगा, है। यदि यादृच्छिक नोड के एक दूसरे के यादृच्छिक नोड के एक पूर्वज तो बजाय पूर्वज यादृच्छिक नोड मुद्रण है, यह यह की मूल प्रिंट होगा। मामला है जिसमें यादृच्छिक नोड में से एक रूट नोड है, यह कुछ भी नहीं प्रिंट होगा यह हमेशा अन्य यादृच्छिक नोड का पूर्वज है के रूप में, और इसलिए उनके आम पूर्वज मौजूद नहीं है। इस विशेष मामले में समारोह वापस आ जाएगी 0x03में mainहै और यह पता लगाया जा सकता।

इस एल्गोरिथ्म इसलिए एक क्रंमोत्तर चंक्रमण करता है के रूप में यह हे (एन) निष्पादन समय है और इस तरह हे (एन) स्मृति की आवश्यकता है। इसके अलावा खोज के रूप में जल्द दोनों नोड्स पाए जाते हैं बंद हो जाता है के रूप में, उथले नोड्स जल्दी खोज समाप्त होता है।

अद्यतन करें

यहाँ कुछ मोड में विचार विमर्श कर रहे हैं कैसे किसी भी द्विआधारी पेड़ में दो नोड्स के निम्नतम आम पूर्वज को खोजने के लिए?

30/05/2011 को 11:23
का स्रोत उपयोगकर्ता

वोट
0

@Above, इस काम नहीं करेगा, क्योंकि आप मानते हैं कि दोनों नोड्स कुछ विशेष नोड के तत्काल बच्चे हैं ...

            8
     10           12
 7             

और मैं 7 और 12 के रूप में नोड्स दिया था, इस सवाल का जवाब 8. इस तरह करने देता है होना चाहिए

    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;
     }
30/08/2011 को 17:29
का स्रोत उपयोगकर्ता

वोट
0

स्यूडोकोड:

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

नोड्स निश्चित रूप से एक ही पेड़ का हिस्सा हैं, तो वे निश्चित रूप से एक आम पूर्वज (भले ही वह सबसे खराब स्थिति में जड़ है) होगा। तो यह हमेशा समाप्त कर देगा और कोई त्रुटि स्थिति के बारे में चिंता है।

पहले पाश रन n बार है, जहां n node1 की गहराई है, तो यह हे (एन) है। दूसरी पाश m गुणा, जहां node2 की गहराई में मीटर चलाता है। अस्थायी सूची में देखने (सबसे खराब) n। तो दूसरी पाश हे (एम * एन) है, और यह हावी है, इसलिए समारोह हे (एम * एन) में चलता है।

आप एक सूची के बजाय अस्थायी स्थान के लिए एक अच्छा सेट डेटा संरचना (जैसे, एक हैश तालिका) का उपयोग करते हैं, तो आप (1), अस्थायी करने के लिए नोड्स जोड़ने की लागत में वृद्धि के बिना करने के लिए (आमतौर पर) हे देखने कटौती कर सकते हैं। यह हे (एम) के लिए हमारी समारोह समय कम कर देता है।

स्थान आवश्यकता हे (एन) किसी भी तरह से है।

जब से हम n जानते हैं और समय से आगे हूँ नहीं है, यह पेड़ में नोड्स की कुल संख्या के मामले में डाल देना: एस तो पेड़ संतुलित है, तो n और मीटर प्रत्येक log_2 (एस) से घिरा रहे हैं, इसलिए रन टाइम ओ (log_2 (एस) ^ 2) है। Log_2, इसलिए S बहुत बड़ी पाने के लिए इससे पहले कि मैं 2. की शक्ति के बारे में चिंता हैं तो पेड़ संतुलित नहीं है के लिए होता है, बहुत शक्तिशाली है तो हम log_2 (पेड़ वास्तव में एक लिंक्ड सूची में पतित हो सकता है) खो देते हैं। तो निरपेक्ष सबसे खराब स्थिति (जब एक नोड जड़ है और अन्य एक पूरी तरह से पतित पेड़ की पत्ती है) हे (एस ^ 2) है।

30/08/2011 को 18:15
का स्रोत उपयोगकर्ता

वोट
6

यादृच्छिक नोड्स के दोनों पर एक सूचक सेट करें। शीर्ष करने के लिए traversing और रूट नोड से दूरी की गणना के द्वारा प्रत्येक नोड की गहराई का पता लगाएं। तब फिर दोनों नोड्स पर सूचक निर्धारित किया है। गहरी नोड के लिए, ऊपर पार जब तक दोनों संकेत उसी गहराई पर हैं। जब तक संकेत दिए गए एक ही नोड को इंगित फिर दोनों नोड्स के लिए बढ़ावा देते हैं। यही कारण है कि पूर्वज नोड है।

द्वारा "ऊपर पार" मैं सिर्फ मतलब वर्तमान नोड के माता-पिता के लिए सूचक ले जाएँ।

स्पष्ट करने के लिए संपादित करें: कुंजी विचार जब दोनों नोड्स उसी गहराई में हैं, तो आप सिर्फ सरल ट्रेवर्सल द्वारा बहुत जल्दी आम माता-पिता को खोजने के कर सकते हैं। तो तुम दोनों जब तक कम एक चढ़ाई एक ही गहराई पर हैं, और फिर आप को बढ़ावा देते हैं। खेद है कि मैं वास्तव में सी पता नहीं या मैं कोड लिखने चाहते हैं, लेकिन है कि एल्गोरिथ्म आपके प्रश्न का उत्तर चाहिए।

फिर से संपादित करें: और मेरे विधि हे (लॉग (एन)) समय और हे (1) स्मृति में चलता है।

एक और संपादित करें: ओ (लॉग (एन)) एक संतुलित पेड़ में। बुरी से बुरी हालत प्रदर्शन हे (एन) एक असंतुलित पेड़ के लिए है। धन्यवाद @DaveCahill

30/08/2011 को 20:15
का स्रोत उपयोगकर्ता

वोट
1

यह समस्या बहुत अच्छी तरह से अध्ययन किया गया है और वहाँ ज्ञात एल्गोरिथम कि यह रेखीय समय में हल कर सकते हैं कर रहे हैं। इस पत्र कई विभिन्न दृष्टिकोण आप इसे हल करने के लिए उपयोग कर सकते हैं वर्णन करता है। Admittedtly यह एक शोध पत्र है और इसलिए एल्गोरिदम थोड़ा मुश्किल है, लेकिन दृष्टिकोण यह वर्णन से कुछ वास्तव में काफी संभव है।

30/08/2011 को 20:47
का स्रोत उपयोगकर्ता

वोट
7

हो सकता है कि मूर्ख दृष्टिकोण:

रूट में प्रत्येक नोड से पथ उत्पन्न करें, "L" और "आर" के एक स्ट्रिंग के रूप में यह भंडारण।

इन तार उल्टा। सबसे लंबे समय तक आम उपसर्ग डालें - तुम अब आम पूर्वज के लिए पथ है।

30/08/2011 को 22:21
का स्रोत उपयोगकर्ता

वोट
0
  1. पूर्व आदेश ट्रावर्सल जब तक नोड के किसी भी 1 से मुलाकात की और बचाने नोड्स अब uptil का दौरा किया है।

  2. ट्रेवर्सल Inorder, नोड्स बचत करना शुरू जब किसी भी 1 (के दो प्रदान की नोड्स) नोड पूरा किया जाता है, और जब तक अगले नोड पूरा किया जाता है सूची सहेजें।

  3. पोस्ट आदेश ट्रावर्सल, नोड्स बचत करना शुरू जब दोनों नोड्स का दौरा किया गया हवलदार ...
      ईसा पूर्व         
  DEFG       
HIJKLMNO     

मान लीजिए एच और ई दो यादृच्छिक नोड्स रहे हैं।

  1. ABDH
  2. HDIBJE
  3. EBLMENOGCA

प्रथम नोड तीनों में आम खोजें ...

15/01/2012 को 15:55
का स्रोत उपयोगकर्ता

वोट
3

मुझे लगता है कि तुम सिर्फ दोनों नोड्स के लिए एक साथ एक खोज कर सकता है; बिंदु है जिस पर खोज diverges आम पूर्वज है।

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

दिलचस्प बात यह है इस दृष्टिकोण दो से अधिक नोड्स (उन सभी के लिए जाँच की बाईं ओर स्थित होने के लिए करने के लिए पैमाने पर होगा tree, आदि)

05/02/2012 को 06:18
का स्रोत उपयोगकर्ता

वोट
0

नोड्स के लिए> डेटा मान पारित कर दिया जा रहा है - हाय यह सबसे कम पूर्वज नोड मूल्य जहां पेड़ और VAL1 की जड़, val2 वापस आ जाएगी

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;      
        }
}
25/09/2012 को 11:57
का स्रोत उपयोगकर्ता

वोट
0

यहाँ c # (.net) (दोनों के ऊपर चर्चा) संदर्भ के लिए में दो दृष्टिकोण हैं:

  1. द्विआधारी पेड़ (ओ (एन) - के रूप में प्रत्येक नोड अधिक से अधिक का दौरा किया है) में एलसीए पाने की पुनरावर्ती संस्करण (समाधान के मुख्य बिंदुओं एलसीए है (क) जहां दोनों तत्वों (उपतरू के दोनों ओर रहते बाईं द्विआधारी पेड़ में केवल नोड और दाएं) एलसीए है। ख () और यह भी कोई फर्क नहीं पड़ता जो नोड वर्तमान दोनों ओर है - शुरू में मैं उस जानकारी रखने की कोशिश की, और स्पष्ट रूप से पुनरावर्ती क्रिया इतनी भ्रमित हो जाते हैं एक बार मैं यह महसूस किया है, यह बहुत ही सुंदर बन गया।।

  2. दोनों नोड्स (ओ (एन)) सर्च कर रहे हैं, और पथ (का ट्रैक रखने के लिए अतिरिक्त जगह का उपयोग करता है - तो, ​​# 1 शायद बेहतर है भी विचार था कि यदि द्विआधारी पेड़ में अच्छी तरह से तो अतिरिक्त स्मृति की खपत सिर्फ में होगा संतुलित है अंतरिक्ष शायद नगण्य है हे (लॉग (एन))।

    ताकि रास्तों तुलना की जाती है (essentailly स्वीकार किए जाते हैं जवाब देने के लिए इसी तरह की है - लेकिन पथ सूचक नोड संभालने करके की जाती है द्विआधारी पेड़ नोड में मौजूद नहीं है)

  3. बस पूरा होने (के लिए सवाल करने के लिए संबंधित नहीं ), एलसीए BST में (ओ (लॉग (एन))

  4. टेस्ट

पुनरावर्ती:

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

जहां निजी पुनरावर्ती संस्करण ऊपर निम्नलिखित सार्वजनिक विधि से शुरू हो जाती है:

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

दोनों नोड्स के रास्तों पर नज़र रखने के द्वारा समाधान:

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

जहां FindNodeAndPath के रूप में परिभाषित किया गया है

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 (एलसीए) - संबंधित नहीं है (बस संदर्भ के लिए पूरा करने के लिए)

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

यूनिट टेस्ट

[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);
            }
        }
14/07/2014 को 14:02
का स्रोत उपयोगकर्ता

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