题目:
You are given a 2D integer array intervals
where intervals[i] = [lefti, righti]
represents the inclusive interval [lefti, righti]
.
You have to divide the intervals into one or more groups such that each interval is in exactly one group, and no two intervals that are in the same group intersect each other.
Return the minimum number of groups you need to make.
Two intervals intersect if there is at least one common number between them. For example, the intervals [1, 5]
and [5, 8]
intersect.
Example 1:
Input: intervals = [[5,10],[6,8],[1,5],[2,3],[1,10]] Output: 3 Explanation: We can divide the intervals into the following groups: - Group 1: [1, 5], [6, 8]. - Group 2: [2, 3], [5, 10]. - Group 3: [1, 10]. It can be proven that it is not possible to divide the intervals into fewer than 3 groups.
我用的语言是GO,说是4种,其实本质上只有两大类解法
第一类:找出每个点有多少个区间,时间O(n)
参考文章:C++ Easy Solution
其实原理也不是很难理解,例如在K个区间里任取两个都是相交的,那么就要分配至少K组
// 统计每个点的区间树,找出最大的一个相交点,至少要分成这么多个组
func solution4(intervals [][]int) int {
// 区间起点+1,终点的右边-1表示区间结束
// 将每个点的数字累计起来,即可得到每个点的区间数量
counter := make([]int, 1000002)
minPoint, maxPoint := math.MaxInt32, -1
for _, interval := range intervals {
s, e := interval[0], interval[1]
counter[s]++
counter[e+1]--
if s < minPoint {
minPoint = s
}
if e > maxPoint {
maxPoint = e
}
}
ans := 0
for i := minPoint; i <= maxPoint; i++ {
counter[i] += counter[i-1]
if counter[i] > ans {
ans = counter[i]
}
}
return ans
}
type position struct {
pos int
affect int
}
type positionSlice []position
func (s positionSlice) Len() int { return len(s) }
func (s positionSlice) Less(i, j int) bool {
if s[i].pos == s[j].pos { // 某一个点同时有右和左,先统计左
return s[i].affect > s[j].affect
}
return s[i].pos < s[j].pos
}
func (s positionSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
// 榜1写法:其实是solution 4的优化, 将区间和延后计算
func solution7(intervals [][]int) int {
counter := make([]position, 0)
for _, interval := range intervals {
counter = append(counter, position{interval[0], 1})
counter = append(counter, position{interval[1], -1})
}
sort.Sort(positionSlice(counter))
ans, cur := 0, 0
for _, pos := range counter {
cur += pos.affect
if cur > ans {
ans = cur
}
}
return ans
}
第二类: 排序法。时间复杂度O(nlogn)
参考:[Java] 39ms 100% || much faster || Meeting Room II || 1 line core logic
// 分别用两个数组存储区间开始和结束时间,并升序排序
// 把区间想象成会议开始到结束的时间,找到最早结束的一个会议,然后在这之前开始的都要单独安排一个会议室
// 然后用一个指针指向当前将要结束的会议,另一个指针指向下一个要安排的会议
// 这个和solution3本质上是一个方法,不过solution3的意图没有这么明显,把很多逻辑压缩到了一起
func solution5(intervals [][]int) int {
start, end := &Heap{}, &Heap{}
heap.Init(start)
heap.Init(end)
for _, interval := range intervals {
heap.Push(start, interval[0])
heap.Push(end, interval[1])
}
ans := 0
for start.Len() > 0 {
if heap.Pop(start).(int) <= end.IntSlice[0] {
ans++
} else {
heap.Pop(end)
}
}
return ans
}
// 压缩的写法
// 按照start排序, 用一个最小堆存放每个Group最后一个X的end值. 每次遇到相交则往heap push,否则pop然后push
func solution3(intervals [][]int) int {
sort.Sort(IntervalSlice(intervals))
h := &Heap{}
heap.Init(h)
heap.Push(h, intervals[0][1])
for i := 1; i < len(intervals); i++ {
if intervals[i][0] > h.IntSlice[0] {
heap.Pop(h)
}
heap.Push(h, intervals[i][1])
}
return h.Len()
}
辅助代码
type IntervalSlice [][]int
func (s IntervalSlice) Len() int { return len(s) }
func (s IntervalSlice) Less(i, j int) bool { return s[i][0] < s[j][0] }
func (s IntervalSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
type Heap struct {
sort.IntSlice
}
func (h *Heap) Push(x interface{}) {
h.IntSlice = append(h.IntSlice, x.(int))
}
func (h *Heap) Pop() interface{} {
x := h.IntSlice[h.Len()-1]
h.IntSlice = h.IntSlice[:h.Len()-1]
return x
}
另外吐槽下实际运行的结果第一种并没有快多少