33import java .util .ArrayList ;
44import java .util .Arrays ;
55import java .util .List ;
6- import java .util .stream . Collector ;
6+ import java .util .function . IntConsumer ;
77import java .util .stream .Collectors ;
88
9+ /**
10+ * CentroidDecomposition builds the centroid decomposition of an undirected tree.
11+ *
12+ * <p>The algorithm recursively finds centroids, marks them as removed, and
13+ * constructs a centroid tree whose height is O(log n). This structure enables
14+ * efficient solutions for distance queries, nearest-colored-node queries, and
15+ * other divide-and-conquer algorithms on trees.</p>
16+ *
17+ * <p>Supports adding edges of the original tree, building the centroid tree,
18+ * and querying centroid parents, children, and internal state.
19+ * </p>
20+ *
21+ * <p><strong>Author:</strong> Lennart S.<br>
22+ * <strong>GitHub: https://github.com/lens161<br>
23+ * </p>
24+ */
25+
926public class CentroidDecomposition {
1027 private ArrayList <Integer >[] tree ;
1128 private ArrayList <Integer >[] centroidTree ;
@@ -28,6 +45,7 @@ public CentroidDecomposition(int n, int startingNode){
2845 if (startingNode < 0 || startingNode > n -1 ){
2946 throw new IllegalArgumentException ("Starting node must be in range 0.." + (n - 1 ) + " but got " + startingNode );
3047 }
48+ this .startingNode = startingNode ;
3149 for (int i = 0 ; i <n ; i ++){
3250 centroidParent [i ] = -1 ;
3351 tree [i ] = new ArrayList <>();
@@ -40,6 +58,7 @@ public CentroidDecomposition(int n){
4058 }
4159
4260 public void build (){
61+ reset ();
4362 findCentroid (startingNode , startingNode );
4463 }
4564
@@ -86,6 +105,15 @@ public List<Integer> getCentroidChildren(int v) {
86105 .collect (Collectors .toList ());
87106 }
88107
108+ public int getRoot () {
109+ for (int i = 0 ; i < N ; i ++) {
110+ if (centroidParent [i ] == -1 ) {
111+ return i ;
112+ }
113+ }
114+ throw new IllegalStateException ("Centroid tree has no root. likely it was not built" );
115+ }
116+
89117 public void findSubtreeSizes (int src ){
90118 visited [src ] = true ;
91119 subtreeSizes [src ] = 1 ;
@@ -130,43 +158,16 @@ public void findCentroid(int src, int previousCentroid){
130158 }
131159 }
132160
133- public static void main (String [] args ) {
134- CentroidDecomposition cd = new CentroidDecomposition (16 );
135- cd .addEdgeTree (0 , 1 );
136- cd .addEdgeTree (0 , 2 );
137- cd .addEdgeTree (0 , 3 );
138- cd .addEdgeTree (1 , 4 );
139- cd .addEdgeTree (1 , 5 );
140- cd .addEdgeTree (2 , 6 );
141- cd .addEdgeTree (2 , 7 );
142- cd .addEdgeTree (3 , 8 );
143- cd .addEdgeTree (8 , 9 );
144- cd .addEdgeTree (8 , 10 );
145- cd .addEdgeTree (6 , 11 );
146- cd .addEdgeTree (11 , 12 );
147- cd .addEdgeTree (11 , 13 );
148- cd .addEdgeTree (13 , 14 );
149- cd .addEdgeTree (14 , 15 );
150-
151- // boolean[] visited = new boolean[16];
152- // int[] subtreeSizes = new int[16];
153-
154- // int start = cd.startingNode;
155- // int src = (int)(Math.random() * 15);
156- // System.out.println("src= " + src);
157- int start = cd .getStartingNode ();
158- cd .findCentroid (start , start );
159-
160- // System.out.println((int)(Math.random() * 16));
161-
162- for (int i = 0 ; i < cd .centroidTree .length ; i ++) {
163- System .out .println (String .format ("%s %s" , i , cd .centroidTree [i ]));
161+ /**
162+ * Applies the given action to all centroid ancestors of the given node,
163+ * including the node itself, walking up via centroidParent[] until the root.
164+ */
165+ public void forEachAcestor (int centroid , IntConsumer action ){
166+ int curr = centroid ;
167+ while (curr != -1 ){
168+ action .accept (curr );
169+ curr = getParent (curr );
164170 }
165-
166- // cd.findSubtreeSizes(8, visited, subtreeSizes);
167- // for (int i = 0; i < subtreeSizes.length; i++) {
168- // System.out.println(String.format("%s %s", i, subtreeSizes[i]));
169- // }
170171 }
171172
172173}
0 commit comments