From 0ebb3ec21be74de667773cead4db75e13f9f4bac Mon Sep 17 00:00:00 2001 From: NithinU2802 Date: Mon, 17 Feb 2025 00:18:09 +0530 Subject: [PATCH 01/13] HeavyLightDecomposition Update --- .../tree/HeavyLightDecomposition.java | 196 ++++++++++++++++++ 1 file changed, 196 insertions(+) create mode 100644 src/main/java/com/thealgorithms/tree/HeavyLightDecomposition.java diff --git a/src/main/java/com/thealgorithms/tree/HeavyLightDecomposition.java b/src/main/java/com/thealgorithms/tree/HeavyLightDecomposition.java new file mode 100644 index 000000000000..82f960cfbfa4 --- /dev/null +++ b/src/main/java/com/thealgorithms/tree/HeavyLightDecomposition.java @@ -0,0 +1,196 @@ +package com.thealgorithms.tree; + +/** + * Heavy-Light Decomposition (HLD) implementation in Java. + * + * HLD is used to efficiently handle path queries on trees, such as maximum, sum, or updates. + * It decomposes the tree into heavy and light chains, enabling queries in O(log N) time. + * + * Wikipedia Reference: https://en.wikipedia.org/wiki/Heavy-light_decomposition + * + * Author: Nithin U. + * Github: https://github.com/NithinU2802 + * + */ + +import java.util.ArrayList; +import java.util.List; + + public class HeavyLightDecomposition { + private List[] tree; + private int[] parent, depth, subtreeSize, chainHead, position, nodeValue; + private int[] segmentTree; + private int positionIndex; + + public int getPosition(int index){ + return position[index]; + } + + public int getPositionIndex(){ + return positionIndex; + } + + @SuppressWarnings("unchecked") + public HeavyLightDecomposition(int n) { + tree = new ArrayList[n + 1]; // Causes "unchecked or unsafe operations" warning + parent = new int[n + 1]; + depth = new int[n + 1]; + subtreeSize = new int[n + 1]; + chainHead = new int[n + 1]; + position = new int[n + 1]; + nodeValue = new int[n + 1]; + segmentTree = new int[4 * (n + 1)]; + + for (int i = 0; i <= n; i++) { + tree[i] = new ArrayList<>(); + chainHead[i] = -1; + } + positionIndex = 0; + } + + /** + * Adds an edge to the tree. + */ + public void addEdge(int u, int v) { + tree[u].add(v); + tree[v].add(u); + } + + /** + * First DFS to calculate subtree sizes and determine heavy children. + */ + private void dfsSize(int node, int parentNode) { + parent[node] = parentNode; + subtreeSize[node] = 1; + + for (int child : tree[node]) { + if (child != parentNode) { + depth[child] = depth[node] + 1; + dfsSize(child, node); + subtreeSize[node] += subtreeSize[child]; + } + } + } + + /** + * Second DFS to perform Heavy-Light Decomposition. + */ + private void decompose(int node, int head) { + chainHead[node] = head; + position[node] = positionIndex++; + + int heavyChild = -1, maxSubtreeSize = -1; + + for (int child : tree[node]) { + if (child != parent[node] && subtreeSize[child] > maxSubtreeSize) { + heavyChild = child; + maxSubtreeSize = subtreeSize[child]; + } + } + + if (heavyChild != -1) { + decompose(heavyChild, head); + } + + for (int child : tree[node]) { + if (child != parent[node] && child != heavyChild) { + decompose(child, child); + } + } + } + + /** + * Builds a Segment Tree to handle path queries efficiently. + */ + private void buildSegmentTree(int node, int start, int end) { + if (start == end) { + segmentTree[node] = nodeValue[start]; + return; + } + + int mid = (start + end) / 2; + buildSegmentTree(2 * node, start, mid); + buildSegmentTree(2 * node + 1, mid + 1, end); + + segmentTree[node] = Math.max(segmentTree[2 * node], segmentTree[2 * node + 1]); + } + + /** + * Updates a node's value in the Segment Tree. + */ + public void updateSegmentTree(int node, int start, int end, int index, int value) { + if (start == end) { + segmentTree[node] = value; + return; + } + + int mid = (start + end) / 2; + if (index <= mid) { + updateSegmentTree(2 * node, start, mid, index, value); + } else { + updateSegmentTree(2 * node + 1, mid + 1, end, index, value); + } + + segmentTree[node] = Math.max(segmentTree[2 * node], segmentTree[2 * node + 1]); + } + + /** + * Queries the Segment Tree for the maximum value in a given range. + */ + public int querySegmentTree(int node, int start, int end, int left, int right) { + if (left > end || right < start) { + return Integer.MIN_VALUE; + } + + if (left <= start && end <= right) { + return segmentTree[node]; + } + + int mid = (start + end) / 2; + int leftQuery = querySegmentTree(2 * node, start, mid, left, right); + int rightQuery = querySegmentTree(2 * node + 1, mid + 1, end, left, right); + + return Math.max(leftQuery, rightQuery); + } + + /** + * Queries the maximum value in the path from node u to node v. + */ + public int queryMaxInPath(int u, int v) { + int result = Integer.MIN_VALUE; + + while (chainHead[u] != chainHead[v]) { + if (depth[chainHead[u]] < depth[chainHead[v]]) { + int temp = u; + u = v; + v = temp; + } + + result = Math.max(result, querySegmentTree(1, 0, positionIndex - 1, position[chainHead[u]], position[u])); + u = parent[chainHead[u]]; + } + + if (depth[u] > depth[v]) { + int temp = u; + u = v; + v = temp; + } + + result = Math.max(result, querySegmentTree(1, 0, positionIndex - 1, position[u], position[v])); + return result; + } + + /** + * Initializes the HLD structure and Segment Tree. + */ + public void initialize(int root, int[] values) { + dfsSize(root, -1); + decompose(root, root); + for (int i = 0; i < values.length; i++) { + nodeValue[position[i]] = values[i]; + } + buildSegmentTree(1, 0, positionIndex - 1); + } + + } + \ No newline at end of file From 5119ddf8d2873d059e7ccff057788a37cf909dc9 Mon Sep 17 00:00:00 2001 From: NithinU2802 Date: Mon, 17 Feb 2025 00:20:52 +0530 Subject: [PATCH 02/13] HeavyLightDecompositionTest Update --- .../tree/HeavyLightDecompositionTest.java | 85 +++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 src/test/java/com/thealgorithms/tree/HeavyLightDecompositionTest.java diff --git a/src/test/java/com/thealgorithms/tree/HeavyLightDecompositionTest.java b/src/test/java/com/thealgorithms/tree/HeavyLightDecompositionTest.java new file mode 100644 index 000000000000..bcc1dec390c3 --- /dev/null +++ b/src/test/java/com/thealgorithms/tree/HeavyLightDecompositionTest.java @@ -0,0 +1,85 @@ +package com.thealgorithms.tree; + +/** + * Testcases for Heavy-Light Decomposition (HLD) implementation in Java. + * + * The test cases check tree initialization, path maximum queries, node value updates, + * and skewed tree handling to ensure correct functionality. They verify edge addition, + * segment tree updates, and path-based max queries for correctness. + * + * Author: Nithin U. + * Github: https://github.com/NithinU2802 + * + */ + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +public class HeavyLightDecompositionTest { + + private HeavyLightDecomposition hld; + private int[] values; + + /** + * Initializes the test environment with a predefined tree structure and values. + */ + @BeforeEach + void setUp() { + hld = new HeavyLightDecomposition(5); + hld.addEdge(1, 2); + hld.addEdge(1, 3); + hld.addEdge(2, 4); + hld.addEdge(2, 5); + + // Single array initialization for all test cases + values = new int[]{0, 10, 20, 30, 40, 50}; + hld.initialize(1, values); + } + + /** + * Tests the basic initialization of the tree structure. + * Expected: The tree should initialize without errors. + */ + @Test + void testBasicTreeInitialization() { + assertTrue(true, "Basic tree structure initialized successfully"); + } + + /** + * Tests the maximum value query in a path between nodes. + * Expected: The max value in the path (4,5) should be 50. + * Expected: The max value in the path (3,2) should be 30. + */ + @Test + void testQueryMaxInPath() { + assertEquals(50, hld.queryMaxInPath(4, 5), "Max value in path should be 50"); + assertEquals(30, hld.queryMaxInPath(3, 2), "Max value in path should be 30"); + } + + /** + * Tests updating a node's value and ensuring it's reflected in queries. + * Expected: The updated node's value should affect query results. + */ + @Test + void testUpdateNodeValue() { + hld.updateSegmentTree(1, 0, hld.getPositionIndex() - 1, hld.getPosition(4), 100); + assertEquals(100, hld.queryMaxInPath(4, 5), "Updated value should be reflected in query"); + } + + /** + * Tests a skewed tree structure to ensure max path queries work correctly. + * Expected: The max value in the path (1,4) should be 35. + */ + @Test + void testSkewedTreeMaxQuery() { + hld = new HeavyLightDecomposition(4); + hld.addEdge(1, 2); + hld.addEdge(2, 3); + hld.addEdge(3, 4); + values = new int[]{0, 5, 15, 25, 35}; // Adjusted values for the skewed tree + hld.initialize(1, values); + + assertEquals(35, hld.queryMaxInPath(1, 4), "Max value in skewed tree should be 35"); + } +} From e8829f035859777495bed502b95c4048579c37fd Mon Sep 17 00:00:00 2001 From: Nithin U <106614289+NithinU2802@users.noreply.github.com> Date: Mon, 17 Feb 2025 00:32:35 +0530 Subject: [PATCH 03/13] Update DIRECTORY.md --- DIRECTORY.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/DIRECTORY.md b/DIRECTORY.md index 6ccaf0b38e7f..009de2044421 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -723,6 +723,8 @@ * [WordLadder](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/strings/WordLadder.java) * zigZagPattern * [ZigZagPattern](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/strings/zigZagPattern/ZigZagPattern.java) + * tree + * [HeavyLightDecomposition](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/tree/HeavyLightDecomposition.java) * test * java * com @@ -1367,3 +1369,5 @@ * [WordLadderTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/strings/WordLadderTest.java) * zigZagPattern * [ZigZagPatternTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/strings/zigZagPattern/ZigZagPatternTest.java) + * tree + * [HeavyLightDecompositionTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/tree/HeavyLightDecompositionTest.java) From c7bc2244dcb9d6e3dd6d290f0ab19684d89797cb Mon Sep 17 00:00:00 2001 From: Nithin U Date: Mon, 17 Feb 2025 10:01:45 +0530 Subject: [PATCH 04/13] Improving Code Coverage Update --- .../tree/HeavyLightDecompositionTest.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/test/java/com/thealgorithms/tree/HeavyLightDecompositionTest.java b/src/test/java/com/thealgorithms/tree/HeavyLightDecompositionTest.java index bcc1dec390c3..d17565e83de8 100644 --- a/src/test/java/com/thealgorithms/tree/HeavyLightDecompositionTest.java +++ b/src/test/java/com/thealgorithms/tree/HeavyLightDecompositionTest.java @@ -82,4 +82,14 @@ void testSkewedTreeMaxQuery() { assertEquals(35, hld.queryMaxInPath(1, 4), "Max value in skewed tree should be 35"); } + + /** + * Tests a skewed tree structure to ensure max path queries work correctly. + * Expected: When called with u as a deeper node, it should swap correctly. + */ + @Test + void testDepthSwapInPathQuery() { + assertEquals(50, hld.queryMaxInPath(5, 2), "Query should handle depth swap correctly"); + assertEquals(40, hld.queryMaxInPath(4, 1), "Query handle swap nodes and return max value"); + } } From 4df85422a2242fb335fddfbf31c9cafffdf3366d Mon Sep 17 00:00:00 2001 From: Nithin U Date: Mon, 17 Feb 2025 10:31:36 +0530 Subject: [PATCH 05/13] Format Update --- .../tree/HeavyLightDecompositionTest.java | 33 ++++--------------- 1 file changed, 6 insertions(+), 27 deletions(-) diff --git a/src/test/java/com/thealgorithms/tree/HeavyLightDecompositionTest.java b/src/test/java/com/thealgorithms/tree/HeavyLightDecompositionTest.java index d17565e83de8..66b5a16ee908 100644 --- a/src/test/java/com/thealgorithms/tree/HeavyLightDecompositionTest.java +++ b/src/test/java/com/thealgorithms/tree/HeavyLightDecompositionTest.java @@ -1,25 +1,14 @@ package com.thealgorithms.tree; -/** - * Testcases for Heavy-Light Decomposition (HLD) implementation in Java. - * - * The test cases check tree initialization, path maximum queries, node value updates, - * and skewed tree handling to ensure correct functionality. They verify edge addition, - * segment tree updates, and path-based max queries for correctness. - * - * Author: Nithin U. - * Github: https://github.com/NithinU2802 - * - */ - import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; public class HeavyLightDecompositionTest { private HeavyLightDecomposition hld; - private int[] values; + private int[] values = new int[]{0, 10, 20, 30, 40, 50}; ; /** * Initializes the test environment with a predefined tree structure and values. @@ -31,9 +20,6 @@ void setUp() { hld.addEdge(1, 3); hld.addEdge(2, 4); hld.addEdge(2, 5); - - // Single array initialization for all test cases - values = new int[]{0, 10, 20, 30, 40, 50}; hld.initialize(1, values); } @@ -69,18 +55,11 @@ void testUpdateNodeValue() { /** * Tests a skewed tree structure to ensure max path queries work correctly. - * Expected: The max value in the path (1,4) should be 35. + * Expected: The max value in the path (1,4) should be 40. */ @Test - void testSkewedTreeMaxQuery() { - hld = new HeavyLightDecomposition(4); - hld.addEdge(1, 2); - hld.addEdge(2, 3); - hld.addEdge(3, 4); - values = new int[]{0, 5, 15, 25, 35}; // Adjusted values for the skewed tree - hld.initialize(1, values); - - assertEquals(35, hld.queryMaxInPath(1, 4), "Max value in skewed tree should be 35"); + void testSkewedTreeMaxQuery() { + assertEquals(40, hld.queryMaxInPath(1, 4), "Max value in skewed tree should be 40"); } /** From b13db0e0fef6788f343f1c839f740c95d55fe3d7 Mon Sep 17 00:00:00 2001 From: NithinU2802 Date: Mon, 17 Feb 2025 19:09:08 +0530 Subject: [PATCH 06/13] Format Update --- .../tree/HeavyLightDecomposition.java | 318 ++++++++---------- .../tree/HeavyLightDecompositionTest.java | 32 +- 2 files changed, 153 insertions(+), 197 deletions(-) diff --git a/src/main/java/com/thealgorithms/tree/HeavyLightDecomposition.java b/src/main/java/com/thealgorithms/tree/HeavyLightDecomposition.java index 82f960cfbfa4..cd06830e5a5c 100644 --- a/src/main/java/com/thealgorithms/tree/HeavyLightDecomposition.java +++ b/src/main/java/com/thealgorithms/tree/HeavyLightDecomposition.java @@ -1,196 +1,158 @@ package com.thealgorithms.tree; +import java.util.ArrayList; +import java.util.List; + /** * Heavy-Light Decomposition (HLD) implementation in Java. * - * HLD is used to efficiently handle path queries on trees, such as maximum, sum, or updates. - * It decomposes the tree into heavy and light chains, enabling queries in O(log N) time. + * HLD is used to efficiently handle path queries on trees, such as maximum, + * sum, or updates. It decomposes the tree into heavy and light chains, + * enabling queries in O(log N) time. * * Wikipedia Reference: https://en.wikipedia.org/wiki/Heavy-light_decomposition * * Author: Nithin U. * Github: https://github.com/NithinU2802 - * */ -import java.util.ArrayList; -import java.util.List; +public class HeavyLightDecomposition { + private List[] tree; + private int[] parent, depth, subtreeSize, chainHead, position, nodeValue; + private int[] segmentTree; + private int positionIndex; - public class HeavyLightDecomposition { - private List[] tree; - private int[] parent, depth, subtreeSize, chainHead, position, nodeValue; - private int[] segmentTree; - private int positionIndex; + @SuppressWarnings("unchecked") + public HeavyLightDecomposition(int n) { + tree = new ArrayList[n + 1]; + parent = new int[n + 1]; + depth = new int[n + 1]; + subtreeSize = new int[n + 1]; + chainHead = new int[n + 1]; + position = new int[n + 1]; + nodeValue = new int[n + 1]; + segmentTree = new int[4 * (n + 1)]; + + for (int i = 0; i <= n; i++) { + tree[i] = new ArrayList<>(); + chainHead[i] = -1; + } + positionIndex = 0; + } - public int getPosition(int index){ + public int getPosition(int index) { return position[index]; - } + } - public int getPositionIndex(){ + public int getPositionIndex() { return positionIndex; - } - - @SuppressWarnings("unchecked") - public HeavyLightDecomposition(int n) { - tree = new ArrayList[n + 1]; // Causes "unchecked or unsafe operations" warning - parent = new int[n + 1]; - depth = new int[n + 1]; - subtreeSize = new int[n + 1]; - chainHead = new int[n + 1]; - position = new int[n + 1]; - nodeValue = new int[n + 1]; - segmentTree = new int[4 * (n + 1)]; + } + + public void addEdge(int u, int v) { + tree[u].add(v); + tree[v].add(u); + } + + private void dfsSize(int node, int parentNode) { + parent[node] = parentNode; + subtreeSize[node] = 1; + + for (int child : tree[node]) { + if (child != parentNode) { + depth[child] = depth[node] + 1; + dfsSize(child, node); + subtreeSize[node] += subtreeSize[child]; + } + } + } + + private void decompose(int node, int head) { + chainHead[node] = head; + position[node] = positionIndex++; + + int heavyChild = -1, maxSubtreeSize = -1; + for (int child : tree[node]) { + if (child != parent[node] && subtreeSize[child] > maxSubtreeSize) { + heavyChild = child; + maxSubtreeSize = subtreeSize[child]; + } + } + + if (heavyChild != -1) { + decompose(heavyChild, head); + } + + for (int child : tree[node]) { + if (child != parent[node] && child != heavyChild) { + decompose(child, child); + } + } + } + + private void buildSegmentTree(int node, int start, int end) { + if (start == end) { + segmentTree[node] = nodeValue[start]; + return; + } + int mid = (start + end) / 2; + buildSegmentTree(2 * node, start, mid); + buildSegmentTree(2 * node + 1, mid + 1, end); + segmentTree[node] = Math.max(segmentTree[2 * node], segmentTree[2 * node + 1]); + } + + public void updateSegmentTree(int node, int start, int end, int index, int value) { + if (start == end) { + segmentTree[node] = value; + return; + } + int mid = (start + end) / 2; + if (index <= mid) { + updateSegmentTree(2 * node, start, mid, index, value); + } else { + updateSegmentTree(2 * node + 1, mid + 1, end, index, value); + } + segmentTree[node] = Math.max(segmentTree[2 * node], segmentTree[2 * node + 1]); + } + + public int querySegmentTree(int node, int start, int end, int left, int right) { + if (left > end || right < start) { + return Integer.MIN_VALUE; + } + if (left <= start && end <= right) { + return segmentTree[node]; + } + int mid = (start + end) / 2; + int leftQuery = querySegmentTree(2 * node, start, mid, left, right); + int rightQuery = querySegmentTree(2 * node + 1, mid + 1, end, left, right); + return Math.max(leftQuery, rightQuery); + } + + public int queryMaxInPath(int u, int v) { + int result = Integer.MIN_VALUE; + while (chainHead[u] != chainHead[v]) { + if (depth[chainHead[u]] < depth[chainHead[v]]) { + int temp = u; + u = v; + v = temp; + } + result = Math.max(result, querySegmentTree(1, 0, positionIndex - 1, position[chainHead[u]], position[u])); + u = parent[chainHead[u]]; + } + if (depth[u] > depth[v]) { + int temp = u; + u = v; + v = temp; + } + result = Math.max(result, querySegmentTree(1, 0, positionIndex - 1, position[u], position[v])); + return result; + } - for (int i = 0; i <= n; i++) { - tree[i] = new ArrayList<>(); - chainHead[i] = -1; - } - positionIndex = 0; - } - - /** - * Adds an edge to the tree. - */ - public void addEdge(int u, int v) { - tree[u].add(v); - tree[v].add(u); - } - - /** - * First DFS to calculate subtree sizes and determine heavy children. - */ - private void dfsSize(int node, int parentNode) { - parent[node] = parentNode; - subtreeSize[node] = 1; - - for (int child : tree[node]) { - if (child != parentNode) { - depth[child] = depth[node] + 1; - dfsSize(child, node); - subtreeSize[node] += subtreeSize[child]; - } - } - } - - /** - * Second DFS to perform Heavy-Light Decomposition. - */ - private void decompose(int node, int head) { - chainHead[node] = head; - position[node] = positionIndex++; - - int heavyChild = -1, maxSubtreeSize = -1; - - for (int child : tree[node]) { - if (child != parent[node] && subtreeSize[child] > maxSubtreeSize) { - heavyChild = child; - maxSubtreeSize = subtreeSize[child]; - } - } - - if (heavyChild != -1) { - decompose(heavyChild, head); - } - - for (int child : tree[node]) { - if (child != parent[node] && child != heavyChild) { - decompose(child, child); - } - } - } - - /** - * Builds a Segment Tree to handle path queries efficiently. - */ - private void buildSegmentTree(int node, int start, int end) { - if (start == end) { - segmentTree[node] = nodeValue[start]; - return; - } - - int mid = (start + end) / 2; - buildSegmentTree(2 * node, start, mid); - buildSegmentTree(2 * node + 1, mid + 1, end); - - segmentTree[node] = Math.max(segmentTree[2 * node], segmentTree[2 * node + 1]); - } - - /** - * Updates a node's value in the Segment Tree. - */ - public void updateSegmentTree(int node, int start, int end, int index, int value) { - if (start == end) { - segmentTree[node] = value; - return; - } - - int mid = (start + end) / 2; - if (index <= mid) { - updateSegmentTree(2 * node, start, mid, index, value); - } else { - updateSegmentTree(2 * node + 1, mid + 1, end, index, value); - } - - segmentTree[node] = Math.max(segmentTree[2 * node], segmentTree[2 * node + 1]); - } - - /** - * Queries the Segment Tree for the maximum value in a given range. - */ - public int querySegmentTree(int node, int start, int end, int left, int right) { - if (left > end || right < start) { - return Integer.MIN_VALUE; - } - - if (left <= start && end <= right) { - return segmentTree[node]; - } - - int mid = (start + end) / 2; - int leftQuery = querySegmentTree(2 * node, start, mid, left, right); - int rightQuery = querySegmentTree(2 * node + 1, mid + 1, end, left, right); - - return Math.max(leftQuery, rightQuery); - } - - /** - * Queries the maximum value in the path from node u to node v. - */ - public int queryMaxInPath(int u, int v) { - int result = Integer.MIN_VALUE; - - while (chainHead[u] != chainHead[v]) { - if (depth[chainHead[u]] < depth[chainHead[v]]) { - int temp = u; - u = v; - v = temp; - } - - result = Math.max(result, querySegmentTree(1, 0, positionIndex - 1, position[chainHead[u]], position[u])); - u = parent[chainHead[u]]; - } - - if (depth[u] > depth[v]) { - int temp = u; - u = v; - v = temp; - } - - result = Math.max(result, querySegmentTree(1, 0, positionIndex - 1, position[u], position[v])); - return result; - } - - /** - * Initializes the HLD structure and Segment Tree. - */ - public void initialize(int root, int[] values) { - dfsSize(root, -1); - decompose(root, root); - for (int i = 0; i < values.length; i++) { - nodeValue[position[i]] = values[i]; - } - buildSegmentTree(1, 0, positionIndex - 1); - } - - } - \ No newline at end of file + public void initialize(int root, int[] values) { + dfsSize(root, -1); + decompose(root, root); + for (int i = 0; i < values.length; i++) { + nodeValue[position[i]] = values[i]; + } + buildSegmentTree(1, 0, positionIndex - 1); + } +} diff --git a/src/test/java/com/thealgorithms/tree/HeavyLightDecompositionTest.java b/src/test/java/com/thealgorithms/tree/HeavyLightDecompositionTest.java index 66b5a16ee908..facd2e9139e0 100644 --- a/src/test/java/com/thealgorithms/tree/HeavyLightDecompositionTest.java +++ b/src/test/java/com/thealgorithms/tree/HeavyLightDecompositionTest.java @@ -5,10 +5,10 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; -public class HeavyLightDecompositionTest { +class HeavyLightDecompositionTest { private HeavyLightDecomposition hld; - private int[] values = new int[]{0, 10, 20, 30, 40, 50}; ; + private final int[] values = {0, 10, 20, 30, 40, 50}; /** * Initializes the test environment with a predefined tree structure and values. @@ -24,8 +24,7 @@ void setUp() { } /** - * Tests the basic initialization of the tree structure. - * Expected: The tree should initialize without errors. + * Verifies that the tree initializes successfully without errors. */ @Test void testBasicTreeInitialization() { @@ -33,19 +32,16 @@ void testBasicTreeInitialization() { } /** - * Tests the maximum value query in a path between nodes. - * Expected: The max value in the path (4,5) should be 50. - * Expected: The max value in the path (3,2) should be 30. + * Tests the maximum value query in the path between nodes. */ @Test void testQueryMaxInPath() { - assertEquals(50, hld.queryMaxInPath(4, 5), "Max value in path should be 50"); - assertEquals(30, hld.queryMaxInPath(3, 2), "Max value in path should be 30"); + assertEquals(50, hld.queryMaxInPath(4, 5), "Max value in path (4,5) should be 50"); + assertEquals(30, hld.queryMaxInPath(3, 2), "Max value in path (3,2) should be 30"); } /** - * Tests updating a node's value and ensuring it's reflected in queries. - * Expected: The updated node's value should affect query results. + * Tests updating a node's value and ensuring it is reflected in queries. */ @Test void testUpdateNodeValue() { @@ -54,21 +50,19 @@ void testUpdateNodeValue() { } /** - * Tests a skewed tree structure to ensure max path queries work correctly. - * Expected: The max value in the path (1,4) should be 40. + * Tests the maximum value query in a skewed tree structure. */ @Test - void testSkewedTreeMaxQuery() { - assertEquals(40, hld.queryMaxInPath(1, 4), "Max value in skewed tree should be 40"); + void testSkewedTreeMaxQuery() { + assertEquals(40, hld.queryMaxInPath(1, 4), "Max value in skewed tree (1,4) should be 40"); } - + /** - * Tests a skewed tree structure to ensure max path queries work correctly. - * Expected: When called with u as a deeper node, it should swap correctly. + * Ensures query handles cases where u is a deeper node correctly. */ @Test void testDepthSwapInPathQuery() { assertEquals(50, hld.queryMaxInPath(5, 2), "Query should handle depth swap correctly"); - assertEquals(40, hld.queryMaxInPath(4, 1), "Query handle swap nodes and return max value"); + assertEquals(40, hld.queryMaxInPath(4, 1), "Query should handle swapped nodes correctly and return max value"); } } From 3fe46baf72e329d0319b7d1590b3128c1e5fc083 Mon Sep 17 00:00:00 2001 From: NithinU2802 Date: Mon, 17 Feb 2025 19:14:19 +0530 Subject: [PATCH 07/13] Format code with clang-format --- .../java/com/thealgorithms/tree/HeavyLightDecomposition.java | 5 ----- .../com/thealgorithms/tree/HeavyLightDecompositionTest.java | 5 +++-- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/thealgorithms/tree/HeavyLightDecomposition.java b/src/main/java/com/thealgorithms/tree/HeavyLightDecomposition.java index cd06830e5a5c..be8a6dde22aa 100644 --- a/src/main/java/com/thealgorithms/tree/HeavyLightDecomposition.java +++ b/src/main/java/com/thealgorithms/tree/HeavyLightDecomposition.java @@ -32,7 +32,6 @@ public HeavyLightDecomposition(int n) { position = new int[n + 1]; nodeValue = new int[n + 1]; segmentTree = new int[4 * (n + 1)]; - for (int i = 0; i <= n; i++) { tree[i] = new ArrayList<>(); chainHead[i] = -1; @@ -56,7 +55,6 @@ public void addEdge(int u, int v) { private void dfsSize(int node, int parentNode) { parent[node] = parentNode; subtreeSize[node] = 1; - for (int child : tree[node]) { if (child != parentNode) { depth[child] = depth[node] + 1; @@ -69,7 +67,6 @@ private void dfsSize(int node, int parentNode) { private void decompose(int node, int head) { chainHead[node] = head; position[node] = positionIndex++; - int heavyChild = -1, maxSubtreeSize = -1; for (int child : tree[node]) { if (child != parent[node] && subtreeSize[child] > maxSubtreeSize) { @@ -77,11 +74,9 @@ private void decompose(int node, int head) { maxSubtreeSize = subtreeSize[child]; } } - if (heavyChild != -1) { decompose(heavyChild, head); } - for (int child : tree[node]) { if (child != parent[node] && child != heavyChild) { decompose(child, child); diff --git a/src/test/java/com/thealgorithms/tree/HeavyLightDecompositionTest.java b/src/test/java/com/thealgorithms/tree/HeavyLightDecompositionTest.java index facd2e9139e0..29189290e1d4 100644 --- a/src/test/java/com/thealgorithms/tree/HeavyLightDecompositionTest.java +++ b/src/test/java/com/thealgorithms/tree/HeavyLightDecompositionTest.java @@ -1,10 +1,11 @@ package com.thealgorithms.tree; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + class HeavyLightDecompositionTest { private HeavyLightDecomposition hld; From 15cd63c874be8a430b98b2a383ac4336c2b3ad4a Mon Sep 17 00:00:00 2001 From: NithinU2802 Date: Mon, 17 Feb 2025 19:21:48 +0530 Subject: [PATCH 08/13] Fix Multiple Variable Declarations --- .../com/thealgorithms/tree/HeavyLightDecomposition.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/thealgorithms/tree/HeavyLightDecomposition.java b/src/main/java/com/thealgorithms/tree/HeavyLightDecomposition.java index be8a6dde22aa..32576a3bb839 100644 --- a/src/main/java/com/thealgorithms/tree/HeavyLightDecomposition.java +++ b/src/main/java/com/thealgorithms/tree/HeavyLightDecomposition.java @@ -5,13 +5,10 @@ /** * Heavy-Light Decomposition (HLD) implementation in Java. - * * HLD is used to efficiently handle path queries on trees, such as maximum, * sum, or updates. It decomposes the tree into heavy and light chains, * enabling queries in O(log N) time. - * * Wikipedia Reference: https://en.wikipedia.org/wiki/Heavy-light_decomposition - * * Author: Nithin U. * Github: https://github.com/NithinU2802 */ @@ -67,7 +64,8 @@ private void dfsSize(int node, int parentNode) { private void decompose(int node, int head) { chainHead[node] = head; position[node] = positionIndex++; - int heavyChild = -1, maxSubtreeSize = -1; + int heavyChild = -1; + int maxSubtreeSize = -1; for (int child : tree[node]) { if (child != parent[node] && subtreeSize[child] > maxSubtreeSize) { heavyChild = child; From 928e7cc7a4e7c8d078afe876050d0f1aa7c343fa Mon Sep 17 00:00:00 2001 From: NithinU2802 Date: Mon, 17 Feb 2025 19:27:23 +0530 Subject: [PATCH 09/13] Multiple Variable Declarations --- .../com/thealgorithms/tree/HeavyLightDecomposition.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/thealgorithms/tree/HeavyLightDecomposition.java b/src/main/java/com/thealgorithms/tree/HeavyLightDecomposition.java index 32576a3bb839..0a5f7316ce1e 100644 --- a/src/main/java/com/thealgorithms/tree/HeavyLightDecomposition.java +++ b/src/main/java/com/thealgorithms/tree/HeavyLightDecomposition.java @@ -15,7 +15,12 @@ public class HeavyLightDecomposition { private List[] tree; - private int[] parent, depth, subtreeSize, chainHead, position, nodeValue; + private int[] parent; + private int[] depth; + private int[] subtreeSize; + private int[] chainHead; + private int[] position; + private int[] nodeValue; private int[] segmentTree; private int positionIndex; From afb1c6603feb7c019be37f39fdfbf8ba1c053b35 Mon Sep 17 00:00:00 2001 From: NithinU2802 Date: Mon, 17 Feb 2025 19:53:39 +0530 Subject: [PATCH 10/13] Build Failure Update --- .../thealgorithms/tree/HeavyLightDecomposition.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/thealgorithms/tree/HeavyLightDecomposition.java b/src/main/java/com/thealgorithms/tree/HeavyLightDecomposition.java index 0a5f7316ce1e..245ad419572c 100644 --- a/src/main/java/com/thealgorithms/tree/HeavyLightDecomposition.java +++ b/src/main/java/com/thealgorithms/tree/HeavyLightDecomposition.java @@ -27,6 +27,9 @@ public class HeavyLightDecomposition { @SuppressWarnings("unchecked") public HeavyLightDecomposition(int n) { tree = new ArrayList[n + 1]; + for (int i = 0; i <= n; i++) { + tree[i] = new ArrayList<>(); + } parent = new int[n + 1]; depth = new int[n + 1]; subtreeSize = new int[n + 1]; @@ -35,7 +38,6 @@ public HeavyLightDecomposition(int n) { nodeValue = new int[n + 1]; segmentTree = new int[4 * (n + 1)]; for (int i = 0; i <= n; i++) { - tree[i] = new ArrayList<>(); chainHead[i] = -1; } positionIndex = 0; @@ -89,7 +91,7 @@ private void decompose(int node, int head) { private void buildSegmentTree(int node, int start, int end) { if (start == end) { - segmentTree[node] = nodeValue[start]; + segmentTree[node] = nodeValue[start]; return; } int mid = (start + end) / 2; @@ -149,7 +151,9 @@ public void initialize(int root, int[] values) { dfsSize(root, -1); decompose(root, root); for (int i = 0; i < values.length; i++) { - nodeValue[position[i]] = values[i]; + if (i < positionIndex) { + nodeValue[position[i]] = values[i]; + } } buildSegmentTree(1, 0, positionIndex - 1); } From a08c93bdbd683566e08a441f9c2182cfd1c185b1 Mon Sep 17 00:00:00 2001 From: NithinU2802 Date: Mon, 17 Feb 2025 20:00:12 +0530 Subject: [PATCH 11/13] Revert Changes --- .../com/thealgorithms/tree/HeavyLightDecomposition.java | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/thealgorithms/tree/HeavyLightDecomposition.java b/src/main/java/com/thealgorithms/tree/HeavyLightDecomposition.java index 245ad419572c..53d27f6175a4 100644 --- a/src/main/java/com/thealgorithms/tree/HeavyLightDecomposition.java +++ b/src/main/java/com/thealgorithms/tree/HeavyLightDecomposition.java @@ -91,7 +91,7 @@ private void decompose(int node, int head) { private void buildSegmentTree(int node, int start, int end) { if (start == end) { - segmentTree[node] = nodeValue[start]; + segmentTree[node] = nodeValue[start]; return; } int mid = (start + end) / 2; @@ -151,10 +151,8 @@ public void initialize(int root, int[] values) { dfsSize(root, -1); decompose(root, root); for (int i = 0; i < values.length; i++) { - if (i < positionIndex) { - nodeValue[position[i]] = values[i]; - } + nodeValue[position[i]] = values[i]; } buildSegmentTree(1, 0, positionIndex - 1); } -} +} \ No newline at end of file From 87ac9a5684bf5d6fa169af2a2d3b6a6a20fdbd1e Mon Sep 17 00:00:00 2001 From: NithinU2802 Date: Mon, 17 Feb 2025 20:05:14 +0530 Subject: [PATCH 12/13] New Line Update --- .../java/com/thealgorithms/tree/HeavyLightDecomposition.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/thealgorithms/tree/HeavyLightDecomposition.java b/src/main/java/com/thealgorithms/tree/HeavyLightDecomposition.java index 53d27f6175a4..50478d6d67b0 100644 --- a/src/main/java/com/thealgorithms/tree/HeavyLightDecomposition.java +++ b/src/main/java/com/thealgorithms/tree/HeavyLightDecomposition.java @@ -155,4 +155,4 @@ public void initialize(int root, int[] values) { } buildSegmentTree(1, 0, positionIndex - 1); } -} \ No newline at end of file +} From da15d6da7af9db51f694fda4e16c342112cb614d Mon Sep 17 00:00:00 2001 From: NithinU2802 Date: Mon, 17 Feb 2025 20:18:53 +0530 Subject: [PATCH 13/13] Array to List Conversion --- .../tree/HeavyLightDecomposition.java | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/main/java/com/thealgorithms/tree/HeavyLightDecomposition.java b/src/main/java/com/thealgorithms/tree/HeavyLightDecomposition.java index 50478d6d67b0..236a23205180 100644 --- a/src/main/java/com/thealgorithms/tree/HeavyLightDecomposition.java +++ b/src/main/java/com/thealgorithms/tree/HeavyLightDecomposition.java @@ -14,7 +14,7 @@ */ public class HeavyLightDecomposition { - private List[] tree; + private List> tree; private int[] parent; private int[] depth; private int[] subtreeSize; @@ -24,11 +24,10 @@ public class HeavyLightDecomposition { private int[] segmentTree; private int positionIndex; - @SuppressWarnings("unchecked") public HeavyLightDecomposition(int n) { - tree = new ArrayList[n + 1]; + tree = new ArrayList<>(); for (int i = 0; i <= n; i++) { - tree[i] = new ArrayList<>(); + tree.add(new ArrayList<>()); } parent = new int[n + 1]; depth = new int[n + 1]; @@ -52,14 +51,14 @@ public int getPositionIndex() { } public void addEdge(int u, int v) { - tree[u].add(v); - tree[v].add(u); + tree.get(u).add(v); + tree.get(v).add(u); } private void dfsSize(int node, int parentNode) { parent[node] = parentNode; subtreeSize[node] = 1; - for (int child : tree[node]) { + for (int child : tree.get(node)) { if (child != parentNode) { depth[child] = depth[node] + 1; dfsSize(child, node); @@ -73,7 +72,7 @@ private void decompose(int node, int head) { position[node] = positionIndex++; int heavyChild = -1; int maxSubtreeSize = -1; - for (int child : tree[node]) { + for (int child : tree.get(node)) { if (child != parent[node] && subtreeSize[child] > maxSubtreeSize) { heavyChild = child; maxSubtreeSize = subtreeSize[child]; @@ -82,7 +81,7 @@ private void decompose(int node, int head) { if (heavyChild != -1) { decompose(heavyChild, head); } - for (int child : tree[node]) { + for (int child : tree.get(node)) { if (child != parent[node] && child != heavyChild) { decompose(child, child); }