diff --git a/src/main/java/com/thealgorithms/datastructures/bloomfilter/BloomFilter.java b/src/main/java/com/thealgorithms/datastructures/bloomfilter/BloomFilter.java
index 90625ad1c902..d6347cdd293e 100644
--- a/src/main/java/com/thealgorithms/datastructures/bloomfilter/BloomFilter.java
+++ b/src/main/java/com/thealgorithms/datastructures/bloomfilter/BloomFilter.java
@@ -15,7 +15,6 @@
*
* @param The type of elements to be stored in the Bloom filter.
*/
-@SuppressWarnings("rawtypes")
public class BloomFilter {
private final int numberOfHashFunctions;
@@ -39,7 +38,7 @@ public BloomFilter(int numberOfHashFunctions, int bitArraySize) {
}
this.numberOfHashFunctions = numberOfHashFunctions;
this.bitArray = new BitSet(bitArraySize);
- this.hashFunctions = new Hash[numberOfHashFunctions];
+ this.hashFunctions = (Hash[]) new Hash>[numberOfHashFunctions];
initializeHashFunctions();
}
@@ -121,6 +120,8 @@ private static class Hash {
*
* The hash value is calculated by multiplying the index of the hash function
* with the ASCII sum of the string representation of the key.
+ * For array types, the content of the array is used instead of the default
+ * toString.
*
*
* @param key the element to hash
@@ -143,9 +144,10 @@ public int compute(T key) {
private int asciiString(String word) {
int sum = 0;
for (char c : word.toCharArray()) {
- sum += c;
- }
- return sum;
+ sum += c;
+ }
+ return sum;
+ }
}
/**
@@ -174,4 +176,3 @@ private int contentHash(Object key) {
return asciiString(String.valueOf(key));
}
}
-}
diff --git a/src/main/java/com/thealgorithms/others/SkylineProblem.java b/src/main/java/com/thealgorithms/others/SkylineProblem.java
index e84a5c5b585b..d9e2a8cc2547 100644
--- a/src/main/java/com/thealgorithms/others/SkylineProblem.java
+++ b/src/main/java/com/thealgorithms/others/SkylineProblem.java
@@ -1,17 +1,58 @@
package com.thealgorithms.others;
import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
/**
- * The {@code SkylineProblem} class is used to solve the skyline problem using a
- * divide-and-conquer approach.
- * It reads input for building data, processes it to find the skyline, and
- * prints the skyline.
+ * Skyline Problem
+ *
+ * Solves the classic skyline problem using a divide-and-conquer approach. Given
+ * a list of buildings (each defined by left, height, right),
+ * computes the silhouette (skyline) formed by these buildings when viewed from
+ * a distance.
+ *
+ *
+ * Usage example:
+ *
+ *
+ * SkylineProblem sp = new SkylineProblem();
+ * sp.building = new SkylineProblem.Building[3];
+ * sp.add(1, 10, 5);
+ * sp.add(2, 15, 7);
+ * sp.add(3, 12, 9);
+ * List skyline = sp.findSkyline(0, 2);
+ *
+ *
+ *
+ * This class is not thread-safe.
+ *
*/
public class SkylineProblem {
- Building[] building;
- int count;
+ /**
+ * Array of buildings to process. Must be initialized before use.
+ */
+ private Building[] building;
+
+ /**
+ * Number of buildings added so far.
+ */
+ public int count;
+
+ /**
+ * Sets the building array to the specified size.
+ *
+ * @param size The size of the building array.
+ * @throws IllegalArgumentException if size is negative
+ */
+ public void setBuilding(int size) {
+ if (size < 0) {
+ throw new IllegalArgumentException("Size must be non-negative");
+ }
+ this.building = new Building[size];
+ this.count = 0;
+ }
/**
* Adds a building with the given left, height, and right values to the
@@ -20,8 +61,23 @@ public class SkylineProblem {
* @param left The left x-coordinate of the building.
* @param height The height of the building.
* @param right The right x-coordinate of the building.
+ * @throws IllegalArgumentException if left >= right or height < 0
+ * @throws IllegalStateException if building array is not initialized or is
+ * full
*/
public void add(int left, int height, int right) {
+ if (building == null) {
+ throw new IllegalStateException("Building array not initialized");
+ }
+ if (count >= building.length) {
+ throw new IllegalStateException("Building array is full");
+ }
+ if (left >= right) {
+ throw new IllegalArgumentException("Left coordinate must be less than right coordinate");
+ }
+ if (height < 0) {
+ throw new IllegalArgumentException("Height must be non-negative");
+ }
building[count++] = new Building(left, height, right);
}
@@ -29,92 +85,99 @@ public void add(int left, int height, int right) {
* Computes the skyline for a range of buildings using the divide-and-conquer
* strategy.
*
- * @param start The starting index of the buildings to process.
- * @param end The ending index of the buildings to process.
+ * @param start The starting index of the buildings to process (inclusive).
+ * @param end The ending index of the buildings to process (inclusive).
* @return A list of {@link Skyline} objects representing the computed skyline.
+ * @throws IllegalArgumentException if indices are out of bounds or building
+ * array is null
*/
- public ArrayList findSkyline(int start, int end) {
+ public List findSkyline(int start, int end) {
+ if (building == null) {
+ throw new IllegalArgumentException("Building array is not initialized");
+ }
+ if (start < 0 || end >= count || start > end) {
+ throw new IllegalArgumentException("Invalid start or end index");
+ }
// Base case: only one building, return its skyline.
if (start == end) {
- ArrayList list = new ArrayList<>();
+ List list = new ArrayList<>();
list.add(new Skyline(building[start].left, building[start].height));
- list.add(new Skyline(building[end].right, 0)); // Add the end of the building
+ list.add(new Skyline(building[start].right, 0)); // Add the end of the building
return list;
}
int mid = (start + end) / 2;
-
- ArrayList sky1 = this.findSkyline(start, mid); // Find the skyline of the left half
- ArrayList sky2 = this.findSkyline(mid + 1, end); // Find the skyline of the right half
+ List sky1 = this.findSkyline(start, mid); // Find the skyline of the left half
+ List sky2 = this.findSkyline(mid + 1, end); // Find the skyline of the right half
return this.mergeSkyline(sky1, sky2); // Merge the two skylines
}
/**
* Merges two skylines (sky1 and sky2) into one combined skyline.
*
- * @param sky1 The first skyline list.
- * @param sky2 The second skyline list.
+ * @param sky1 The first skyline list. Not modified.
+ * @param sky2 The second skyline list. Not modified.
* @return A list of {@link Skyline} objects representing the merged skyline.
+ * @throws NullPointerException if either argument is null
*/
- public ArrayList mergeSkyline(ArrayList sky1, ArrayList sky2) {
- int currentH1 = 0;
- int currentH2 = 0;
- ArrayList skyline = new ArrayList<>();
- int maxH = 0;
-
- // Merge the two skylines
- while (!sky1.isEmpty() && !sky2.isEmpty()) {
- if (sky1.get(0).coordinates < sky2.get(0).coordinates) {
- int currentX = sky1.get(0).coordinates;
- currentH1 = sky1.get(0).height;
-
- if (currentH1 < currentH2) {
- sky1.remove(0);
- if (maxH != currentH2) {
- skyline.add(new Skyline(currentX, currentH2));
- }
- } else {
- maxH = currentH1;
- sky1.remove(0);
- skyline.add(new Skyline(currentX, currentH1));
- }
- } else {
- int currentX = sky2.get(0).coordinates;
- currentH2 = sky2.get(0).height;
-
- if (currentH2 < currentH1) {
- sky2.remove(0);
- if (maxH != currentH1) {
- skyline.add(new Skyline(currentX, currentH1));
- }
- } else {
- maxH = currentH2;
- sky2.remove(0);
- skyline.add(new Skyline(currentX, currentH2));
- }
+ public List mergeSkyline(List sky1, List sky2) {
+ Objects.requireNonNull(sky1, "sky1 must not be null");
+ Objects.requireNonNull(sky2, "sky2 must not be null");
+ int i = 0;
+ int j = 0;
+ int h1 = 0;
+ int h2 = 0;
+ int prevHeight = 0;
+ List result = new ArrayList<>();
+ while (i < sky1.size() && j < sky2.size()) {
+ Skyline p1 = sky1.get(i);
+ Skyline p2 = sky2.get(j);
+ int x;
+ if (p1.coordinates < p2.coordinates) {
+ x = p1.coordinates;
+ h1 = p1.height;
+ i++;
+ } else if (p2.coordinates < p1.coordinates) {
+ x = p2.coordinates;
+ h2 = p2.height;
+ j++;
+ } else { // same x
+ x = p1.coordinates;
+ h1 = p1.height;
+ h2 = p2.height;
+ i++;
+ j++;
+ }
+ int maxH = Math.max(h1, h2);
+ if (result.isEmpty() || prevHeight != maxH) {
+ result.add(new Skyline(x, maxH));
+ prevHeight = maxH;
}
}
-
- // Add any remaining points from sky1 or sky2
- while (!sky1.isEmpty()) {
- skyline.add(sky1.get(0));
- sky1.remove(0);
+ // Append remaining points
+ while (i < sky1.size()) {
+ Skyline p = sky1.get(i++);
+ if (result.isEmpty() || result.get(result.size() - 1).height != p.height || result.get(result.size() - 1).coordinates != p.coordinates) {
+ result.add(new Skyline(p.coordinates, p.height));
+ }
}
-
- while (!sky2.isEmpty()) {
- skyline.add(sky2.get(0));
- sky2.remove(0);
+ while (j < sky2.size()) {
+ Skyline p = sky2.get(j++);
+ if (result.isEmpty() || result.get(result.size() - 1).height != p.height || result.get(result.size() - 1).coordinates != p.coordinates) {
+ result.add(new Skyline(p.coordinates, p.height));
+ }
}
-
- return skyline;
+ return result;
}
/**
- * A class representing a point in the skyline with its x-coordinate and height.
+ * Represents a point in the skyline with its x-coordinate and height.
*/
- public class Skyline {
- public int coordinates;
- public int height;
+ public static class Skyline {
+ /** The x-coordinate of the skyline point. */
+ public final int coordinates;
+ /** The height of the skyline at the given coordinate. */
+ public final int height;
/**
* Constructor for the {@code Skyline} class.
@@ -126,16 +189,40 @@ public Skyline(int coordinates, int height) {
this.coordinates = coordinates;
this.height = height;
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ Skyline skyline = (Skyline) o;
+ return coordinates == skyline.coordinates && height == skyline.height;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(coordinates, height);
+ }
+
+ @Override
+ public String toString() {
+ return "(" + coordinates + ", " + height + ")";
+ }
}
/**
- * A class representing a building with its left, height, and right
- * x-coordinates.
+ * Represents a building with its left, height, and right x-coordinates.
*/
- public class Building {
- public int left;
- public int height;
- public int right;
+ public static class Building {
+ /** The left x-coordinate of the building. */
+ public final int left;
+ /** The height of the building. */
+ public final int height;
+ /** The right x-coordinate of the building. */
+ public final int right;
/**
* Constructor for the {@code Building} class.
@@ -149,5 +236,11 @@ public Building(int left, int height, int right) {
this.height = height;
this.right = right;
}
+
+ @Override
+ public String toString() {
+ return "Building{"
+ + "left=" + left + ", height=" + height + ", right=" + right + '}';
+ }
}
}
diff --git a/src/test/java/com/thealgorithms/others/SkylineProblemTest.java b/src/test/java/com/thealgorithms/others/SkylineProblemTest.java
index 1ed5ced709c1..3112d769debf 100644
--- a/src/test/java/com/thealgorithms/others/SkylineProblemTest.java
+++ b/src/test/java/com/thealgorithms/others/SkylineProblemTest.java
@@ -1,86 +1,130 @@
package com.thealgorithms.others;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
-import java.util.ArrayList;
+import java.util.List;
import org.junit.jupiter.api.Test;
-public class SkylineProblemTest {
+class SkylineProblemTest {
@Test
- public void testSingleBuildingSkyline() {
+ void testSingleBuildingSkyline() {
SkylineProblem skylineProblem = new SkylineProblem();
- skylineProblem.building = new SkylineProblem.Building[1];
+ skylineProblem.setBuilding(1);
skylineProblem.add(2, 10, 9);
- ArrayList result = skylineProblem.findSkyline(0, 0);
+ List result = skylineProblem.findSkyline(0, 0);
- assertEquals(2, result.get(0).coordinates);
- assertEquals(10, result.get(0).height);
- assertEquals(9, result.get(1).coordinates);
- assertEquals(0, result.get(1).height);
+ assertEquals(List.of(new SkylineProblem.Skyline(2, 10), new SkylineProblem.Skyline(9, 0)), result);
}
@Test
- public void testTwoBuildingsSkyline() {
+ void testTwoBuildingsSkyline() {
SkylineProblem skylineProblem = new SkylineProblem();
- skylineProblem.building = new SkylineProblem.Building[2];
+ skylineProblem.setBuilding(2);
skylineProblem.add(1, 11, 5);
skylineProblem.add(2, 6, 7);
- ArrayList result = skylineProblem.findSkyline(0, 1);
+ List result = skylineProblem.findSkyline(0, 1);
// Expected skyline points: (1, 11), (5, 6), (7, 0)
- assertEquals(1, result.get(0).coordinates);
- assertEquals(11, result.get(0).height);
- assertEquals(5, result.get(1).coordinates);
- assertEquals(6, result.get(1).height);
- assertEquals(7, result.get(2).coordinates);
- assertEquals(0, result.get(2).height);
+ assertEquals(List.of(new SkylineProblem.Skyline(1, 11), new SkylineProblem.Skyline(5, 6), new SkylineProblem.Skyline(7, 0)), result);
}
@Test
- public void testMergeSkyline() {
+ void testMergeSkyline() {
+ List sky1 = List.of(new SkylineProblem.Skyline(2, 10), new SkylineProblem.Skyline(9, 0));
+ List sky2 = List.of(new SkylineProblem.Skyline(3, 15), new SkylineProblem.Skyline(7, 0));
SkylineProblem skylineProblem = new SkylineProblem();
- ArrayList sky1 = new ArrayList<>();
- ArrayList sky2 = new ArrayList<>();
-
- sky1.add(skylineProblem.new Skyline(2, 10));
- sky1.add(skylineProblem.new Skyline(9, 0));
-
- sky2.add(skylineProblem.new Skyline(3, 15));
- sky2.add(skylineProblem.new Skyline(7, 0));
-
- ArrayList result = skylineProblem.mergeSkyline(sky1, sky2);
+ List result = skylineProblem.mergeSkyline(sky1, sky2);
// Expected merged skyline: (2, 10), (3, 15), (7, 10), (9, 0)
- assertEquals(2, result.get(0).coordinates);
- assertEquals(10, result.get(0).height);
- assertEquals(3, result.get(1).coordinates);
- assertEquals(15, result.get(1).height);
- assertEquals(7, result.get(2).coordinates);
- assertEquals(10, result.get(2).height);
- assertEquals(9, result.get(3).coordinates);
- assertEquals(0, result.get(3).height);
+ assertEquals(List.of(new SkylineProblem.Skyline(2, 10), new SkylineProblem.Skyline(3, 15), new SkylineProblem.Skyline(7, 10), new SkylineProblem.Skyline(9, 0)), result);
}
@Test
- public void testMultipleBuildingsSkyline() {
+ void testMultipleBuildingsSkyline() {
SkylineProblem skylineProblem = new SkylineProblem();
- skylineProblem.building = new SkylineProblem.Building[3];
+ skylineProblem.setBuilding(3);
skylineProblem.add(1, 10, 5);
skylineProblem.add(2, 15, 7);
skylineProblem.add(3, 12, 9);
- ArrayList result = skylineProblem.findSkyline(0, 2);
+ List result = skylineProblem.findSkyline(0, 2);
+
+ assertEquals(List.of(new SkylineProblem.Skyline(1, 10), new SkylineProblem.Skyline(2, 15), new SkylineProblem.Skyline(7, 12), new SkylineProblem.Skyline(9, 0)), result);
+ }
+
+ @Test
+ void testAddBuildingInvalidCases() {
+ SkylineProblem skylineProblem = new SkylineProblem();
+ // Not initialized
+ Exception ex = assertThrows(IllegalStateException.class, () -> skylineProblem.add(1, 2, 3));
+ assertTrue(ex.getMessage().contains("not initialized"));
+
+ skylineProblem.setBuilding(1);
+ skylineProblem.add(1, 2, 3);
+ // Array full
+ Exception ex2 = assertThrows(IllegalStateException.class, () -> skylineProblem.add(4, 5, 6));
+ assertTrue(ex2.getMessage().contains("full"));
+
+ // Invalid left >= right
+ SkylineProblem skylineProblem2 = new SkylineProblem();
+ skylineProblem2.setBuilding(1);
+ Exception ex3 = assertThrows(IllegalArgumentException.class, () -> skylineProblem2.add(5, 2, 2));
+ assertTrue(ex3.getMessage().contains("Left coordinate"));
+
+ // Invalid height < 0
+ Exception ex4 = assertThrows(IllegalArgumentException.class, () -> skylineProblem2.add(1, -1, 2));
+ assertTrue(ex4.getMessage().contains("Height must be non-negative"));
+ }
+
+ @Test
+ void testFindSkylineInvalidCases() {
+ SkylineProblem skylineProblem = new SkylineProblem();
+ // Not initialized
+ Exception ex = assertThrows(IllegalArgumentException.class, () -> skylineProblem.findSkyline(0, 0));
+ assertTrue(ex.getMessage().contains("not initialized"));
+
+ skylineProblem.setBuilding(2);
+ skylineProblem.count = 1;
+ Exception ex2 = assertThrows(IllegalArgumentException.class, () -> skylineProblem.findSkyline(0, 1));
+ assertTrue(ex2.getMessage().contains("Invalid start or end index"));
+ }
+
+ @Test
+ void testMergeSkylineNullCases() {
+ SkylineProblem skylineProblem = new SkylineProblem();
+ Exception ex1 = assertThrows(NullPointerException.class, () -> skylineProblem.mergeSkyline(null, List.of()));
+ Exception ex2 = assertThrows(NullPointerException.class, () -> skylineProblem.mergeSkyline(List.of(), null));
+ assertTrue(ex1.getMessage().contains("sky1"));
+ assertTrue(ex2.getMessage().contains("sky2"));
+ }
+
+ @Test
+ void testSkylineEqualsAndHashCode() {
+ SkylineProblem.Skyline s1 = new SkylineProblem.Skyline(1, 2);
+ SkylineProblem.Skyline s2 = new SkylineProblem.Skyline(1, 2);
+ SkylineProblem.Skyline s3 = new SkylineProblem.Skyline(2, 2);
+ assertEquals(s1, s2);
+ assertEquals(s1.hashCode(), s2.hashCode());
+ assertNotEquals(s1, s3);
+ assertNotEquals(null, s1);
+ assertNotEquals("string", s1);
+ }
+
+ @Test
+ void testSkylineToString() {
+ SkylineProblem.Skyline s = new SkylineProblem.Skyline(5, 10);
+ assertEquals("(5, 10)", s.toString());
+ }
- assertEquals(1, result.get(0).coordinates);
- assertEquals(10, result.get(0).height);
- assertEquals(2, result.get(1).coordinates);
- assertEquals(15, result.get(1).height);
- assertEquals(7, result.get(2).coordinates);
- assertEquals(12, result.get(2).height);
- assertEquals(9, result.get(3).coordinates);
- assertEquals(0, result.get(3).height);
+ @Test
+ void testBuildingToString() {
+ SkylineProblem.Building b = new SkylineProblem.Building(1, 2, 3);
+ assertEquals("Building{left=1, height=2, right=3}", b.toString());
}
}