堆
堆是一种建立在数组之上的数据结构,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)
}
}
题目
解答:
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
}
}