排序算法之堆排序

今天来向大家介绍的是堆排序,他利用的是数据结构堆的结构特点来实现的一种排序方法。我们经常如果要进行升序排列的话,会用到的是最大堆。堆排序的整体过程分为两步,一个是建堆过程,另外一个是不断地将结点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语言的过程中写了堆排序练手,下一段时间估计都在实习,所以更新可能会慢一点了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值