leetcode weekly contest 310: Divide Intervals Into Minimum Number of Groups 四种解法

题目:

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
}

另外吐槽下实际运行的结果第一种并没有快多少

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值