LCA för n-ärt träd | Konstant fråga O(1)
Vi har sett olika metoder med olika tidskomplexiteter för att beräkna LCA i n-ärt träd:-
Metod 1: Naiv metod (genom att beräkna rot till nod-väg) | O(n) per fråga
Metod 2: Använda Sqrt Decomposition | O(sqrt H)
Metod 3: Använder Sparse Matrix DP-metod | O(logga)
Låt oss studera en annan metod som har snabbare frågetid än alla ovanstående metoder. Så vårt mål kommer att vara att beräkna LCA in konstant tid ~ O(1) . Låt oss se hur vi kan uppnå det.
Metod 4: Använda intervallminimumfråga
Vi har diskuterat LCA och RMQ för binärt träd . Här diskuterar vi LCA-problem till RMQ-problemkonvertering för n-ärt träd.
Pre-requisites:- LCA in Binary Tree using RMQ RMQ using sparse table
Nyckelkoncept: I den här metoden kommer vi att reducera vårt LCA-problem till RMQ(Range Minimum Query)-problem över en statisk array. När vi väl har gjort det kommer vi att relatera minimiintervallsfrågorna till de obligatoriska LCA-frågorna.
Det första steget är att bryta ner trädet till en platt linjär array. För att göra detta kan vi tillämpa Euler-vandringen. Euler-vandringen ger förbeställningens genomgång av grafen. Så vi kommer att utföra en Euler Walk på trädet och lagra noderna i en array när vi besöker dem. Denna process reducerar trädet >
Låt oss nu tänka i allmänna termer: Betrakta vilka två noder som helst på trädet. Det kommer att finnas exakt en väg som förbinder både noderna och den nod som har det minsta djupvärdet i vägen kommer att vara LCA för de två givna noderna.
Ta nu valfri två distinkta noder säger i och v i Eulers promenadfält. Nu kommer alla element i vägen från u till v att ligga mellan indexet för noderna u och v i Euler-gångmatrisen. Därför behöver vi bara beräkna noden med minsta djup mellan index för nod u och nod v i euler-matrisen.
För detta kommer vi att upprätthålla en annan array som kommer att innehålla djupet av alla noder som motsvarar deras position i Euler walk array så att vi kan tillämpa vår RMQ-algoritm på den.
Nedan ges euler-vandringsarrayen parallell med dess djupspårarray.
Exempel: - Betrakta två noder nod 6 och nod 7 i Euler-arrayen. För att beräkna LCA för nod 6 och nod 7 tittar vi på det minsta djupvärdet för alla noder mellan nod 6 och nod 7.
Därför nod 1 har den minsta djupvärde = 0 och därför är det LCA för nod 6 och nod 7.
Genomförande:-
We will be maintaining three arrays 1) Euler Path 2) Depth array 3) First Appearance Index
Euler Path och Depth array är samma som beskrivits ovan
First Appearance Index FAI[] : First Appearance index Array kommer att lagra indexet för den första positionen för varje nod i Euler Path arrayen. FAI[i] = Första uppkomsten av en nod i Euler Walk-array.
Implementeringen av ovanstående metod ges nedan:-
Genomförande:
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 ));
Produktion
LCA(67) : 1 LCA(64) : 2
Obs: Vi förberäknar all erforderlig effekt av 2:or och förberäknar också alla nödvändiga loggvärden för att säkerställa konstant tidskomplexitet per fråga. Om vi annars gjorde loggberäkningar för varje frågeoperation skulle vår tidskomplexitet inte ha varit konstant.
Tidskomplexitet: Konverteringsprocessen från LCA till RMQ görs av Euler Walk som tar På) tid.
Förbearbetning av den glesa tabellen i RMQ tar O(nlogn) tid och att svara på varje fråga är en konstant tidsprocess. Därför är övergripande tidskomplexitet O(nlogn) - förbearbetning och O(1) för varje fråga.
Hjälputrymme: O(n+s)
Skapa frågesport