212. go语言堆实现

630. 课程表 III

在看这道题的题解的时候总结下

package main

import (
	"container/heap"
	"sort"
)

/*
@如寒灬  的评论,  https://leetcode-cn.com/u/wanyan/  这是他的主页
为了更好的了解其贪心原则的使用我们来看看这个例子
例:[[4,6],[5,5],[2,6]]
首先对其结束时间进行排序
[[5,5],[4,6],[2,6]]

排课的起始时间为0,当前已选的课程为[]
选择第0号课程[5,5]
起始时间变为5,当前已选课程为[[5,5]]
选择第1号课程[4,6]
由于其结束时间为5+4=9大于其要求的结束时间,因此不可取
好了重点来了,对于这前两门课程我无论怎么选,都无法再多选一门课程,那我还不如直接找到其中耗时最长的那么课程,
如果其耗时比当前课程([4,6])要长,那么我就用当前课程将其替换,
此时选择的课程的数目虽然没有变化,但是起始时间变短了。给后面的课程的安排留下了更为宽阔的空间
用[4,6]替换掉[5,5]课程
起始时间变为5-5+4=4,当前已选课程为[[4,6]]
为什么当前课程能直接替换掉前面的某一课程?
要能替换掉该课程,则所有的已选课程都要能在规定的End的时间内能学完
假设当前已选课程为[A,B,C,D],然后要加入的课程为E,已知B的持续时长>E的持续时长,是否能使用E来替换掉B ?
对于B之前的数据显然没有影响
对于B之后的数据有
CurTime替换前>CurTime替换后
因为
CurTimeB之前+TimeB<EndC
=>
CurTimeB之前+TimeE < EndC

只不过是使用比较小的TimeE将TimeB替换掉了, 也就是时间少了TimeB-TimeE的差值

因此对于B之后的数据也没有影响,只是CurTime变小,留给之后安排的课程的时间变宽裕了


这个完了之后, 大概说一下go堆的使用:
go默认没有实现具体直接可以使用的结构体给你使用, 比如python里面的heaq, 以及java中的PriorityQueue优先队列
而是设置了一个接口供你自己来实现它, 而本身 heap如果要实现一个堆, 需要对内部元素进行比较排序, 所以你自己的
数据类型必须实现sortj接口的Len(),  Less(), Swap()方法
Less()这个方式是用来表示生成的是大顶堆还是小顶堆的, 比如
func (h IntHeap) Less(i, j int) bool { return h[i] < h[j] }, 输入的i, j 和比较式的顺序, 一致且比较时
使用的<那么他就是一个小丁堆, 如果把return h[i] > h[j]改为这样, 那么他就是大顶堆.
Swap() 方法是交换元素的方式, 主要在升序以及降序时使用.

同时heap自己的接口定义如下, sort.Interface 需要一个实现了sort接口的结构, 也就是上面我说的那个实现了sort接口的一个结构
比较典型的是哪些呢?sort.IntSlice, 可以看他的源码, 发现它本身就实现类sort接口的方法
type Interface interface {
	sort.Interface
	Push(x interface{}) // add x as element Len()
	Pop() interface{}   // remove and return element Len() - 1.
}

如果我们想定义一个自己的堆:
1. 定义我们要用的结构
type IntHeap []int
2. 实现sort方法
func (h IntHeap) Len() int           { return len(h) }
func (h IntHeap) Less(i, j int) bool { return h[i] < h[j] }
func (h IntHeap) Swap(i, j int)      { h[i], h[j] = h[j], h[i] }

3.实现heap的Push, 和Pop方法接口
func (h *IntHeap) Push(x interface{}) {
	// Push and Pop use pointer receivers because they modify the slice's length,
	// not just its contents.
	*h = append(*h, x.(int))
}

func (h *IntHeap) Pop() interface{} {
	old := *h
	n := len(old)
	x := old[n-1]
	*h = old[0 : n-1]
	return x
}
4. 使用heap操作我们自己的堆就可以了
func main() {
	h := &IntHeap{2, 1, 5}
	heap.Init(h)
	heap.Push(h, 3)
	fmt.Printf("minimum: %d\n", (*h)[0])
	for h.Len() > 0 {
		fmt.Printf("%d ", heap.Pop(h))
	}
}

*/

type Heap struct {
	sort.IntSlice
}

func (h Heap) Less(i, j int) bool {
	return h.IntSlice[i] > h.IntSlice[j] // less比较反过来之后, 会导致堆变成一个大顶堆, 最小的元素跑到最后面去
}

func (h *Heap) Push(x interface{}) {
	h.IntSlice = append(h.IntSlice, x.(int))
}

func (h *Heap) Pop() interface{} {
	a := h.IntSlice
	x := a[len(a)-1]
	h.IntSlice = a[:len(a)-1]
	return x
}

func scheduleCourse(courses [][]int) int {
	// 对courses进行排序将, lastDayi 小的放前面
	sort.Slice(courses, func(i, j int) bool { return courses[i][1] < courses[j][1] })
	q := &Heap{}
	q.Swap(1, 2)
	total := 0
	for _, course := range courses {
		t1, t2 := course[0], course[1]
		if total+t1 <= t2 {
			total += t1
			heap.Push(q, t1)
		} else if q.Len() > 0 && q.IntSlice[0] > t1 {
			total -= q.IntSlice[0]
			total += t1
			q.IntSlice[0] = t1 // 因为现在是大顶堆, go中的堆,直接将最大值覆盖掉
			heap.Fix(q, 0)     // Fix方法相当于, 调用了一次heap中的up方法, 等价于heap.remove(q, i), heap.Push(q, t1)
		}
	}
	return q.Len()
}
posted @ 2021-12-14 13:42  楠海  阅读(110)  评论(0编辑  收藏  举报