堆排序和优先级队列

堆是一种建立在数组之上的数据结构,A[i]的左右子结点是A[2*i+1], A[2*i+2],且满足A[i]是这3个数中最小的(或最大的那个)。

heapify过程描述了如何将某个结点从i处“下沉”到合适的位置:

# 最大堆
heapify(A,i,heapSize):  # 也可以叫做 sink(A,i,heapSize), java优先级队列的命名就是sink
    L, R = 2*i+1, 2*i+2
    largest = i
    if L < heapSize && A[L] > A[i]:
        largest = L
    if R < heapSize && A[R] > A[largest]:
        largest = R
    if largest!=i:
        A[largest],A[i] = A[largest],A[i]
        heapify(A,largest)

在堆中,第一个非叶子节点是: 2*i + 1 <= A.length-1, i <=(A.length-2)/2, i=floor((A.length-2)/2)
buildHeap从最高的非叶节点开始,对每个点调用heapify

buildHeap(A):
    # 循环不变式:循环开始时,[i+1,i+2, ..., A.length-1]都是满足堆的性质,即任意一点都是左右子节点的最大值
    # 循环过程中,heapify(A,i) 将会使得[i,i+1,i+2,..., A.length-1]都是满足堆的性质,维持循环不变式
    for i=(A.length-2)/2;i>=0;i--:
        heapify(A,i,A.length)

这里的for循环,实际上就是不断的扩展堆,将新的节点i往两个子堆中进行下沉。

heapSort对数组进行排序,排序的过程就是不断交换最大值的过程:

heapSort(A):
    buildHeap(A)
    heapSize = A.length
    while heapSize > 1: # 最后一个点不需要交换
        heapSize--
        A[0],A[heapSize] = A[heapSize],A[0]
        heapify(A,0,heapSize)

push操作

push操作只需要将元素上移即可,但是注意,parent是(i-1)/2, 而不是i/2。
对这道题的提交记录:https://leetcode-cn.com/problems/last-stone-weight/submissions/ 显示了我的错误。

siftUp(A,i):
    while i>0:
        p = (i-1)/2
        if A[p] >= A[i]:
            return
        A[p],A[i] = A[i],A[p]
        i=p

go实现

package main

import "fmt"

func main(){
    arr := []int{4,6,-1,-2,9,10,23,8,7}
    heapSort(arr)
    fmt.Printf("arr=%v\n",arr) // arr=[-2 -1 4 6 7 8 9 10 23]
}

func heapSort(arr []int){
    buildHeap(arr)
    heapSize := len(arr)
    for heapSize > 1 {
        heapSize--
        arr[0],arr[heapSize] = arr[heapSize],arr[0]
        heapify(arr,0,heapSize)
    }
}

func buildHeap(arr []int){
    for i:=(len(arr)-2)/2;i>=0;i--{
        heapify(arr,i,len(arr))
    }
}
func heapify(arr []int,i int,size int){
     l,r := 2*i+1,2*i+2
     largest := i
     if l<size && arr[l] > arr[i] {
         largest = l
     }
     if r < size && arr[r] > arr[largest] {
         largest = r
     }
     if largest!=i{
         arr[i],arr[largest] = arr[largest],arr[i]
         heapify(arr,largest,size)
     }
}

题目

LeetCode Easy: 最后一个石头的重量

解答:

func lastStoneWeight(stones []int) int {
    makeHeap(stones)
    size := len(stones)
    for size > 1 {
        y := pop(stones,size)
        size--
        x := pop(stones,size)
        size--
        if y>x {
            stones[size]=y-x
            siftUp(stones,size)
            size++
        }
    }
    if size == 0 {
        return 0
    }
    return stones[0]
}

func makeHeap(A []int){
    for i:=len(A)/2;i>=0;i--{
        sink(A,i,len(A))
    }
}
func pop(A []int,size int) int {
    if size == 1 {
        return A[0]
    }
    size--
    A[0],A[size] = A[size],A[0]
    sink(A,0,size)
    return A[size]
}

func sink(A []int,i int,size int) {
    L, R := 2*i+1, 2*i+2
    largest := i
    if L<size && A[L] > A[largest] {
        largest = L
    }
    if R<size && A[R] > A[largest] {
        largest = R
    }
    if largest!=i {
        A[i],A[largest] = A[largest],A[i]
        sink(A,largest,size)
    }
}
func siftUp(A []int,i int){
    for i > 0 {
        p := (i-1)/2
        if A[p] >= A[i] {
            return 
        }
        A[p],A[i] = A[i],A[p]
        i=p
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值