Go语言-堆排序

1、前言

在了解堆排序之前,需要知道堆的一些特征,那就是堆就是一个完全二叉树,所以需要了解完全二叉树的特点

完全二叉树的特点:

(1)叶子节点只能在最大的两层出现

(2)如果i=1,结点就是根结点,如果i>1,则其双亲parent(i)=i/2

(3)如果2i>n,则结点无左孩子,如果2i+1>n,则结点无右孩子

(4)如果下标从0开始,则第一个非叶子结点的下标为 length/2-1

堆排序会用到大顶堆和小顶堆,升序排序时用大顶堆,降序排序时用小顶堆。大顶堆的特点是节点值大于其左右孩子节点值,即value(n)>=value(n->left)并且value(n)>=value(n->right),小顶堆相反

 

2、稳定性、时间复杂度、空间复杂度

堆排序为什么不稳定:因为构建大顶堆/小顶堆的时间不稳定

平均时间复杂度:O(n*logn)

空间复杂度几乎为0(只用到几个临时变量)

 

3、具体思路(以升序、大顶推为例,数组小标为0,长度为length)

(1)构建初始堆(或者叫调整堆结构,就是将一个数组构建成一个标准堆结构)

选取第一个非叶子节点,数据下标为length/2-1,倒序遍历整个数组,将堆元素按自下到上的顺序,将最大的数字交换到堆顶,构建大顶堆。为什么初始化堆时要选取第一个非叶子节点呢?以及为什么第一个非叶子节点的下标为length/2-1呢?对于完全二叉树(N层)第一个非叶子节点在N-1层,因为需要将最大的元素通过拉的方式拉倒堆顶,所以肯定从N-1层开始拉,并且只有父节点需要做这个工作,所以从最后一个父节点开始拉。因为完全二叉树每个父节点(下标i)的孩子左节点下标为2i+1右孩子2i+2,假设只有左孩子则length为偶数,此时2i+1=length-1,即i=length/2-1。假设有右孩子则length为奇数,此时2i+2=length-1,即i=(length-1)/2-1,由于整数的除法是向下取整,(length-1)/2=length/2,所以i=length/2-1

(2)将堆顶元素与数组最后一个元素交换,此时最大的元素在数组最后面

(3)将数组0到length-2之间的元素继续调整为堆结构,然后交换下标0和length-2的元素值 ,继续调整 0到length-3的之间的数据元素,直到所有元素都有序

 

4、代码

package api

//SortByHeap 堆排序
func SortByHeap(arr []int, length int, desc bool) {
	//第一个for循环用来构建初始堆,从第一个非叶子节点开始自下向上,让最大/最小的数值从下面一步步的升到堆顶
	for i := length/2 - 1; i >= 0; i-- {
		if desc {
			adjustSmallHeap(arr, i, length)
		} else {
			adjustBigHeap(arr, i, length)
		}
	}
	//交换、再调整
	for j := length - 1; j >= 0; j-- {
		swap(arr, 0, j)
		//交换后,只有数组的第一个元素会破坏堆结构,所以只需要调整第一个元素,通过调整,将第一个元素放到合适的位置,同时将子数组中最大/最小的值交换到堆顶
		if desc {
			adjustSmallHeap(arr, 0, j)
		} else {
			adjustBigHeap(arr, 0, j)
		}
	}
}

//构建一个大顶堆
func adjustBigHeap(arr []int, i int, length int) {
	var temp int = arr[i]
	for k := 2*i + 1; k < length; k = 2*k + 1 {
		if k+1 < length && arr[k] < arr[k+1] {
			k = k + 1
		}
		if arr[k] > temp {
			arr[i] = arr[k]
			i = k
		} else {
			break
		}
	}
	arr[i] = temp

}

//构建一个小顶堆
func adjustSmallHeap(arr []int, i int, length int) {
	var temp int = arr[i]
	for k := 2*i + 1; k < length; k = 2*k + 1 {
		if k+1 < length && arr[k+1] < arr[k] {
			k = k + 1
		}
		if arr[k] < temp {
			arr[i] = arr[k]
			i = k
		} else {
			break
		}
	}
	arr[i] = temp

}
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值