堆排序算法 golang实现

算法描述


我们以大根堆举例,堆其实就是完全二叉树,大根堆的堆顶是堆中的最大元素,大根堆的父节点比左右两个孩子都大:

在这里插入图片描述
看完大根堆的结构之后,我们其实还需要将大根堆中的元素都存到数组中,比如上面结构的大根堆对应的数组如下所示:

在这里插入图片描述
同时用数组储存大根堆还有一些规则:
在这里插入图片描述
大根堆的维护

如果大根堆中的元素不满足大根堆的性质,就要进行堆的维护。比如下图中的红色节点,节点值为4,此时就不满足对的性质了(孩子节点的值比父节点大),需要进行堆的维护。

在这里插入图片描述
首先将父节点与左孩子进行交换:

在这里插入图片描述
接着还需要对左孩子进行堆的维护,因为交换之后,左孩子与它自己的孩子就违反了堆的性质,我们将左孩子与它的右孩子进行交换,就完成了堆的维护:

在这里插入图片描述

递归O(logN)

以下是堆维护的代码:

func heapify(arr []int, n int, i int) {
	//堆的维护
	largest := i  		//i为当前父节点的下标
	lson := i * 2 + 1   //左孩子
	rson := i * 2 + 2   //右孩子
	// 保存最大值的下标
	if lson < n && arr[largest] < arr[lson] {
		largest = lson
	}
	if rson < n && arr[largest] < arr[rson] {
		largest = rson
	}
	if largest != i {
		arr[largest], arr[i] = arr[i], arr[largest]
		// 往孩子节点递归维护堆
		heapify(arr, n, largest)
	}
}

建堆

建堆

以下面这个无序数组为例:

在这里插入图片描述
堆的初始状态是这样的:

n / 2 - 1n / 2 - 10

所以建堆的代码:

// 建堆
	// 从最后一个元素的父节点进行建堆(第一次堆的维护)
	for i := n / 2 - 1; i >= 0; i-- {
		heapify(arr, n, i)
	}

堆排序

建立好大根堆之后,我们需要进行堆排序,其实就是每次将堆顶元素放到数组的后面(将数组中的当前元素与数组末尾的元素做交换),同时进行堆的维护,这样每一次操作之后堆顶元素始终是堆中的最大元素,那么当结束这个过程之后,数组就是一个递增的有序数组。

堆排序的代码:

	// 排序
	// 每次将堆的最后一个元素与堆顶元素交换,然后从堆中删除最后一个元素放到数组的最后一位
	// 堆顶元素就是每次维护结束以后堆中最大的元素
	// 到最后数组就会从小到大排序
	for i := n - 1; i > 0; i-- {
		arr[i], arr[0] = arr[0], arr[i]
		// 交换元素以后要进行堆的维护
		heapify(arr, i, 0)
	}

完整代码

package main

import "fmt"

func heapify(arr []int, n int, i int) {
	//堆的维护
	largest := i  		//i为当前父节点的下标
	lson := i * 2 + 1   //左孩子
	rson := i * 2 + 2   //右孩子
	// 保存最大值的下标
	if lson < n && arr[largest] < arr[lson] {
		largest = lson
	}
	if rson < n && arr[largest] < arr[rson] {
		largest = rson
	}
	if largest != i {
		arr[largest], arr[i] = arr[i], arr[largest]
		// 往孩子节点递归维护堆
		heapify(arr, n, largest)
	}
}

func HeapSort(arr []int, n int)  {
	// 建堆
	// 从最后一个元素的父节点进行建堆(第一次堆的维护)
	for i := n / 2 - 1; i >= 0; i-- {
		heapify(arr, n, i)
	}
	// 排序
	// 每次将堆的最后一个元素与堆顶元素交换,然后从堆中删除最后一个元素放到数组的最后一位
	// 堆顶元素就是每次维护结束以后堆中最大的元素
	// 到最后数组就会从小到大排序
	for i := n - 1; i > 0; i-- {
		arr[i], arr[0] = arr[0], arr[i]
		// 交换元素以后要进行堆的维护
		heapify(arr, i, 0)
	}
}

func main() {
	arr := []int{1, 9, 10, 30, 2, 5, 45, 8, 63, 234, 12}
	HeapSort(arr, len(arr))
	fmt.Println(arr)
}

输出结果:

[1 2 5 8 9 10 12 30 45 63 234]