1. 程式人生 > 其它 >212. go語言堆實現

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()
}