GoLang Heap и Heapsort
Поэтому я пытаюсь реализовать максимальную кучу для практики, чтобы я мог познакомиться с Go.
type MaxHeap struct {
slice []int
heapSize int
}
func BuildMaxHeap(slice []int) MaxHeap{
h := MaxHeap{slice: slice, heapSize: len(slice)}
for i := len(slice)/2; i >= 0; i-- {
h.MaxHeapify(i)
}
return h
}
func (h MaxHeap) MaxHeapify(i int) {
left := 2*i
right := 2*i + 1
largest := i
slice := h.slice
if left < h.size() {
if slice[left] > slice[i] {
largest = left
} else {
largest = i
}
}
if right < h.size() {
if slice[right] > slice[largest] {
largest = right
}
}
if largest != i {
prevLargest := slice[i]
slice[i] = slice[largest]
slice[largest] = prevLargest
h.MaxHeapify(largest)
}
}
На массиве [4,1,3,2,16,9,10,14,8,7]
Я производю [16 14 9 10 8 1 4 2 3 7]
что неправильно, так как 9 - один уровень слишком высоко и должен переключаться с 10.
Куда я иду не так?
Я также знаю, что-то странное, потому что, когда я пытаюсь и heapsort
func heapSort(slice []int) []int {
h := BuildMaxHeap(slice)
fmt.Println(slice)
for i := len(h.slice) - 1; i >=1 ; i-- {
first := h.slice[0]
last := h.slice[i]
h.slice[0] = last
h.slice[i] = first
h.heapSize--
h.MaxHeapify(1)
}
return h.slice
}
Это не работает.
2 ответа
Проблема заключалась в том, что индексы срезов начинаются с нуля, поэтому ваш:
left := 2*i
right := 2*i + 1
дает левому потомку 0 для индекса 0 (т. е. самого себя). Просто добавьте один к каждому из них.
Ваш heapSort
была похожая проблема вызова h.MaxHeapify(1)
вместо 0. Это фактически оставило то значение, которое было впереди.
Вот модифицированная версия вашего кода, которая работает (тестовый файл также включен, который использует testing/quick
проверить это против container/heap
а также sort
).
heap.go:
package main
import "fmt"
type MaxHeap struct {
slice []int
heapSize int
}
func BuildMaxHeap(slice []int) MaxHeap {
h := MaxHeap{slice: slice, heapSize: len(slice)}
for i := len(slice) / 2; i >= 0; i-- {
h.MaxHeapify(i)
}
return h
}
func (h MaxHeap) MaxHeapify(i int) {
l, r := 2*i+1, 2*i+2
max := i
if l < h.size() && h.slice[l] > h.slice[max] {
max = l
}
if r < h.size() && h.slice[r] > h.slice[max] {
max = r
}
//log.Printf("MaxHeapify(%v): l,r=%v,%v; max=%v\t%v\n", i, l, r, max, h.slice)
if max != i {
h.slice[i], h.slice[max] = h.slice[max], h.slice[i]
h.MaxHeapify(max)
}
}
func (h MaxHeap) size() int { return h.heapSize } // ???
func heapSort(slice []int) []int {
h := BuildMaxHeap(slice)
//log.Println(slice)
for i := len(h.slice) - 1; i >= 1; i-- {
h.slice[0], h.slice[i] = h.slice[i], h.slice[0]
h.heapSize--
h.MaxHeapify(0)
}
return h.slice
}
func main() {
s := []int{4, 1, 3, 2, 16, 9, 10, 14, 8, 7}
h := BuildMaxHeap(s)
fmt.Println(h)
s = heapSort(s)
fmt.Println(s)
}
heap_test.go:
package main
import (
"container/heap"
"reflect"
"sort"
"testing"
"testing/quick"
)
// Compare against container/heap implementation:
// https://golang.org/pkg/container/heap/#example__intHeap
type IntHeap []int
func (h IntHeap) Len() int { return len(h) }
func (h IntHeap) Less(i, j int) bool { return h[i] > h[j] } // use > for MaxHeap
func (h IntHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
func (h *IntHeap) Push(x interface{}) { *h = append(*h, x.(int)) }
func (h *IntHeap) Pop() interface{} {
old := *h
n := len(old)
x := old[n-1]
*h = old[:n-1]
return x
}
func TestMaxHeap(t *testing.T) {
f := func(s []int) bool {
//t.Log("testing heap len", len(s))
h := BuildMaxHeap(s)
h2 := make(IntHeap, len(h.slice))
copy(h2, h.slice)
for i := range h2 {
heap.Fix(&h2, i)
}
eq := reflect.DeepEqual(h.slice, []int(h2))
if !eq {
t.Logf("MaxHeap: %v\n\t IntHeap: %v", h.slice, h2)
}
return eq
}
if err := quick.Check(f, nil); err != nil {
t.Error(err)
}
}
func TestHeapSort(t *testing.T) {
f := func(s []int) bool {
s = heapSort(s)
return sort.IntsAreSorted(s)
}
if err := quick.Check(f, nil); err != nil {
t.Error(err)
}
}
Вот способ сделать то же самое без добавления структуры для хранения данных и размера кучи:
// AS described in Introduction to Algorithms (3rd Edition)
package main
import (
"fmt"
)
func left(i int) int {
return 2 * i
}
func right(i int) int {
return 2*i + 1
}
func parent(i int) int {
return i / 2
}
func maxHeapify(a []int, i int) []int {
fmt.Printf("Array: %v\n", a)
l := left(i) + 1
r := right(i) + 1
var largest int
if l < len(a) && l >= 0 && a[l] > a[i] {
largest = l
} else {
largest = i
}
if r < len(a) && r >= 0 && a[r] > a[largest] {
largest = r
}
if largest != i {
fmt.Printf("Exchanging: %d index (%d) with %d index (%d)\n", a[i], i, a[largest], largest)
a[i], a[largest] = a[largest], a[i]
a = maxHeapify(a, largest)
}
return a
}
func buildMaxHeap(a []int) []int {
for i := len(a)/2 - 1; i >= 0; i-- {
fmt.Printf("Building: %d index %d\n", a[i], i)
a = maxHeapify(a, i)
}
return a
}
func heapsort(a []int) []int {
a = buildMaxHeap(a)
fmt.Printf("Starting sort ... array is %v\n", a)
size := len(a)
for i := size - 1; i >= 1; i-- {
a[0], a[i] = a[i], a[0]
size--
maxHeapify(a[:size], 0)
}
return a
}
func main() {
a := [10]int{4, 1, 3, 2, 16, 9, 10, 14, 8, 7}
fmt.Printf("Array: %v\n", a)
b := heapsort(a[:])
fmt.Printf("Array: %v\n", b)
}