堆排序

堆排序

时间复杂度: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
    }
}
posted @ 2022-12-21 18:21  kohn  阅读(12)  评论(0)    收藏  举报