给你一个细长的画,用数轴表示。这幅画由若干有重叠的线段表示,每个线段有 独一无二 的颜色。给你二维整数数组 segments
,其中 segments[i] = [starti, endi, colori]
表示线段为 半开区间 [starti, endi)
且颜色为 colori
。
线段间重叠部分的颜色会被 混合 。如果有两种或者更多颜色混合时,它们会形成一种新的颜色,用一个 集合 表示这个混合颜色。
- 比方说,如果颜色
2
,4
和6
被混合,那么结果颜色为{2,4,6}
。
为了简化题目,你不需要输出整个集合,只需要用集合中所有元素的 和 来表示颜色集合。
你想要用 最少数目 不重叠 半开区间 来 表示 这幅混合颜色的画。这些线段可以用二维数组 painting
表示,其中 painting[j] = [leftj, rightj, mixj]
表示一个 半开区间[leftj, rightj)
的颜色 和 为 mixj
。
- 比方说,这幅画由
segments = [[1,4,5],[1,7,7]]
组成,那么它可以表示为painting = [[1,4,12],[4,7,7]]
,因为:[1,4)
由颜色{5,7}
组成(和为12
),分别来自第一个线段和第二个线段。[4,7)
由颜色{7}
组成,来自第二个线段。
请你返回二维数组 painting
,它表示最终绘画的结果(没有 被涂色的部分不出现在结果中)。你可以按 任意顺序 返回最终数组的结果。
半开区间 [a, b)
是数轴上点 a
和点 b
之间的部分,包含 点 a
且 不包含 点 b
。
示例 1:
输入:segments = [[1,4,5],[4,7,7],[1,7,9]] 输出:[[1,4,14],[4,7,16]] 解释:绘画结果可以表示为: - [1,4) 颜色为 {5,9} (和为 14),分别来自第一和第二个线段。 - [4,7) 颜色为 {7,9} (和为 16),分别来自第二和第三个线段。
示例 2:
输入:segments = [[1,7,9],[6,8,15],[8,10,7]] 输出:[[1,6,9],[6,7,24],[7,8,15],[8,10,7]] 解释:绘画结果可以以表示为: - [1,6) 颜色为 9 ,来自第一个线段。 - [6,7) 颜色为 {9,15} (和为 24),来自第一和第二个线段。 - [7,8) 颜色为 15 ,来自第二个线段。 - [8,10) 颜色为 7 ,来自第三个线段。
示例 3:
输入:segments = [[1,4,5],[1,4,7],[4,7,1],[4,7,11]] 输出:[[1,4,12],[4,7,12]] 解释:绘画结果可以表示为: - [1,4) 颜色为 {5,7} (和为 12),分别来自第一和第二个线段。 - [4,7) 颜色为 {1,11} (和为 12),分别来自第三和第四个线段。 注意,只返回一个单独的线段 [1,7) 是不正确的,因为混合颜色的集合不相同。
提示:
1 <= segments.length <= 2 * 10^4
segments[i].length == 3
1 <= starti < endi <= 10^5
1 <= colori <= 10^9
- 每种颜色
colori
互不相同。
提示 1
Can we sort the segments in a way to help solve the problem?
提示 2
How can we dynamically keep track of the sum of the current segment(s)?
解法1:差分数组 + 哈希表 + 排序
由于每条线段的起止点均为整数,因此我们可以在位置 k 处记录数轴上单位长度区间 [k,k+1) 的颜色和,这样每条线段都覆盖了若干个连续的整数坐标。为了得到数轴上每个整数的颜色和,我们需要将每个线段对数轴的影响叠加。一般的做法是,对于线段覆盖的每个整数,我们都将该整数的颜色和加上线段对应的值。
但这样的做法时间复杂度较高。因此我们可以维护每个线段对于数轴颜色和的变化量。对于每个位置为 [l,r),颜色为 c 的线段,它对于数轴颜色和的影响体现在两个部分:
- l 相对于 l−1 的颜色和增加 c;
- r 相对于 r−1 的颜色和减少 c。
一般我们可以用数轴中整数位置对应的数组(又称差分数组)来维护颜色和变化量。但此处由于颜色和对应的颜色集合可能有很多种,使得即使出现某个边界点颜色和变化量为 0,其两侧的颜色也会不同。
因此,我们使用哈希表来维护所有线段产生的变化量,在数轴上的位置对应哈希表的键,变化量对应哈希表的值。在遍历完所有线段后,我们将这些键值对按照在数轴上的位置升序排序。对于排序后的键值对,我们遍历这些键值对并对颜色和求解前缀和,就可以得出数轴上的颜色和分布。
为了返回数轴的绘画结果,我们需要记录每个颜色和对应的区间,即当前键值对位置与下一个键值对位置组成的左闭右开区间。我们用数组按照格式记录这些区间中颜色和不为零的区间,并最终返回作为答案。
另外,由于每个位置的颜色和变化量和最终的颜色和可能会超出 32 位有符号整数的上界,因此我们需要用 64 位整数存储这些值。
Java版:
class Solution {
public List<List<Long>> splitPainting(int[][] segments) {
// 计算每个位置对应的颜色和改变量并用哈希表存储
TreeMap<Integer, Long> color = new TreeMap<>();
for (int[] segment: segments) {
color.merge(segment[0], (long) segment[2], Long::sum);
color.merge(segment[1], (long) -segment[2], Long::sum);
}
int n = color.size();
long[][] diff = new long[n][2];
int id = 0;
// 将哈希表转化为数组,由于treemap是有序哈希表,所以数组是有序的
// 对数组求前缀和计算对应颜色和
for (Map.Entry<Integer, Long> entry : color.entrySet()) {
diff[id][0] = entry.getKey();
diff[id][1] = entry.getValue();
if (id > 0) {
diff[id][1] += diff[id - 1][1];
}
id++;
}
// 遍历数组生成最终绘画结果
List<List<Long>> ans = new ArrayList<>();
for (int i = 0; i < n - 1; i++) {
if (diff[i][1] > 0) {
ans.add(new ArrayList<>(List.of(diff[i][0], diff[i + 1][0], diff[i][1])));
}
}
return ans;
}
}
另一种写法:
class Solution {
public List<List<Long>> splitPainting(int[][] segments) {
Map<Integer, Long> color = new HashMap<>();
for (int[] segment: segments) {
color.merge(segment[0], (long) segment[2], Long::sum);
color.merge(segment[1], (long) -segment[2], Long::sum);
}
int n = color.size();
long[][] diff = new long[n][2];
int id = 0;
for (Map.Entry<Integer, Long> entry: color.entrySet()) {
diff[id][0] = entry.getKey();
diff[id][1] = entry.getValue();
id++;
}
Arrays.sort(diff, new Comparator<long[]>() {
public int compare(long[] diff1, long[] diff2) {
return (int) (diff1[0] - diff2[0]);
}
});
for (int i = 1; i < n; i++) {
diff[i][1] += diff[i - 1][1];
}
List<List<Long>> ans = new ArrayList<>();
for (int i = 0; i < n - 1; i++) {
if (diff[i][1] > 0) {
ans.add(new ArrayList<>(List.of(diff[i][0], diff[i + 1][0], diff[i][1])));
}
}
return ans;
}
}
Python3版:
class Solution:
def splitPainting(self, segments: List[List[int]]) -> List[List[int]]:
# 计算每个位置对应的颜色和改变量并用哈希表存储
color = defaultdict(lambda: 0)
for l, r, c in segments:
color[l] += c
color[r] -= c
n = len(color)
# 将哈希表转化为数组并按数轴坐标升序排序
diff = sorted([[k, v] for k, v in color.items()])
# 对数组求前缀和计算对应颜色和
for i in range(1, n):
diff[i][1] += diff[i - 1][1]
ans = []
# 遍历数组生成最终绘画结果
for i in range(n - 1):
if diff[i][1]:
ans.append([diff[i][0], diff[i + 1][0], diff[i][1]])
return ans
复杂度分析
- 时间复杂度:O(nlogn),其中 n 为线段的数量。维护变化量哈希表的时间复杂度为 O(n),将哈希表转化为数组并排序的时间复杂度为 O(nlogn),遍历数组求前缀和并生成返回数组的时间复杂度为 O(n)。
- 空间复杂度:O(n),即为存储变化量的哈希表和数组的空间开销。