Efficient Solutions for Interval Scheduling and Merging

Optimizing Disjoint Interval Selection

Problem Statement: Given a collection of time ranges represented as pairs \[start, end\], determine the minimum number of ranges that must be discarded to ensure the remaining set contains no overlaps.

Approach: This scenario is best resolved using a greedy strategy. By arranging the intervals according to their starting points, we can iterate through them sequentially. We maintain a reference to the end point of the last non-conflicting interval selected. Whenever a new interval begins before this reference point, an overlap exists. At this juncture, we increment a removal counter. Crucially, to maximize future compatibility, when an overlap is detected, we adjust our reference end point to the smaller of the two conflicting endpoints. This effectively keeps the tighter interval and discards the looser one.

public class IntervalOptimizer {
    public static int calculateMinDeletions(int[][] rangeSet) {
        // Sort ranges based on the starting coordinate
        Arrays.sort(rangeSet, (a, b) -> Integer.compare(a[0], b[0]));

        int removedCount = 0;
        // Initialize with the end of the first interval
        int prevEnd = rangeSet[0][1];

        for (int idx = 1; idx < rangeSet.length; idx++) {
            int[] current = rangeSet[idx];
            
            // Check for intersection
            if (current[0] < prevEnd) {
                removedCount++;
                // Retain the interval that ends sooner to minimize blocking future ones
                prevEnd = Math.min(prevEnd, current[1]);
            } else {
                // No overlap, move the reference to the current end
                prevEnd = current[1];
            }
        }

        return removedCount;
    }
}

Partitioning Strings by Character Boundaries

Problem Statement: You are provided with a text string inputText. The goal is to split this string into the maximum number of parts such that no character appears in more than one part. Return the length of each resulting partition.

Approach: To solve this efficiently, we first scan the entire input to record the final appearance index for every unique character present. During a second traversal, we track the farthest position required to complete the current segment. As we iterate, we update the segment's boundary based on the latest occurrence of the current character. Once the iterasion index reaches this calculated boundary, we know all characters within that window have been accounted for, allowing us to finalize the segment and begin a new one.

public class TextSegmenter {
    public static List<Integer> getPartitionSizes(String inputText) {
        int[] lastIndexMap = new int[26];
        
        // Identify the last index for each character
        for (int i = 0; i < inputText.length(); i++) {
            lastIndexMap[inputText.charAt(i) - 'a'] = i;
        }

        List<Integer> partitionResults = new ArrayList<>();
        int segmentStart = 0;
        int segmentBoundary = 0;

        for (int i = 0; i < inputText.length(); i++) {
            // Expand the current segment boundary if needed
            segmentBoundary = Math.max(segmentBoundary, lastIndexMap[inputText.charAt(i) - 'a']);

            // If the current index matches the boundary, close the segment
            if (i == segmentBoundary) {
                partitionResults.add(i - segmentStart + 1);
                segmentStart = i + 1;
            }
        }
        return partitionResults;
    }
}

Merging Overlapping Time Ranges

Problem Statement: Accept a list of intervals where overlapping regions exist. Combine these intersecting ranges into a single contiguous span and produce a clean list containing only non-overlapping merged intervals that cover the original data completely.

Approach: Similar to finding overlaps, the key here is to sort the dataset by the initial coordinate. We then initialize a collection with the first sorted interval. For each subsequent interval, we compare its start point against the end point of the last item added to our result list. If the new start is less than or equal to the existing end, an overlap is confirmed. In this case, we merge them by updating the end of the last recorded interval to the maximum of both ends. If there is no overlap, the current interval is added as a distinct entry.

public class RangeMerger {
    public static int[][] unifyRanges(int[][] rawIntervals) {
        if (rawIntervals.length == 0) {
            return new int[0][];
        }

        // Order intervals by start time
        Arrays.sort(rawIntervals, (a, b) -> Integer.compare(a[0], b[0]));

        List<int[]> mergedList = new ArrayList<>();
        
        for (int[] interval : rawIntervals) {
            // If list is empty or no overlap with previous interval
            if (mergedList.isEmpty() || mergedList.get(mergedList.size() - 1)[1] < interval[0]) {
                mergedList.add(interval);
            } else {
                // Overlap detected: merge by extending the end boundary
                int[] last = mergedList.get(mergedList.size() - 1);
                last[1] = Math.max(last[1], interval[1]);
            }
        }

        return mergedList.toArray(new int[mergedList.size()][]);
    }
}

Tags: java greedy-algorithms interval-processing string-partitioning data-manipulation

Posted on Thu, 29 Oct 2026 16:27:34 +0000 by leapingfrog