今天来向大家介绍的是堆排序,他利用的是数据结构堆的结构特点来实现的一种排序方法。我们经常如果要进行升序排列的话,会用到的是最大堆。堆排序的整体过程分为两步,一个是建堆过程,另外一个是不断地将结点siftdown的排序的过程。
整体思路如下:
1.首先建立一个数组,数组中的元素随机排列
2.从最后一个有字节点的节点(或者叫做最后一个非叶节点)开始,将每一个非叶节点都下拉,使整个树结构符合最大堆的结构,这个过程叫做建堆
3.当整棵树符合最大堆的结构之后,我们依此将根节点与树的最后一个节点交换,并且重新对根节点实现下拉操作,使之符合最大堆的结构,不断重复这个过程就可以得到堆排序的结果
其中如果对思路不明白,可以参考堆排序----深入浅出。
以下实现代码的讲解
首先是下拉函数,这是这里面最重要的一个函数,体现了堆排序中节点下拉的策略
//下拉
func siftdown(arr[] int ,length int, i int){
//左子节点,右子节点,将会被交换的节点,当前即将被下拉的节点
var left_index,right_index,swap_index,curr_index int
//给当前节点赋值为传进来的参数
curr_index = i
//根据完全二叉树的规律我们可以得到左,右字节点的下标
left_index = curr_index*2
right_index = curr_index*2+1
//开启循环,将当前节点下拉直到该字节点没有儿子就结束
for ; left_index<=length; {
//选择与左,还是右子节点进行交换,当然右子节点也必须在length范围内
if right_index<=length {
if(arr[left_index]>arr[right_index]) {
swap_index = left_index
}else {
swap_index = right_index
}
//如果只有左子节点那swap_index就是左子节点
} else {
swap_index = left_index
}
//如果swap_index节点比当前节点要大,那么就交换他俩
if arr[swap_index] > arr[curr_index] {
swap(&arr[curr_index] , &arr[swap_index])
}
//更新相关的下标,进行下一轮循环
curr_index = swap_index
left_index = curr_index*2
right_index = curr_index*2+1
}
}
第二个部分就是建堆函数,建堆的过程就如上所述,下面进行代码解释
//建堆
func BuildHeap(arr[] int , length int ){
var i int
//首先选择完全二叉树的最后一个非叶节点,这是不难计算出来的
if(length%2!=0){
i = (length-1)/2
} else {
i = length/2
}
//然后从最后一个非叶节点开始,让他执行siftdown操作,执行完毕之后就是一个最大堆了
for ; i>=1; i-- {
siftdown(arr , length , i)
}
}
第三个部分就是将一个最大堆变成一个升序的数组,详解如下
//堆排序
func heapSort(arr [] int){
var length = len(arr)-1
//首先进行建堆
BuildHeap(arr,length)
//建堆完毕之后,从最后一个节点开始,依次让他和根节点进行交换
for i:=length; i>1; i-- {
swap(&arr[1],&arr[i])
//交换完毕之后对根节点进行下拉操作,同时长度必须减一,因为后面的部分已经有序了
siftdown(arr,i-1,1)
}
//打印输出
for i:=1; i<=length; i++ {
fmt.Print(" " , arr[i])
}
}
这篇博客用的语言是go语言而非以前常用的C++和Java,因为最近博主在实习,实习这边需要go语言因此就在学go语言的过程中写了堆排序练手,下一段时间估计都在实习,所以更新可能会慢一点了