堆技术细节与堆排序【原创】

参考:

https://www.jianshu.com/p/6b526aa481b1(数据结构:堆)

https://www.runoob.com/w3cnote/heap-sort.html(堆排序)

堆的性质

堆是具有以下性质的完全二叉树,每个节点的值都大于或等于其左右孩子节点的值称为最大堆,反之为最小堆。

注意:没有要求节点的左孩子的值和右孩子的值的大小关系。

 

堆的存储

1,堆使用数组实现,不是结点和指针。
2,堆的属性可以让堆和数组元素直接对应起来

这个数组arr逻辑上就是一个堆。

从这里我们可以得出以下性质(重点)

对于大顶堆:arr[i] >= arr[2i + 1] && arr[i] >= arr[2i + 2]

对于小顶堆:arr[i] <= arr[2i + 1] && arr[i] <= arr[2i + 2]

 

堆结点与数组小标关系:

1,index从0开始

2,父节点为 i,左节点:2i+1;右结点:2i+2

3,子节点为 i,floor( (i-1)/2) 等价于 (i-1)>>1

 

核心操作

上浮操作

当前结点与父节点比较,小于父节点则交换(最小堆情况下,最大堆相反),递归向上

func HeapUp(arr []int,index int)  {

    for pi:=(index-1)>>1;index>0;pi=(index-1)>>1{
        piv:=arr[pi]//切片本身是引用型
        iv:=arr[index]
        if piv>iv{
            arr[index],arr[pi] = arr[pi],arr[index]
        }else {
            break
        }
        index=pi
    }
    //return arr
}
//test
arr:=[]int{5,3,6,4,7,2}
HeapUp(arr,len(arr)-1)
fmt.Println(arr)

下沉操作

当前结点与左右子节点比较,大于较小的节点则交换(最小堆的情况下,最大堆是小于较大的节点),递归向下

func HeapDown(arr []int,index int)  {
    for li,ri :=2*index+1,2*index+2;ri<len(arr); li,ri =2*index+1,2*index+2{//第3句pi=(index-1)>>1不能省略,这里涉及到for循环的本质
        liv:=arr[li]//切片本身是引用型
        riv:=arr[ri]
        iv:=arr[index]
        small:=liv
        si:=li
        if small>riv{
            small = riv
            si=ri
        }
        if iv>small{
            arr[index],arr[si] = arr[si],arr[index]
            fmt.Println(index,si)
        }else {
            break
        }
        index=si
    }
}
//测试
arr:=[]int{9,5,3,6,4,7,2}
HeapDown(arr,0)
fmt.Println(arr)

 

插入

1,先把元素插入到末尾

2,使用上浮操作,对插入的尾部元素

删除:最小头元素

1,将头尾元素交换

2,删除交换后的尾部元素

3,使用下层操作,对交换后的呕吐不元素

获取

直接获取小标为0的元素

 

堆排序

堆排序是利用堆这种数据结构而设计的一种排序算法,堆排序是一种选择排序,它的最坏,最好,平均时间复杂度均为O(nlogn),是不稳定排序

算法步骤

  1. 创建一个堆 H[0……n-1];

  2. 把堆首(最大值)和堆尾互换;

  3. 把堆的尺寸缩小 1,并调用 shift_down(0),目的是把新的数组顶端数据调整到相应位置;

  4. 重复步骤 2,直到堆的尺寸为 1。

show me the code

func heapSort(arr []int) []int {
        arrLen := len(arr)
        buildMaxHeap(arr, arrLen)
        for i := arrLen - 1; i >= 0; i-- {
                swap(arr, 0, i)
                arrLen -= 1
                heapify(arr, 0, arrLen)
        }
        return arr
}

func buildMaxHeap(arr []int, arrLen int) {
        for i := arrLen / 2; i >= 0; i-- {
                heapify(arr, i, arrLen)
        }
}

func heapify(arr []int, i, arrLen int) {
        left := 2*i + 1
        right := 2*i + 2
        largest := i
        if left < arrLen && arr[left] > arr[largest] {
                largest = left
        }
        if right < arrLen && arr[right] > arr[largest] {
                largest = right
        }
        if largest != i {
                swap(arr, i, largest)
                heapify(arr, largest, arrLen)
        }
}

func swap(arr []int, i, j int) {
        arr[i], arr[j] = arr[j], arr[i]
}

 

posted @ 2022-05-30 15:43  小匡程序员  阅读(26)  评论(0编辑  收藏  举报