Skip to content

refactor: LineSweep #5398

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Aug 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 30 additions & 23 deletions src/main/java/com/thealgorithms/others/LineSweep.java
Original file line number Diff line number Diff line change
@@ -1,55 +1,62 @@
package com.thealgorithms.others;

import java.util.Arrays;
import java.util.Comparator;

/* Line Sweep algorithm can be used to solve range problems by first sorting the list of ranges
* by the start value of the range in non-decreasing order and doing a "sweep" through the number
* line(x-axis) by incrementing the start point by 1 and decrementing the end point+1 by 1 on the
* number line.
* An overlapping range is defined as (StartA <= EndB) AND (EndA >= StartB)
* References
* https://en.wikipedia.org/wiki/Sweep_line_algorithm
* https://en.wikipedia.org/wiki/De_Morgan%27s_laws>
/**
* The Line Sweep algorithm is used to solve range problems efficiently. It works by:
* 1. Sorting a list of ranges by their start values in non-decreasing order.
* 2. Sweeping through the number line (x-axis) while updating a count for each point based on the ranges.
*
* An overlapping range is defined as:
* - (StartA <= EndB) AND (EndA >= StartB)
*
* References:
* - https://en.wikipedia.org/wiki/Sweep_line_algorithm
* - https://en.wikipedia.org/wiki/De_Morgan%27s_laws
*/
public final class LineSweep {
private LineSweep() {
}

/**
* Find Maximum end point
* param = ranges : Array of range[start,end]
* return Maximum Endpoint
* Finds the maximum endpoint from a list of ranges.
*
* @param ranges a 2D array where each element is a range represented by [start, end]
* @return the maximum endpoint among all ranges
*/
public static int findMaximumEndPoint(int[][] ranges) {
Arrays.sort(ranges, Comparator.comparingInt(a -> a[1]));
Arrays.sort(ranges, Comparator.comparingInt(range -> range[1]));
return ranges[ranges.length - 1][1];
}

/**
* Find if any ranges overlap
* param = ranges : Array of range[start,end]
* return true if overlap exists false otherwise.
* Determines if any of the given ranges overlap.
*
* @param ranges a 2D array where each element is a range represented by [start, end]
* @return true if any ranges overlap, false otherwise
*/
public static boolean isOverlap(int[][] ranges) {
if (ranges == null || ranges.length == 0) {
return false;
}

int maximumEndPoint = findMaximumEndPoint(ranges);
Arrays.sort(ranges, Comparator.comparingInt(a -> a[0]));
int[] numberLine = new int[maximumEndPoint + 2];
for (int[] range : ranges) {

int start = range[0];
int end = range[1];

numberLine[start] += 1;
numberLine[end + 1] -= 1;
}

int current = 0;
int overlaps = 0;
for (int num : numberLine) {
current += num;
overlaps = Math.max(overlaps, current);
int currentCount = 0;
int maxOverlaps = 0;
for (int count : numberLine) {
currentCount += count;
maxOverlaps = Math.max(maxOverlaps, currentCount);
}
return overlaps > 1;
return maxOverlaps > 1;
}
}
45 changes: 26 additions & 19 deletions src/test/java/com/thealgorithms/others/LineSweepTest.java
Original file line number Diff line number Diff line change
@@ -1,30 +1,37 @@
package com.thealgorithms.others;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;

import org.junit.jupiter.api.Test;
import java.util.stream.Stream;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;

public class LineSweepTest {
private record OverlapTestCase(int[][] ranges, boolean expected) {
}

@Test
void testForOverlap() {
int[][] arr = {{0, 10}, {7, 20}, {15, 24}};
assertTrue(LineSweep.isOverlap(arr));
private record MaximumEndPointTestCase(int[][] ranges, int expected) {
}

@Test
void testForNoOverlap() {
int[][] arr = {{0, 10}, {11, 20}, {21, 24}};
assertFalse(LineSweep.isOverlap(arr));
@ParameterizedTest
@MethodSource("provideOverlapTestData")
void testIsOverlap(OverlapTestCase testCase) {
assertEquals(testCase.expected(), LineSweep.isOverlap(testCase.ranges()));
}
@Test
void testForOverlapWhenEndAEqualsStartBAndViceVersa() {
int[][] arr = {{0, 10}, {10, 20}, {21, 24}};
assertTrue(LineSweep.isOverlap(arr));

private static Stream<Arguments> provideOverlapTestData() {
return Stream.of(Arguments.of(new OverlapTestCase(new int[][] {{0, 10}, {7, 20}, {15, 24}}, true)), Arguments.of(new OverlapTestCase(new int[][] {{0, 10}, {11, 20}, {21, 24}}, false)), Arguments.of(new OverlapTestCase(new int[][] {{0, 10}, {10, 20}, {21, 24}}, true)),
Arguments.of(new OverlapTestCase(new int[][] {{5, 10}}, false)), Arguments.of(new OverlapTestCase(new int[][] {{1, 5}, {1, 5}, {1, 5}}, true)), Arguments.of(new OverlapTestCase(new int[][] {{1, 1}, {2, 2}, {3, 3}}, false)), Arguments.of(new OverlapTestCase(new int[][] {}, false)));
}
@Test
void testForMaximumEndPoint() {
int[][] arr = {{10, 20}, {1, 100}, {14, 16}, {1, 8}};
assertEquals(100, LineSweep.findMaximumEndPoint(arr));

@ParameterizedTest
@MethodSource("provideMaximumEndPointTestData")
void testFindMaximumEndPoint(MaximumEndPointTestCase testCase) {
assertEquals(testCase.expected(), LineSweep.findMaximumEndPoint(testCase.ranges()));
}

private static Stream<Arguments> provideMaximumEndPointTestData() {
return Stream.of(Arguments.of(new MaximumEndPointTestCase(new int[][] {{10, 20}, {1, 100}, {14, 16}, {1, 8}}, 100)));
}
}