堆排序
堆排序
时间复杂度:O(logn)
先创建一个堆,然后调整堆,调整过程是将节点和子节点进行比较,将其中最大的值变为父节点,递归调整调整次数lgn,最后将根节点和尾节点交换再n次调整O(nlgn).
算法步骤:
创建最大堆或者最小堆
调整堆
交换首尾节点
手写堆排序
package leetcode
type Heap struct {
arr []int
length int
}
func NewHeap(arr []int) Heap {
return Heap{arr: arr, length: len(arr)}
}
// 大顶堆
func (h *Heap) Init() {
n := h.length
for i := n/2 - 1; i >= 0; i-- { // 初始化堆
h.down(i, n) // 下沉操作
}
}
// 堆的下沉函数, 对下标为 idx 的节点进行下沉操作; n 为整个堆元素的总个数,防止越界。
func (h *Heap) down(idx, n int) {
// 这个for循环是为了能让元素沉到底
for {
left := idx*2 + 1 // 左节点
right := idx*2 + 2 // 右节点
maxIdx := idx // 左节点,右节点,当前节点最大的元素下标
if left < n && h.arr[left] > h.arr[maxIdx] { // 不能越界,找最大的
maxIdx = left
}
if right < n && h.arr[right] > h.arr[maxIdx] { // 不能越界,找最大的
maxIdx = right
}
if idx == maxIdx { // 当前节点大于左右子节点
break
}
h.arr[idx], h.arr[maxIdx] = h.arr[maxIdx], h.arr[idx] // 交换元素
idx = maxIdx // 更新当前节点位置
}
}
// 将堆顶元素与尾部的元素进行交换,然后通过下沉函数处理堆顶,注意此时堆的元素总数量需要减去1
func (h *Heap) Pop() int {
max := h.arr[0]
h.arr[0], h.arr[h.length-1] = h.arr[h.length-1], h.arr[0] // 交换堆顶元素和堆最后一个元素
h.length-- // 堆的元素个数减去1
h.down(0, h.length) // 下沉堆顶元素
return max
}
// {1, 3, 4, 5, 2, 6, 9, 7, 8, 0}
// Init 后
// [9 8 6 7 2 1 4 3 5 0]
// 调用 len(arr) 次 Pop() 排序后
// [0 1 2 3 4 5 6 7 8 9]
heap
package main
import (
"container/heap"
"fmt"
)
// IntHeap 实现 heap.Interface 接口
type IntHeap []int
func (h IntHeap) Len() int {
return len(h)
}
func (h IntHeap) Less(i, j int) bool {
// 如果设置 h[i] < h[j] 就是小顶堆,h[i] > h[j] 就是大顶堆
return h[i] < h[j]
}
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{} {
x := (*h)[len(*h)-1]
*h = (*h)[:len(*h)-1]
return x
}
func main() {
// 创建切片
h := &IntHeap{2, 1, 5, 6, 4, 3, 7, 9, 8, 0}
// 初始化小顶堆
heap.Init(h)
fmt.Println(*h) // [0 1 3 6 2 5 7 9 8 4]
// Pop 元素
fmt.Println(heap.Pop(h).(int)) // 0
// Push 元素
heap.Push(h, 6)
fmt.Println(*h) // [1 2 3 6 4 5 7 9 8 6]
for h.Len() != 0 {
fmt.Printf("%d ", heap.Pop(h).(int)) // 1 2 3 4 5 6 6 7 8 9
}
}