leetcode
leetcode 2201 ~ 2250
统计将重叠区间合并成组的方案数

统计将重叠区间合并成组的方案数

难度:

标签:

题目描述

You are given a 2D integer array ranges where ranges[i] = [starti, endi] denotes that all integers between starti and endi (both inclusive) are contained in the ith range.

You are to split ranges into two (possibly empty) groups such that:

  • Each range belongs to exactly one group.
  • Any two overlapping ranges must belong to the same group.

Two ranges are said to be overlapping if there exists at least one integer that is present in both ranges.

  • For example, [1, 3] and [2, 5] are overlapping because 2 and 3 occur in both ranges.

Return the total number of ways to split ranges into two groups. Since the answer may be very large, return it modulo 109 + 7.

 

Example 1:

Input: ranges = [[6,10],[5,15]]
Output: 2
Explanation: 
The two ranges are overlapping, so they must be in the same group.
Thus, there are two possible ways:
- Put both the ranges together in group 1.
- Put both the ranges together in group 2.

Example 2:

Input: ranges = [[1,3],[10,20],[2,5],[4,8]]
Output: 4
Explanation: 
Ranges [1,3], and [2,5] are overlapping. So, they must be in the same group.
Again, ranges [2,5] and [4,8] are also overlapping. So, they must also be in the same group. 
Thus, there are four possible ways to group them:
- All the ranges in group 1.
- All the ranges in group 2.
- Ranges [1,3], [2,5], and [4,8] in group 1 and [10,20] in group 2.
- Ranges [1,3], [2,5], and [4,8] in group 2 and [10,20] in group 1.

 

Constraints:

  • 1 <= ranges.length <= 105
  • ranges[i].length == 2
  • 0 <= starti <= endi <= 109

代码结果

运行时间: 75 ms, 内存: 44.0 MB


/*
 * Problem: Given a 2D integer array 'ranges' where ranges[i] = [start_i, end_i] 
 * indicates that all integers from start_i to end_i (inclusive) are covered 
 * by the i-th interval.
 * We need to split 'ranges' into two groups such that:
 * 1. Each interval belongs to exactly one group.
 * 2. Intervals with intersection must be in the same group.
 * The task is to return the number of ways to split 'ranges' into two groups,
 * with the result modulo 10^9 + 7.
 */

import java.util.*;
import java.util.stream.*;

public class Solution {
    private static final int MOD = 1_000_000_007;

    public int countWays(int[][] ranges) {
        // Sort intervals based on their start times
        Arrays.sort(ranges, Comparator.comparingInt(a -> a[0]));

        int totalGroups = (int) IntStream.range(0, ranges.length - 1)
                .filter(i -> ranges[i + 1][0] > ranges[i][1])
                .count() + 1;

        // There are 2^totalGroups ways to assign groups
        long result = IntStream.range(0, totalGroups)
                .mapToLong(i -> 2)
                .reduce(1, (a, b) -> (a * b) % MOD);

        return (int) result;
    }

    public static void main(String[] args) {
        Solution sol = new Solution();
        int[][] ranges1 = {{6, 10}, {5, 15}};
        int[][] ranges2 = {{1, 3}, {10, 20}, {2, 5}, {4, 8}};
        System.out.println(sol.countWays(ranges1)); // Output: 2
        System.out.println(sol.countWays(ranges2)); // Output: 4
    }
}

解释

方法:

题解中采用了图的连通分量的思想。首先,通过对区间按照起始点进行排序,确保处理顺序的逻辑性。在遍历排序后的区间时,用一个变量bound来维持当前已遍历区间的最大结束点。如果当前区间的起始点大于bound,说明此区间与之前的区间不连续,即形成了一个新的连通分量。变量n记录这样的连通分量的数量。每一个连通分量都可以独立选择放在两个不同的组中,因此最终的方案数是2的n次幂,使用pow函数可以高效地计算模1_000_000_007下的结果。

时间复杂度:

O(n log n)

空间复杂度:

O(1)

代码细节讲解

🦆
题解中提到使用图的连通分量来处理问题,这种方法是怎样准确地识别出所有连通分量的?
在题解中,通过将区间按照其起始点进行排序,确保了我们在处理它们的时候遵循时间序。这允许我们顺序检查每个区间与前一个区间是否存在重叠或连续性。使用一个变量`bound`来记录当前连通分量的最大结束点,每次遇到一个新区间,我们会检查这个区间的起始点是否大于`bound`。如果是,这意味着当前区间无法与先前的区间连续,从而标志着一个新的连通分量的开始。这个方法通过顺序遍历和比较,能够准确地辨别并计算所有连通分量的数量。
🦆
为什么在处理完一个连通分量后,可以直接将连通分量的数量增加而不需要额外的检查或验证?
当我们确定一个区间的起始点大于之前所有区间的最大结束点`bound`时,这个区间显然与之前的区间不再连续,因此它必然开始了一个新的连通分量。这个逻辑的正确性基于我们已经按照起始点排序的前提。因此,每次这种情况发生时,我们可以确信一个新的连通分量已经开始,无需进行额外的检查或验证。
🦆
题解中提到,如果区间的起始点大于当前连通分量的最大结束点`bound`,则会形成新的连通分量。这里的逻辑是否假定了区间列表已经被完全排序?如果排序不完全会怎样影响结果?
是的,这个逻辑完全基于区间列表已经根据起始点进行了排序的假设。如果区间没有被完全排序,那么我们可能无法正确识别新的连通分量。例如,如果一个较早开始的区间在排序后的位置较晚,这可能导致我们错误地将其视为新的连通分量,从而错误地计算连通分量的总数。因此,排序是识别连通分量的关键步骤,对于算法的正确性至关重要。
🦆
在更新`bound`变量时,只有当新区间的结束点`r`大于`bound`时才进行更新,这种更新策略是否充分考虑了所有情况?例如如果新区间完全包含在前一个区间内怎么处理?
这种更新策略确实考虑了所有可能的情况,包括新区间完全被前一个区间包含的情况。在这种情况下,新区间的结束点`r`不会大于当前的`bound`,因此`bound`不需要更新。这是因为`bound`始终表示当前连通分量中所有区间的最大结束点,无论这些区间的相对包含关系如何。只有当一个区间有更远的结束点时,我们才需要更新`bound`,以保证它覆盖所有相关的区间。

相关问题