LCA עבור n-ary Tree | שאילתה קבועה O(1)

LCA עבור n-ary Tree | שאילתה קבועה O(1)

ראינו שיטות שונות עם מורכבויות זמן שונות לחישוב LCA בעץ n-ary:-

שיטה 1: שיטה נאיבית (על ידי חישוב נתיב שורש לצומת) | O(n) לכל שאילתה  
שיטה 2: שימוש בפירוק Sqrt | O(sqrt H)  
שיטה 3: שימוש בגישת Sparse Matrix DP | O(לוגן) 

בואו ללמוד שיטה אחרת שיש לה זמן שאילתה מהיר יותר מכל השיטות לעיל. אז המטרה שלנו תהיה לחשב LCA ב זמן קבוע ~ O(1) . בואו נראה איך נוכל להשיג את זה. 

שיטה 4: שימוש בשאילתת מינימום טווח 

דנו LCA ו-RMQ עבור עץ בינארי . כאן אנו דנים בבעיית LCA להמרת בעיית RMQ עבור עץ n-ary. 

  Pre-requisites:-     LCA in Binary Tree using RMQ     RMQ using sparse table   

מושג מפתח: בשיטה זו אנו נצמצם את בעיית ה-LCA שלנו לבעיית RMQ(Range Minimum Query) על פני מערך סטטי. ברגע שנעשה זאת, נקשר את שאילתות המינימום של הטווח לשאילתות ה-LCA הנדרשות. 

הצעד הראשון יהיה פירוק העץ למערך ליניארי שטוח. לשם כך נוכל ליישם את הליכת אוילר. הליכת אוילר תיתן את המעבר בהזמנה מראש של הגרף. אז נבצע טיול אוילר על העץ ונאחסן את הצמתים במערך בזמן שנבקר בהם. תהליך זה מקטין את העץ > 16901489_1309372785813855_1903972436_n


עכשיו בואו נחשוב במונחים כלליים: שקול כל שני צמתים על העץ. יהיה נתיב אחד בדיוק המחבר את שני הצמתים והצומת בעל ערך העומק הקטן ביותר בנתיב יהיה ה-LCA של שני הצמתים הנתונים.
עכשיו קח כל שני צומת נפרדים לומר ב ו v במערך ההליכה אוילר. כעת כל האלמנטים בנתיב מ-u ל-v יהיו בין אינדקס הצמתים u ו-v במערך ההליכה של אוילר. לכן אנחנו רק צריכים לחשב את הצומת עם העומק המינימלי בין האינדקס של הצומת u לצומת v במערך euler. 

לשם כך נשמור על מערך נוסף שיכיל את העומק של כל הצמתים התואמים למיקומם במערך ההליכה של אוילר כדי שנוכל ליישם עליו את אלגוריתם ה-RMQ שלנו.

ניתן להלן מערך ההליכה של אולר במקביל למערך מסלולי העומק שלו. 

16934185_1309372782480522_1333490382_n


דוגמה: - שקול שני צמתים צומת 6 ו צומת 7 במערך אוילר. כדי לחשב את ה-LCA של צומת 6 וצומת 7 נסתכל על ערך העומק הקטן ביותר עבור כל הצמתים בין צומת 6 לצומת 7. 
לָכֵן צומת 1 יש את הקטן ביותר ערך עומק = 0 ומכאן שזה ה-LCA עבור צומת 6 וצומת 7.

יישום:-  

We will be maintaining three arrays   1)  Euler Path   2)  Depth array   3)  First Appearance Index 

אולר נתיב ומערך עומק זהים לתואר לעיל

אינדקס הופעה ראשונה FAI[] : מערך אינדקס ההופעה הראשונה יאחסן את האינדקס עבור המיקום הראשון של כל צומת במערך נתיב אוילר. FAI[i] = הופעה ראשונה של הצומת ה-ith במערך Euler Walk. 

היישום עבור השיטה הנ"ל ניתן להלן: -

יישום:

C++
   // C++ program to demonstrate LCA of n-ary tree   // in constant time.   #include     'bits/stdc++.h'   using     namespace     std  ;   #define sz 101   vector      <     int     >     adj  [  sz  ];     // stores the tree   vector      <     int     >     euler  ;     // tracks the eulerwalk   vector      <     int     >     depthArr  ;     // depth for each node corresponding      // to eulerwalk   int     FAI  [  sz  ];     // stores first appearance index of every node   int     level  [  sz  ];     // stores depth for all nodes in the tree   int     ptr  ;     // pointer to euler walk   int     dp  [  sz  ][  18  ];     // sparse table   int     logn  [  sz  ];     // stores log values   int     p2  [  20  ];     // stores power of 2   void     buildSparseTable  (  int     n  )   {      // initializing sparse table      memset  (  dp    -1    sizeof  (  dp  ));      // filling base case values      for     (  int     i  =  1  ;     i   <  n  ;     i  ++  )      dp  [  i  -1  ][  0  ]     =     (  depthArr  [  i  ]  >  depthArr  [  i  -1  ])  ?  i  -1  :  i  ;      // dp to fill sparse table      for     (  int     l  =  1  ;     l   <  15  ;     l  ++  )      for     (  int     i  =  0  ;     i   <  n  ;     i  ++  )      if     (  dp  [  i  ][  l  -1  ]  !=  -1     and     dp  [  i  +  p2  [  l  -1  ]][  l  -1  ]  !=  -1  )      dp  [  i  ][  l  ]     =      (  depthArr  [  dp  [  i  ][  l  -1  ]]  >  depthArr  [  dp  [  i  +  p2  [  l  -1  ]][  l  -1  ]])  ?      dp  [  i  +  p2  [  l  -1  ]][  l  -1  ]     :     dp  [  i  ][  l  -1  ];      else      break  ;   }   int     query  (  int     l    int     r  )   {      int     d     =     r  -  l  ;      int     dx     =     logn  [  d  ];      if     (  l  ==  r  )     return     l  ;      if     (  depthArr  [  dp  [  l  ][  dx  ]]     >     depthArr  [  dp  [  r  -  p2  [  dx  ]][  dx  ]])      return     dp  [  r  -  p2  [  dx  ]][  dx  ];      else      return     dp  [  l  ][  dx  ];   }   void     preprocess  ()   {      // memorizing powers of 2      p2  [  0  ]     =     1  ;      for     (  int     i  =  1  ;     i   <  18  ;     i  ++  )      p2  [  i  ]     =     p2  [  i  -1  ]  *  2  ;      // memorizing all log(n) values      int     val     =     1    ptr  =  0  ;      for     (  int     i  =  1  ;     i   <  sz  ;     i  ++  )      {      logn  [  i  ]     =     ptr  -1  ;      if     (  val  ==  i  )      {      val  *=  2  ;      logn  [  i  ]     =     ptr  ;      ptr  ++  ;      }      }   }   /**    * Euler Walk ( preorder traversal)    * converting tree to linear depthArray    * Time Complexity : O(n)    * */   void     dfs  (  int     cur    int     prev    int     dep  )   {      // marking FAI for cur node      if     (  FAI  [  cur  ]  ==  -1  )      FAI  [  cur  ]     =     ptr  ;      level  [  cur  ]     =     dep  ;      // pushing root to euler walk      euler  .  push_back  (  cur  );      // incrementing euler walk pointer      ptr  ++  ;      for     (  auto     x  :  adj  [  cur  ])      {      if     (  x     !=     prev  )      {      dfs  (  x    cur    dep  +  1  );      // pushing cur again in backtrack      // of euler walk      euler  .  push_back  (  cur  );      // increment euler walk pointer      ptr  ++  ;      }      }   }   // Create Level depthArray corresponding   // to the Euler walk Array   void     makeArr  ()   {      for     (  auto     x     :     euler  )      depthArr  .  push_back  (  level  [  x  ]);   }   int     LCA  (  int     u    int     v  )   {      // trivial case      if     (  u  ==  v  )      return     u  ;      if     (  FAI  [  u  ]     >     FAI  [  v  ])      swap  (  u    v  );      // doing RMQ in the required range      return     euler  [  query  (  FAI  [  u  ]     FAI  [  v  ])];   }   void     addEdge  (  int     u    int     v  )   {      adj  [  u  ].  push_back  (  v  );      adj  [  v  ].  push_back  (  u  );   }   int     main  (  int     argc       char     const     *  argv  [])   {      // constructing the described tree      int     numberOfNodes     =     8  ;      addEdge  (  1    2  );      addEdge  (  1    3  );      addEdge  (  2    4  );      addEdge  (  2    5  );      addEdge  (  2    6  );      addEdge  (  3    7  );      addEdge  (  3    8  );      // performing required precalculations      preprocess  ();      // doing the Euler walk      ptr     =     0  ;      memset  (  FAI    -1    sizeof  (  FAI  ));      dfs  (  1    0    0  );      // creating depthArray corresponding to euler[]      makeArr  ();      // building sparse table      buildSparseTable  (  depthArr  .  size  ());      cout      < <     'LCA(67) : '      < <     LCA  (  6    7  )      < <     '  n  '  ;      cout      < <     'LCA(64) : '      < <     LCA  (  6    4  )      < <     '  n  '  ;      return     0  ;   }   
Java
   // Java program to demonstrate LCA of n-ary   // tree in constant time.   import     java.util.ArrayList  ;   import     java.util.Arrays  ;   class   GFG  {   static     int     sz     =     101  ;   @SuppressWarnings  (  'unchecked'  )   // Stores the tree   static     ArrayList   <  Integer  >[]     adj     =     new     ArrayList  [  sz  ]  ;      // Tracks the eulerwalk   static     ArrayList   <  Integer  >     euler     =     new     ArrayList   <>  ();      // Depth for each node corresponding   static     ArrayList   <  Integer  >     depthArr     =     new     ArrayList   <>  ();      // to eulerwalk   // Stores first appearance index of every node   static     int  []     FAI     =     new     int  [  sz  ]  ;      // Stores depth for all nodes in the tree   static     int  []     level     =     new     int  [  sz  ]  ;      // Pointer to euler walk   static     int     ptr  ;   // Sparse table   static     int  [][]     dp     =     new     int  [  sz  ][  18  ]  ;   // Stores log values   static     int  []     logn     =     new     int  [  sz  ]  ;   // Stores power of 2   static     int  []     p2     =     new     int  [  20  ]  ;   static     void     buildSparseTable  (  int     n  )   {          // Initializing sparse table      for  (  int     i     =     0  ;     i      <     sz  ;     i  ++  )      {      for  (  int     j     =     0  ;     j      <     18  ;     j  ++  )         {      dp  [  i  ][  j  ]     =     -  1  ;      }      }      // Filling base case values      for  (  int     i     =     1  ;     i      <     n  ;     i  ++  )      dp  [  i     -     1  ][  0  ]     =     (  depthArr  .  get  (  i  )     >         depthArr  .  get  (  i     -     1  ))     ?         i     -     1     :     i  ;      // dp to fill sparse table      for  (  int     l     =     1  ;     l      <     15  ;     l  ++  )      for  (  int     i     =     0  ;     i      <     n  ;     i  ++  )      if     (  dp  [  i  ][  l     -     1  ]     !=     -  1     &&      dp  [  i     +     p2  [  l     -     1  ]][  l     -     1  ]     !=     -  1  )      dp  [  i  ][  l  ]     =     (  depthArr  .  get  (  dp  [  i  ][  l     -     1  ]  )     >      depthArr  .  get  (      dp  [  i     +     p2  [  l     -     1  ]][  l     -     1  ]  ))     ?         dp  [  i     +     p2  [  l     -     1  ]][  l     -     1  ]     :         dp  [  i  ][  l     -     1  ]  ;      else      break  ;   }   static     int     query  (  int     l       int     r  )      {      int     d     =     r     -     l  ;      int     dx     =     logn  [  d  ]  ;          if     (  l     ==     r  )      return     l  ;          if     (  depthArr  .  get  (  dp  [  l  ][  dx  ]  )     >         depthArr  .  get  (  dp  [  r     -     p2  [  dx  ]][  dx  ]  ))      return     dp  [  r     -     p2  [  dx  ]][  dx  ]  ;      else      return     dp  [  l  ][  dx  ]  ;   }   static     void     preprocess  ()      {          // Memorizing powers of 2      p2  [  0  ]     =     1  ;      for  (  int     i     =     1  ;     i      <     18  ;     i  ++  )      p2  [  i  ]     =     p2  [  i     -     1  ]     *     2  ;      // Memorizing all log(n) values      int     val     =     1       ptr     =     0  ;      for  (  int     i     =     1  ;     i      <     sz  ;     i  ++  )         {      logn  [  i  ]     =     ptr     -     1  ;      if     (  val     ==     i  )         {      val     *=     2  ;      logn  [  i  ]     =     ptr  ;      ptr  ++  ;      }      }   }   // Euler Walk ( preorder traversal) converting   // tree to linear depthArray    // Time Complexity : O(n)   static     void     dfs  (  int     cur       int     prev       int     dep  )   {          // Marking FAI for cur node      if     (  FAI  [  cur  ]     ==     -  1  )      FAI  [  cur  ]     =     ptr  ;      level  [  cur  ]     =     dep  ;      // Pushing root to euler walk      euler  .  add  (  cur  );      // Incrementing euler walk pointer      ptr  ++  ;      for  (  Integer     x     :     adj  [  cur  ]  )      {      if     (  x     !=     prev  )      {      dfs  (  x       cur       dep     +     1  );      // Pushing cur again in backtrack      // of euler walk      euler  .  add  (  cur  );      // Increment euler walk pointer      ptr  ++  ;      }      }   }   // Create Level depthArray corresponding   // to the Euler walk Array   static     void     makeArr  ()   {      for  (  Integer     x     :     euler  )      depthArr  .  add  (  level  [  x  ]  );   }   static     int     LCA  (  int     u       int     v  )      {          // Trivial case      if     (  u     ==     v  )      return     u  ;      if     (  FAI  [  u  ]     >     FAI  [  v  ]  )      {      int     temp     =     u  ;      u     =     v  ;      v     =     temp  ;      }      // Doing RMQ in the required range      return     euler  .  get  (  query  (  FAI  [  u  ]       FAI  [  v  ]  ));   }   static     void     addEdge  (  int     u       int     v  )   {      adj  [  u  ]  .  add  (  v  );      adj  [  v  ]  .  add  (  u  );   }   // Driver code   public     static     void     main  (  String  []     args  )   {      for  (  int     i     =     0  ;     i      <     sz  ;     i  ++  )      {      adj  [  i  ]     =     new     ArrayList   <>  ();      }          // Constructing the described tree      int     numberOfNodes     =     8  ;      addEdge  (  1       2  );      addEdge  (  1       3  );      addEdge  (  2       4  );      addEdge  (  2       5  );      addEdge  (  2       6  );      addEdge  (  3       7  );      addEdge  (  3       8  );      // Performing required precalculations      preprocess  ();      // Doing the Euler walk      ptr     =     0  ;      Arrays  .  fill  (  FAI       -  1  );      dfs  (  1       0       0  );      // Creating depthArray corresponding to euler[]      makeArr  ();          // Building sparse table      buildSparseTable  (  depthArr  .  size  ());      System  .  out  .  println  (  'LCA(67) : '     +     LCA  (  6       7  ));      System  .  out  .  println  (  'LCA(64) : '     +     LCA  (  6       4  ));   }   }   // This code is contributed by sanjeev2552   
Python3
   # Python program to demonstrate LCA of n-ary tree   # in constant time.   from   typing   import   List   # stores the tree   adj   =   [[]   for   _   in   range  (  101  )]   # tracks the eulerwalk   euler   =   []   # depth for each node corresponding to eulerwalk   depthArr   =   []   # stores first appearance index of every node   FAI   =   [  -  1  ]   *   101   # stores depth for all nodes in the tree   level   =   [  0  ]   *   101   # pointer to euler walk   ptr   =   0   # sparse table   dp   =   [[  -  1  ]   *   18   for   _   in   range  (  101  )]   # stores log values   logn   =   [  0  ]   *   101   # stores power of 2   p2   =   [  0  ]   *   20   def   buildSparseTable  (  n  :   int  ):   # initializing sparse table   for   i   in   range  (  n  ):   dp  [  i  ][  0  ]   =   i  -  1   if   depthArr  [  i  ]   >   depthArr  [  i  -  1  ]   else   i   # dp to fill sparse table   for   l   in   range  (  1     15  ):   for   i   in   range  (  n  ):   if   dp  [  i  ][  l  -  1  ]   !=   -  1   and   dp  [  i  +  p2  [  l  -  1  ]][  l  -  1  ]   !=   -  1  :   dp  [  i  ][  l  ]   =   dp  [  i  +  p2  [  l  -  1  ]][  l  -  1  ]   if   depthArr  [  dp  [  i  ][  l  -  1  ]   ]   >   depthArr  [  dp  [  i  +  p2  [  l  -  1  ]][  l  -  1  ]]   else   dp  [  i  ][  l  -  1  ]   else  :   break   def   query  (  l  :   int     r  :   int  )   ->   int  :   d   =   r  -  l   dx   =   logn  [  d  ]   if   l   ==   r  :   return   l   if   depthArr  [  dp  [  l  ][  dx  ]]   >   depthArr  [  dp  [  r  -  p2  [  dx  ]][  dx  ]]:   return   dp  [  r  -  p2  [  dx  ]][  dx  ]   else  :   return   dp  [  l  ][  dx  ]   def   preprocess  ():   global   ptr   # memorizing powers of 2   p2  [  0  ]   =   1   for   i   in   range  (  1     18  ):   p2  [  i  ]   =   p2  [  i  -  1  ]  *  2   # memorizing all log(n) values   val   =   1   ptr   =   0   for   i   in   range  (  1     101  ):   logn  [  i  ]   =   ptr  -  1   if   val   ==   i  :   val   *=   2   logn  [  i  ]   =   ptr   ptr   +=   1   def   dfs  (  cur  :   int     prev  :   int     dep  :   int  ):   global   ptr   # marking FAI for cur node   if   FAI  [  cur  ]   ==   -  1  :   FAI  [  cur  ]   =   ptr   level  [  cur  ]   =   dep   # pushing root to euler walk   euler  .  append  (  cur  )   # incrementing euler walk pointer   ptr   +=   1   for   x   in   adj  [  cur  ]:   if   x   !=   prev  :   dfs  (  x     cur     dep  +  1  )   # pushing cur again in backtrack   # of euler walk   euler  .  append  (  cur  )   # increment euler walk pointer   ptr   +=   1   # Create Level depthArray corresponding   # to the Euler walk Array   def   makeArr  ():   global   depthArr   for   x   in   euler  :   depthArr  .  append  (  level  [  x  ])   def   LCA  (  u  :   int     v  :   int  )   ->   int  :   # trivial case   if   u   ==   v  :   return   u   if   FAI  [  u  ]   >   FAI  [  v  ]:   u     v   =   v     u   # doing RMQ in the required range   return   euler  [  query  (  FAI  [  u  ]   FAI  [  v  ])]   def   addEdge  (  u     v  ):   adj  [  u  ]  .  append  (  v  )   adj  [  v  ]  .  append  (  u  )   # constructing the described tree   numberOfNodes   =   8   addEdge  (  1     2  )   addEdge  (  1     3  )   addEdge  (  2     4  )   addEdge  (  2     5  )   addEdge  (  2     6  )   addEdge  (  3     7  )   addEdge  (  3     8  )   # performing required precalculations   preprocess  ()   # doing the Euler walk   ptr   =   0   FAI   =   [  -  1  ]   *   (  numberOfNodes   +   1  )   dfs  (  1     0     0  )   # creating depthArray corresponding to euler[]   makeArr  ()   # building sparse table   buildSparseTable  (  len  (  depthArr  ))   print  (  'LCA(67) : '     LCA  (  6     7  ))   print  (  'LCA(64) : '     LCA  (  6     4  ))   
C#
   // C# program to demonstrate LCA of n-ary   // tree in constant time.   using     System  ;   using     System.Collections.Generic  ;   public     class     GFG     {      static     int     sz     =     101  ;      // Stores the tree      static     List   <  int  >  []     adj     =     new     List   <  int  >  [  sz  ];          // Tracks the eulerwalk      static     List   <  int  >     euler     =     new     List   <  int  >  ();          // Depth for each node corresponding      static     List   <  int  >     depthArr     =     new     List   <  int  >  ();          // to eulerwalk      // Stores first appearance index of every node      static     int  []     FAI     =     new     int  [  sz  ];          // Stores depth for all nodes in the tree      static     int  []     level     =     new     int  [  sz  ];          // Pointer to euler walk      static     int     ptr  ;          // Sparse table      static     int  []     dp     =     new     int  [  sz       18  ];          // Stores log values      static     int  []     logn     =     new     int  [  sz  ];          // Stores power of 2      static     int  []     p2     =     new     int  [  20  ];          static     void     buildSparseTable  (  int     n  )      {      // Initializing sparse table      for  (  int     i     =     0  ;     i      <     sz  ;     i  ++  )      {      for  (  int     j     =     0  ;     j      <     18  ;     j  ++  )         {      dp  [  i    j  ]     =     -  1  ;      }      }          // Filling base case values      for  (  int     i     =     1  ;     i      <     n  ;     i  ++  )      dp  [  i     -     1    0  ]     =     (  depthArr  [  i  ]     >     depthArr  [  i     -     1  ])     ?     i     -     1     :     i  ;          // dp to fill sparse table      for  (  int     l     =     1  ;     l      <     15  ;     l  ++  )      for  (  int     i     =     0  ;     i      <     n  ;     i  ++  )      if     (  dp  [  i    l     -     1  ]     !=     -  1     &&     dp  [  i     +     p2  [  l     -     1  ]  l     -     1  ]     !=     -  1  )      dp  [  i    l  ]     =     (  depthArr  [  dp  [  i    l     -     1  ]]     >     depthArr  [  dp  [  i     +     p2  [  l     -     1  ]  l     -     1  ]])     ?     dp  [  i     +     p2  [  l     -     1  ]  l     -     1  ]     :     dp  [  i    l     -     1  ];      else      break  ;      }          static     int     query  (  int     l       int     r  )         {      int     d     =     r     -     l  ;      int     dx     =     logn  [  d  ];          if     (  l     ==     r  )      return     l  ;          if     (  depthArr  [  dp  [  l    dx  ]]     >     depthArr  [  dp  [  r     -     p2  [  dx  ]  dx  ]])      return     dp  [  r     -     p2  [  dx  ]  dx  ];      else      return     dp  [  l    dx  ];      }          static     void     preprocess  ()         {      // Memorizing powers of 2      p2  [  0  ]     =     1  ;      for  (  int     i     =     1  ;     i      <     18  ;     i  ++  )      p2  [  i  ]     =     p2  [  i     -     1  ]     *     2  ;          // Memorizing all log(n) values      int     val     =     1       ptr     =     0  ;      for  (  int     i     =     1  ;     i      <     sz  ;     i  ++  )         {      logn  [  i  ]     =     ptr     -     1  ;      if     (  val     ==     i  )         {      val     *=     2  ;      logn  [  i  ]     =     ptr  ;      ptr  ++  ;      }      }      }          // Euler Walk ( preorder traversal) converting      // tree to linear depthArray       // Time Complexity : O(n)      static     void     dfs  (  int     cur       int     prev       int     dep  )      {      // Marking FAI for cur node      if     (  FAI  [  cur  ]     ==     -  1  )      FAI  [  cur  ]     =     ptr  ;          level  [  cur  ]     =     dep  ;          // Pushing root to euler walk      euler  .  Add  (  cur  );          // Incrementing euler walk pointer      ptr  ++  ;          foreach     (  int     x     in     adj  [  cur  ])      {      if     (  x     !=     prev  )      {      dfs  (  x       cur       dep     +     1  );          euler  .  Add  (  cur  );          ptr  ++  ;      }      }      }          // Create Level depthArray corresponding      // to the Euler walk Array      static     void     makeArr  ()      {      foreach     (  int     x     in     euler  )      depthArr  .  Add  (  level  [  x  ]);      }          static     int     LCA  (  int     u       int     v  )         {      // Trivial case      if     (  u     ==     v  )      return     u  ;          if     (  FAI  [  u  ]     >     FAI  [  v  ])      {      int     temp     =     u  ;      u     =     v  ;      v     =     temp  ;      }          // Doing RMQ in the required range      return     euler  [  query  (  FAI  [  u  ]     FAI  [  v  ])];      }          static     void     addEdge  (  int     u       int     v  )      {      adj  [  u  ].  Add  (  v  );      adj  [  v  ].  Add  (  u  );      }      // Driver Code      static     void     Main  (  string  []     args  )      {      int     sz     =     9  ;      adj     =     new     List   <  int  >  [  sz  ];      for     (  int     i     =     0  ;     i      <     sz  ;     i  ++  )      {      adj  [  i  ]     =     new     List   <  int  >  ();      }      // Constructing the described tree      int     numberOfNodes     =     8  ;      addEdge  (  1       2  );      addEdge  (  1       3  );      addEdge  (  2       4  );      addEdge  (  2       5  );      addEdge  (  2       6  );      addEdge  (  3       7  );      addEdge  (  3       8  );      // Performing required precalculations      preprocess  ();      // Doing the Euler walk      ptr     =     0  ;      Array  .  Fill  (  FAI       -  1  );      dfs  (  1       0       0  );      // Creating depthArray corresponding to euler[]      makeArr  ();      // Building sparse table      buildSparseTable  (  depthArr  .  Count  );      Console  .  WriteLine  (  'LCA(67) : '     +     LCA  (  6       7  ));      Console  .  WriteLine  (  'LCA(64) : '     +     LCA  (  6       4  ));      }       }   // This code is contributed by Prince Kumar   
JavaScript
   let     adj     =     [];   for     (  let     _     =     0  ;     _      <     101  ;     _  ++  )     {      adj  .  push  ([]);   }   // tracks the eulerwalk   let     euler     =     [];   // depth for each node corresponding to eulerwalk   let     depthArr     =     [];   // stores first appearance index of every node   let     FAI     =     new     Array  (  101  ).  fill  (  -  1  );   // stores depth for all nodes in the tree   let     level     =     new     Array  (  101  ).  fill  (  0  );   // pointer to euler walk   let     ptr     =     0  ;   // sparse table   let     dp     =     [];   for     (  let     _     =     0  ;     _      <     101  ;     _  ++  )     {      dp  .  push  (  new     Array  (  18  ).  fill  (  -  1  ));   }   // stores log values   let     logn     =     new     Array  (  101  ).  fill  (  0  );   // stores power of 2   let     p2     =     new     Array  (  20  ).  fill  (  0  );   function     buildSparseTable  (  n  )   {      // initializing sparse table      for     (  let     i     =     0  ;     i      <     n  ;     i  ++  )     {      dp  [  i  ][  0  ]     =     i     -     1     >=     0     &&     depthArr  [  i  ]     >     depthArr  [  i     -     1  ]     ?     i     -     1     :     i  ;      }      // dp to fill sparse table      for     (  let     l     =     1  ;     l      <     15  ;     l  ++  )     {      for     (  let     i     =     0  ;     i      <     n  ;     i  ++  )     {      if     (      dp  [  i  ][  l     -     1  ]     !==     -  1     &&      dp  [  i     +     p2  [  l     -     1  ]][  l     -     1  ]     !==     -  1      )     {      dp  [  i  ][  l  ]     =      depthArr  [  dp  [  i  ][  l     -     1  ]]     >      depthArr  [  dp  [  i     +     p2  [  l     -     1  ]][  l     -     1  ]]      ?     dp  [  i     +     p2  [  l     -     1  ]][  l     -     1  ]      :     dp  [  i  ][  l     -     1  ];      }     else     {      break  ;      }      }      }   }   function     query  (  l       r  )     {      let     d     =     r     -     l  ;      let     dx     =     logn  [  d  ];      if     (  l     ===     r  )     {      return     l  ;      }      if     (  depthArr  [  dp  [  l  ][  dx  ]]     >     depthArr  [  dp  [  r     -     p2  [  dx  ]][  dx  ]])     {      return     dp  [  r     -     p2  [  dx  ]][  dx  ];      }     else     {      return     dp  [  l  ][  dx  ];      }   }   function     preprocess  ()     {      // memorizing powers of 2      p2  [  0  ]     =     1  ;      for     (  let     i     =     1  ;     i      <     18  ;     i  ++  )     {      p2  [  i  ]     =     p2  [  i     -     1  ]     *     2  ;      }      // memorizing all log(n) values      let     val     =     1  ;      ptr     =     0  ;      for     (  let     i     =     1  ;     i      <     101  ;     i  ++  )     {      logn  [  i  ]     =     ptr     -     1  ;      if     (  val     ===     i  )     {      val     *=     2  ;      logn  [  i  ]     =     ptr  ;      ptr     +=     1  ;      }      }   }   function     dfs  (  cur       prev       dep  )     {      // marking FAI for cur node      if     (  FAI  [  cur  ]     ===     -  1  )     {      FAI  [  cur  ]     =     ptr  ;      }      level  [  cur  ]     =     dep  ;      // pushing root to euler walk      euler  .  push  (  cur  );      // incrementing euler walk pointer      ptr     +=     1  ;      for     (  let     x     of     adj  [  cur  ])     {      if     (  x     !==     prev  )     {      dfs  (  x       cur       dep     +     1  );      // pushing cur again in backtrack      // of euler walk      euler  .  push  (  cur  );      // increment euler walk pointer      ptr     +=     1  ;      }      }   }   // Create Level depthArray corresponding   // to the Euler walk Array   function     makeArr  ()     {      for     (  let     x     of     euler  )     {      depthArr  .  push  (  level  [  x  ]);      }   }   function     LCA  (  u       v  )     {      // trivial case      if     (  u     ===     v  )     {      return     u  ;      }      if     (  FAI  [  u  ]     >     FAI  [  v  ])     {      [  u       v  ]     =     [  v       u  ];      }      // doing RMQ in the required range      return     euler  [  query  (  FAI  [  u  ]     FAI  [  v  ])];   }   function     addEdge  (  u       v  )     {      adj  [  u  ].  push  (  v  );      adj  [  v  ].  push  (  u  );   }   // constructing the described tree   let     numberOfNodes     =     8  ;   addEdge  (  1       2  );   addEdge  (  1       3  );   addEdge  (  2       4  );   addEdge  (  2       5  );   addEdge  (  2       6  );   addEdge  (  3       7  );   addEdge  (  3       8  );   // performing required precalculations   preprocess  ();   // doing the Euler walk   ptr     =     0  ;   FAI     =     new     Array  (  numberOfNodes     +     1  ).  fill  (  -  1  );   dfs  (  1       0       0  );   // creating depthArray corresponding to euler[]   makeArr  ();   // building sparse table   buildSparseTable  (  depthArr  .  length  );   console  .  log  (  'LCA(67) : '       LCA  (  6       7  ));   console  .  log  (  'LCA(64) : '       LCA  (  6       4  ));   

תְפוּקָה
LCA(67) : 1 LCA(64) : 2 

הערה: אנו מחשבים מראש את כל ההספק הנדרש של 2 וגם מחשבים מראש את כל ערכי היומן הנדרשים כדי להבטיח מורכבות זמן קבועה לכל שאילתה. אחרת אם עשינו חישוב יומן עבור כל פעולת שאילתה, מורכבות הזמן שלנו לא הייתה קבועה.

מורכבות זמן: תהליך ההמרה מ-LCA ל-RMQ נעשה על ידי אוילר ווק שלוקח עַל) זְמַן. 
עיבוד מקדים לטבלה הדלילה ב-RMQ לוקח זמן O(nlogn) ומענה על כל שאילתה הוא תהליך זמן קבוע. לכן מורכבות הזמן הכוללת היא O(nlogn) - עיבוד מקדים ו O(1) עבור כל שאילתה.

מרחב עזר: O(n+s)

 

צור חידון