Repository: TheAlgorithms/Java
Pull request #5681 (2024-10-09 15:52:32)

feat: add `QuadTree` datastructure
<!--
Thank you for your contribution!
In order to reduce the number of notifications sent to the maintainers, please:
- create your PR as draft, cf. https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests#draft-pull-requests,
- make sure that all of the CI checks pass,
- mark your PR as ready for review, cf. https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/changing-the-stage-of-a-pull-request#marking-a-pull-request-as-ready-for-review
-->

<!-- For completed items, change [ ] to [x] -->

- [x] I have read [CONTRIBUTING.md](https://github.com/TheAlgorithms/Java/blob/master/CONTRIBUTING.md).
- [x] This pull request is all my own work -- I have not plagiarized it.
- [x] All filenames are in PascalCase.
- [x] All functions and variable names follow Java naming conventions.
- [x] All new algorithms have a URL in their comments that points to Wikipedia or other similar explanations.
- [x] All new code is formatted with `clang-format -i --style=file path/to/your/file.java`

sailok: @siriak can you please review this PR?

Patch:
diff --git a/DIRECTORY.md b/DIRECTORY.md
index dea06ca1..3dbd1519 100644
--- a/DIRECTORY.md
+++ b/DIRECTORY.md
@@ -218,6 +218,7 @@
               * [PostOrderTraversal](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/trees/PostOrderTraversal.java)
               * [PreOrderTraversal](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/trees/PreOrderTraversal.java)
               * [PrintTopViewofTree](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/trees/PrintTopViewofTree.java)
+              * [QuadTree](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/trees/QuadTree.java)
               * [RedBlackBST](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/trees/RedBlackBST.java)
               * [SameTreesCheck](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/trees/SameTreesCheck.java)
               * [SegmentTree](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/trees/SegmentTree.java)
diff --git a/src/main/java/com/thealgorithms/datastructures/trees/QuadTree.java b/src/main/java/com/thealgorithms/datastructures/trees/QuadTree.java
new file mode 100644
index 00000000..e0d255b1
--- /dev/null
+++ b/src/main/java/com/thealgorithms/datastructures/trees/QuadTree.java
@@ -0,0 +1,176 @@
+package com.thealgorithms.datastructures.trees;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Point is a simple class that represents a point in 2D space.
+ *
+ * @see <a href="https://en.wikipedia.org/wiki/Point_(geometry)">Point</a>
+ * @author <a href="https://github.com/sailok">Sailok Chinta</a>
+ */
+class Point {
+    public double x;
+    public double y;
+
+    Point(double x, double y) {
+        this.x = x;
+        this.y = y;
+    }
+}
+
+/**
+ * BoundingBox is a simple class that represents a bounding box in 2D space.
+ *
+ * @see <a href="https://en.wikipedia.org/wiki/Bounding_box">Bounding Box</a>
+ * @author <a href="https://github.com/sailok">Sailok Chinta</a>
+ */
+class BoundingBox {
+    public Point center;
+    public double halfWidth;
+
+    BoundingBox(Point center, double halfWidth) {
+        this.center = center;
+        this.halfWidth = halfWidth;
+    }
+
+    /**
+     * Checks if the point is inside the bounding box
+     *
+     * @param point The point to check
+     * @return true if the point is inside the bounding box, false otherwise
+     */
+    public boolean containsPoint(Point point) {
+        return point.x >= center.x - halfWidth && point.x <= center.x + halfWidth && point.y >= center.y - halfWidth && point.y <= center.y + halfWidth;
+    }
+
+    /**
+     * Checks if the bounding box intersects with the other bounding box
+     *
+     * @param otherBoundingBox The other bounding box
+     * @return true if the bounding box intersects with the other bounding box, false otherwise
+     */
+    public boolean intersectsBoundingBox(BoundingBox otherBoundingBox) {
+        return otherBoundingBox.center.x - otherBoundingBox.halfWidth <= center.x + halfWidth && otherBoundingBox.center.x + otherBoundingBox.halfWidth >= center.x - halfWidth && otherBoundingBox.center.y - otherBoundingBox.halfWidth <= center.y + halfWidth
+            && otherBoundingBox.center.y + otherBoundingBox.halfWidth >= center.y - halfWidth;
+    }
+}
+
+/**
+ * QuadTree is a tree data structure that is used to store spatial information
+ * in an efficient way.
+ *
+ * This implementation is specific to Point QuadTrees
+ *
+ * @see <a href="https://en.wikipedia.org/wiki/Quadtree">Quad Tree</a>
+ * @author <a href="https://github.com/sailok">Sailok Chinta</a>
+ */
+public class QuadTree {
+    private final BoundingBox boundary;
+    private final int capacity;
+
+    private List<Point> pointList;
+    private boolean divided;
+    private QuadTree northWest;
+    private QuadTree northEast;
+    private QuadTree southWest;
+    private QuadTree southEast;
+
+    public QuadTree(BoundingBox boundary, int capacity) {
+        this.boundary = boundary;
+        this.capacity = capacity;
+
+        this.pointList = new ArrayList<>();
+        this.divided = false;
+        this.northWest = null;
+        this.northEast = null;
+        this.southWest = null;
+        this.southEast = null;
+    }
+
+    /**
+     * Inserts a point into the tree
+     *
+     * @param point The point to insert
+     * @return true if the point is successfully inserted, false otherwise
+     */
+    public boolean insert(Point point) {
+        if (point == null) {
+            return false;
+        }
+
+        // Ignore points that don't belong to this quad tree
+        if (!boundary.containsPoint(point)) {
+            return false;
+        }
+
+        // if the space is not already occupied, add it to the list
+        if (pointList.size() < capacity) {
+            pointList.add(point);
+            return true;
+        }
+
+        // if subdivision hasn't happened, divide the tree
+        if (!divided) {
+            subDivide();
+        }
+
+        // try to add the point in one of the four quadrants
+        if (northWest.insert(point)) {
+            return true;
+        }
+
+        if (northEast.insert(point)) {
+            return true;
+        }
+
+        if (southWest.insert(point)) {
+            return true;
+        }
+
+        if (southEast.insert(point)) {
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * Create four children that fully divide this quad into four quads of equal area
+     */
+    private void subDivide() {
+        double quadrantHalfWidth = boundary.halfWidth / 2;
+
+        northWest = new QuadTree(new BoundingBox(new Point(boundary.center.x - quadrantHalfWidth, boundary.center.y + quadrantHalfWidth), quadrantHalfWidth), this.capacity);
+        northEast = new QuadTree(new BoundingBox(new Point(boundary.center.x + quadrantHalfWidth, boundary.center.y + quadrantHalfWidth), quadrantHalfWidth), this.capacity);
+        southWest = new QuadTree(new BoundingBox(new Point(boundary.center.x - quadrantHalfWidth, boundary.center.y - quadrantHalfWidth), quadrantHalfWidth), this.capacity);
+        southEast = new QuadTree(new BoundingBox(new Point(boundary.center.x + quadrantHalfWidth, boundary.center.y - quadrantHalfWidth), quadrantHalfWidth), this.capacity);
+        divided = true;
+    }
+
+    /**
+     * Queries all the points that intersect with the other bounding box
+     *
+     * @param otherBoundingBox The other bounding box
+     * @return List of points that intersect with the other bounding box
+     */
+    public List<Point> query(BoundingBox otherBoundingBox) {
+        List<Point> points = new ArrayList<>();
+
+        if (!boundary.intersectsBoundingBox(otherBoundingBox)) {
+            return points;
+        }
+
+        // filter the points that intersect with the other bounding box
+        points.addAll(pointList.stream().filter(otherBoundingBox::containsPoint).toList());
+
+        if (divided) {
+            points.addAll(northWest.query(otherBoundingBox));
+            points.addAll(northEast.query(otherBoundingBox));
+            points.addAll(southWest.query(otherBoundingBox));
+            points.addAll(southEast.query(otherBoundingBox));
+        }
+
+        return points;
+    }
+}


Test patch:
diff --git a/src/test/java/com/thealgorithms/datastructures/trees/QuadTreeTest.java b/src/test/java/com/thealgorithms/datastructures/trees/QuadTreeTest.java
new file mode 100644
index 00000000..62b86da2
--- /dev/null
+++ b/src/test/java/com/thealgorithms/datastructures/trees/QuadTreeTest.java
@@ -0,0 +1,58 @@
+package com.thealgorithms.datastructures.trees;
+
+import java.util.List;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class QuadTreeTest {
+    int quadTreeCapacity = 4;
+    BoundingBox boundingBox = new BoundingBox(new Point(0, 0), 500);
+    QuadTree quadTree = new QuadTree(boundingBox, quadTreeCapacity);
+
+    @Test
+    public void testNullPointInsertIntoQuadTree() {
+        Assertions.assertFalse(quadTree.insert(null));
+    }
+
+    @Test
+    public void testInsertIntoQuadTree() {
+        Assertions.assertTrue(quadTree.insert(new Point(10, -10)));
+        Assertions.assertTrue(quadTree.insert(new Point(-10, 10)));
+        Assertions.assertTrue(quadTree.insert(new Point(-10, -10)));
+        Assertions.assertTrue(quadTree.insert(new Point(10, 10)));
+        Assertions.assertFalse(quadTree.insert(new Point(1050, 1050)));
+    }
+
+    @Test
+    public void testInsertIntoQuadTreeAndSubDivide() {
+        Assertions.assertTrue(quadTree.insert(new Point(10, -10)));
+        Assertions.assertTrue(quadTree.insert(new Point(-10, 10)));
+        Assertions.assertTrue(quadTree.insert(new Point(-10, -10)));
+        Assertions.assertTrue(quadTree.insert(new Point(10, 10)));
+        Assertions.assertTrue(quadTree.insert(new Point(-100, 100)));
+        Assertions.assertTrue(quadTree.insert(new Point(100, -101)));
+        Assertions.assertTrue(quadTree.insert(new Point(-100, -100)));
+        Assertions.assertTrue(quadTree.insert(new Point(100, 100)));
+    }
+
+    @Test
+    public void testQueryInQuadTree() {
+        quadTree.insert(new Point(10, -10));
+        quadTree.insert(new Point(-10, 10));
+        quadTree.insert(new Point(-10, -10));
+        quadTree.insert(new Point(10, 10));
+        quadTree.insert(new Point(-100, 100));
+        quadTree.insert(new Point(100, -100));
+        quadTree.insert(new Point(-100, -100));
+        quadTree.insert(new Point(100, 100));
+
+        List<Point> points = quadTree.query(new BoundingBox(new Point(0, 0), 100));
+        Assertions.assertEquals(8, points.size());
+
+        points = quadTree.query(new BoundingBox(new Point(5, 5), 5));
+        Assertions.assertEquals(1, points.size());
+
+        points = quadTree.query(new BoundingBox(new Point(-200, -200), 5));
+        Assertions.assertEquals(0, points.size());
+    }
+}