11package com .thealgorithms .tree ;
22
3+ import java .util .ArrayList ;
4+ import java .util .List ;
5+
36/**
47 * Heavy-Light Decomposition (HLD) implementation in Java.
58 *
6- * HLD is used to efficiently handle path queries on trees, such as maximum, sum, or updates.
7- * It decomposes the tree into heavy and light chains, enabling queries in O(log N) time.
9+ * HLD is used to efficiently handle path queries on trees, such as maximum,
10+ * sum, or updates. It decomposes the tree into heavy and light chains,
11+ * enabling queries in O(log N) time.
812 *
913 * Wikipedia Reference: https://en.wikipedia.org/wiki/Heavy-light_decomposition
1014 *
1115 * Author: Nithin U.
1216 * Github: https://github.com/NithinU2802
13- *
1417 */
1518
16- import java .util .ArrayList ;
17- import java .util .List ;
19+ public class HeavyLightDecomposition {
20+ private List <Integer >[] tree ;
21+ private int [] parent , depth , subtreeSize , chainHead , position , nodeValue ;
22+ private int [] segmentTree ;
23+ private int positionIndex ;
1824
19- public class HeavyLightDecomposition {
20- private List <Integer >[] tree ;
21- private int [] parent , depth , subtreeSize , chainHead , position , nodeValue ;
22- private int [] segmentTree ;
23- private int positionIndex ;
25+ @ SuppressWarnings ("unchecked" )
26+ public HeavyLightDecomposition (int n ) {
27+ tree = new ArrayList [n + 1 ];
28+ parent = new int [n + 1 ];
29+ depth = new int [n + 1 ];
30+ subtreeSize = new int [n + 1 ];
31+ chainHead = new int [n + 1 ];
32+ position = new int [n + 1 ];
33+ nodeValue = new int [n + 1 ];
34+ segmentTree = new int [4 * (n + 1 )];
35+
36+ for (int i = 0 ; i <= n ; i ++) {
37+ tree [i ] = new ArrayList <>();
38+ chainHead [i ] = -1 ;
39+ }
40+ positionIndex = 0 ;
41+ }
2442
25- public int getPosition (int index ){
43+ public int getPosition (int index ) {
2644 return position [index ];
27- }
45+ }
2846
29- public int getPositionIndex (){
47+ public int getPositionIndex () {
3048 return positionIndex ;
31- }
32-
33- @ SuppressWarnings ("unchecked" )
34- public HeavyLightDecomposition (int n ) {
35- tree = new ArrayList [n + 1 ]; // Causes "unchecked or unsafe operations" warning
36- parent = new int [n + 1 ];
37- depth = new int [n + 1 ];
38- subtreeSize = new int [n + 1 ];
39- chainHead = new int [n + 1 ];
40- position = new int [n + 1 ];
41- nodeValue = new int [n + 1 ];
42- segmentTree = new int [4 * (n + 1 )];
49+ }
50+
51+ public void addEdge (int u , int v ) {
52+ tree [u ].add (v );
53+ tree [v ].add (u );
54+ }
55+
56+ private void dfsSize (int node , int parentNode ) {
57+ parent [node ] = parentNode ;
58+ subtreeSize [node ] = 1 ;
59+
60+ for (int child : tree [node ]) {
61+ if (child != parentNode ) {
62+ depth [child ] = depth [node ] + 1 ;
63+ dfsSize (child , node );
64+ subtreeSize [node ] += subtreeSize [child ];
65+ }
66+ }
67+ }
68+
69+ private void decompose (int node , int head ) {
70+ chainHead [node ] = head ;
71+ position [node ] = positionIndex ++;
72+
73+ int heavyChild = -1 , maxSubtreeSize = -1 ;
74+ for (int child : tree [node ]) {
75+ if (child != parent [node ] && subtreeSize [child ] > maxSubtreeSize ) {
76+ heavyChild = child ;
77+ maxSubtreeSize = subtreeSize [child ];
78+ }
79+ }
80+
81+ if (heavyChild != -1 ) {
82+ decompose (heavyChild , head );
83+ }
84+
85+ for (int child : tree [node ]) {
86+ if (child != parent [node ] && child != heavyChild ) {
87+ decompose (child , child );
88+ }
89+ }
90+ }
91+
92+ private void buildSegmentTree (int node , int start , int end ) {
93+ if (start == end ) {
94+ segmentTree [node ] = nodeValue [start ];
95+ return ;
96+ }
97+ int mid = (start + end ) / 2 ;
98+ buildSegmentTree (2 * node , start , mid );
99+ buildSegmentTree (2 * node + 1 , mid + 1 , end );
100+ segmentTree [node ] = Math .max (segmentTree [2 * node ], segmentTree [2 * node + 1 ]);
101+ }
102+
103+ public void updateSegmentTree (int node , int start , int end , int index , int value ) {
104+ if (start == end ) {
105+ segmentTree [node ] = value ;
106+ return ;
107+ }
108+ int mid = (start + end ) / 2 ;
109+ if (index <= mid ) {
110+ updateSegmentTree (2 * node , start , mid , index , value );
111+ } else {
112+ updateSegmentTree (2 * node + 1 , mid + 1 , end , index , value );
113+ }
114+ segmentTree [node ] = Math .max (segmentTree [2 * node ], segmentTree [2 * node + 1 ]);
115+ }
116+
117+ public int querySegmentTree (int node , int start , int end , int left , int right ) {
118+ if (left > end || right < start ) {
119+ return Integer .MIN_VALUE ;
120+ }
121+ if (left <= start && end <= right ) {
122+ return segmentTree [node ];
123+ }
124+ int mid = (start + end ) / 2 ;
125+ int leftQuery = querySegmentTree (2 * node , start , mid , left , right );
126+ int rightQuery = querySegmentTree (2 * node + 1 , mid + 1 , end , left , right );
127+ return Math .max (leftQuery , rightQuery );
128+ }
129+
130+ public int queryMaxInPath (int u , int v ) {
131+ int result = Integer .MIN_VALUE ;
132+ while (chainHead [u ] != chainHead [v ]) {
133+ if (depth [chainHead [u ]] < depth [chainHead [v ]]) {
134+ int temp = u ;
135+ u = v ;
136+ v = temp ;
137+ }
138+ result = Math .max (result , querySegmentTree (1 , 0 , positionIndex - 1 , position [chainHead [u ]], position [u ]));
139+ u = parent [chainHead [u ]];
140+ }
141+ if (depth [u ] > depth [v ]) {
142+ int temp = u ;
143+ u = v ;
144+ v = temp ;
145+ }
146+ result = Math .max (result , querySegmentTree (1 , 0 , positionIndex - 1 , position [u ], position [v ]));
147+ return result ;
148+ }
43149
44- for (int i = 0 ; i <= n ; i ++) {
45- tree [i ] = new ArrayList <>();
46- chainHead [i ] = -1 ;
47- }
48- positionIndex = 0 ;
49- }
50-
51- /**
52- * Adds an edge to the tree.
53- */
54- public void addEdge (int u , int v ) {
55- tree [u ].add (v );
56- tree [v ].add (u );
57- }
58-
59- /**
60- * First DFS to calculate subtree sizes and determine heavy children.
61- */
62- private void dfsSize (int node , int parentNode ) {
63- parent [node ] = parentNode ;
64- subtreeSize [node ] = 1 ;
65-
66- for (int child : tree [node ]) {
67- if (child != parentNode ) {
68- depth [child ] = depth [node ] + 1 ;
69- dfsSize (child , node );
70- subtreeSize [node ] += subtreeSize [child ];
71- }
72- }
73- }
74-
75- /**
76- * Second DFS to perform Heavy-Light Decomposition.
77- */
78- private void decompose (int node , int head ) {
79- chainHead [node ] = head ;
80- position [node ] = positionIndex ++;
81-
82- int heavyChild = -1 , maxSubtreeSize = -1 ;
83-
84- for (int child : tree [node ]) {
85- if (child != parent [node ] && subtreeSize [child ] > maxSubtreeSize ) {
86- heavyChild = child ;
87- maxSubtreeSize = subtreeSize [child ];
88- }
89- }
90-
91- if (heavyChild != -1 ) {
92- decompose (heavyChild , head );
93- }
94-
95- for (int child : tree [node ]) {
96- if (child != parent [node ] && child != heavyChild ) {
97- decompose (child , child );
98- }
99- }
100- }
101-
102- /**
103- * Builds a Segment Tree to handle path queries efficiently.
104- */
105- private void buildSegmentTree (int node , int start , int end ) {
106- if (start == end ) {
107- segmentTree [node ] = nodeValue [start ];
108- return ;
109- }
110-
111- int mid = (start + end ) / 2 ;
112- buildSegmentTree (2 * node , start , mid );
113- buildSegmentTree (2 * node + 1 , mid + 1 , end );
114-
115- segmentTree [node ] = Math .max (segmentTree [2 * node ], segmentTree [2 * node + 1 ]);
116- }
117-
118- /**
119- * Updates a node's value in the Segment Tree.
120- */
121- public void updateSegmentTree (int node , int start , int end , int index , int value ) {
122- if (start == end ) {
123- segmentTree [node ] = value ;
124- return ;
125- }
126-
127- int mid = (start + end ) / 2 ;
128- if (index <= mid ) {
129- updateSegmentTree (2 * node , start , mid , index , value );
130- } else {
131- updateSegmentTree (2 * node + 1 , mid + 1 , end , index , value );
132- }
133-
134- segmentTree [node ] = Math .max (segmentTree [2 * node ], segmentTree [2 * node + 1 ]);
135- }
136-
137- /**
138- * Queries the Segment Tree for the maximum value in a given range.
139- */
140- public int querySegmentTree (int node , int start , int end , int left , int right ) {
141- if (left > end || right < start ) {
142- return Integer .MIN_VALUE ;
143- }
144-
145- if (left <= start && end <= right ) {
146- return segmentTree [node ];
147- }
148-
149- int mid = (start + end ) / 2 ;
150- int leftQuery = querySegmentTree (2 * node , start , mid , left , right );
151- int rightQuery = querySegmentTree (2 * node + 1 , mid + 1 , end , left , right );
152-
153- return Math .max (leftQuery , rightQuery );
154- }
155-
156- /**
157- * Queries the maximum value in the path from node u to node v.
158- */
159- public int queryMaxInPath (int u , int v ) {
160- int result = Integer .MIN_VALUE ;
161-
162- while (chainHead [u ] != chainHead [v ]) {
163- if (depth [chainHead [u ]] < depth [chainHead [v ]]) {
164- int temp = u ;
165- u = v ;
166- v = temp ;
167- }
168-
169- result = Math .max (result , querySegmentTree (1 , 0 , positionIndex - 1 , position [chainHead [u ]], position [u ]));
170- u = parent [chainHead [u ]];
171- }
172-
173- if (depth [u ] > depth [v ]) {
174- int temp = u ;
175- u = v ;
176- v = temp ;
177- }
178-
179- result = Math .max (result , querySegmentTree (1 , 0 , positionIndex - 1 , position [u ], position [v ]));
180- return result ;
181- }
182-
183- /**
184- * Initializes the HLD structure and Segment Tree.
185- */
186- public void initialize (int root , int [] values ) {
187- dfsSize (root , -1 );
188- decompose (root , root );
189- for (int i = 0 ; i < values .length ; i ++) {
190- nodeValue [position [i ]] = values [i ];
191- }
192- buildSegmentTree (1 , 0 , positionIndex - 1 );
193- }
194-
195- }
196-
150+ public void initialize (int root , int [] values ) {
151+ dfsSize (root , -1 );
152+ decompose (root , root );
153+ for (int i = 0 ; i < values .length ; i ++) {
154+ nodeValue [position [i ]] = values [i ];
155+ }
156+ buildSegmentTree (1 , 0 , positionIndex - 1 );
157+ }
158+ }
0 commit comments