简介
堆排序(Heapsort)是指利用堆积树(堆)这种数据结构所设计的一种排序算法,它是选择排序的一种。可以利用数组的特点快速定位指定索引的元素。堆分为大根堆和小根堆,是完全二叉树。大根堆的要求是每个节点的值都不大于其父节点的值,即A[PARENT[i]] >= A[i]。在数组的非降序排序中,需要使用的就是大根堆,因为根据大根堆的要求可知,最大的值一定在堆顶。——百度百科
在说堆之前先说一下二叉树,堆都是完全二叉树,完全二叉树的定义为除了最后一个节点其他都是满二叉。
完全二叉树的定义是,当父节点id为n的时候子节点为2n和2n+1。这个规律就是堆排序的核心。
代码
/**
* 调整函数
* @param array
* @param start
* @param end
*/
public static void adjust(int [] array,int start,int end)
{
// 0
// 1 2
// 3 4 5 6
//先取出父节点的值
int temp = array[start];
//在所有子节点或者子节点的子节点找最大值
//i = 2*start+1 原因为 父节点的左孩子节点为2*父节点编号+1 如 0号左孩子为1号
//i = 2*i+1 原因为 堆排序所用的完全而差事,并且在寻找最大值的时候是深度遍历
// 则需要一直往深处走,直到到达边界
for (int i = 2*start+1; i <= end ; i = 2*i+1) {
//找到左右孩子的最大值进行交换,保证有右孩子
//当当前父节点的右孩子比左孩子大的时候,要把i移动到右孩子上面
//又因为右孩子和左孩子的关系只是+1,所以i++即可
if(i < end && array[i]<array[i+1])
{
i++;//i为最大值下标
}
//如果当前最大的(左孩子or右孩子) 大于父节点
if(array[i]>temp)
{
//将当前的节点赋值给父节点
array[start] = array[i];
//将i的位置给 start
start = i;
}
//如果temp是最大的,则两个子节点都没有父节点大
if(array[i]<temp)
{
//直接跳出
break;
}
}
//将temp及原来的父节点放入适当的位置。
//如果父节点为最大,此处未变
//如果某个子节点或者某个子节点的子节点比父节点大
//则start为那个比父节点大的节点的下标
//及将父节点和最大的子节点进行交换
array[start] = temp;
}
/**
* 堆排序主函数
* @param array 数组
*/
public static void heapSort(int [] array)
{
//先将当前数组调整成大顶堆
// 0
// 1 2
// 3 4 5 6 length = 7
//调整的时候应该从2开始 然后是 1 最后调整0 则 应该为 array.length-1-1
//第一个-1为将数组长度和下标对应,第二个减一为将其移动到左孩子
for (int i = (array.length-1-1)/2; i >= 0; i--) {
//进行调整,每次只调整一个小树
//如上图的话
//1. 调整2---6
//2. 调整1---6
//3. 调整0---6
adjust(array, i, array.length-1);
}
//最后一个值和根节点交换
int temp;
//每次都将当前的堆调整为大顶堆,及最上面的为最大值
for (int j = 0; j < array.length-1; j++) {
//将array[0]和array[array.length-1-j]交换
//及将大顶堆的最顶上放到最后面
temp = array[0];
array[0] = array[array.length-1-j];
array[array.length-1-j] = temp;
//重新调整 调整范围为
// 0-----array.length-1-1-j
//第一个-1为让其和下标对其
//第二个-1为因为上面已经交换完成了一个
//-j为一个动态调整,则就是当某一个最后的位置确定以后
//下一次构造大顶堆就应该跳过他。
adjust(array, 0, array.length-1-1-j);
}
}
测试
public static void main(String[] args) {
int [] a = {1,13,4,23,1,43,54,7,32,341};
heapSort(a);
System.out.println(Arrays.toString(a));
}
结果[1, 1, 4, 7, 13, 23, 32, 43, 54, 341]
分步测试
题目1,13,4,23,1,43,54,7,32,341
1.先构造树
2.开始调整
因为数组长度为10,则先从4号下标开始调整,及下面的1开始
1)先调整4—9,父节点1没有最大的子节点341大
2)调整3—9,父节点23没有最大的子节点32大,进行调整
3)调整2—9,父节点4没有最大的子节点54大,进行调整
4)调整1—9,父节点13没有最大的子节点341大,且比341的最大的子节点1大,所以341和13互换位置
5)调整0-9,父节点1没有最大的子节点341大,且没有341最大的子节点32大,并且没有32的最大的子节点23大,所以进行多步调整
①将1放入temp
②将341赋值到1(0号下标)的位置
③因temp没有341最大的子节点32大,将32赋值到原341的位置(1号下标)
④因temp没32的最大的子节点23大,将23赋值到原32的位置(3号下标)
⑤将temp放到原23的位置(8号位置)
3.目前第一个大顶堆已经构造完成,需要开始进行换位置,重新构造的循环之中
1)
①先将最大的数及堆顶array[0]号下标的数和最后未有序的数(9号下标)进行交换
目前最后一个数已经有序。
②重新进行构造大顶堆这次的范围为0—8,及最后一个数不参数构造
父节点1没有子节点中最大的54大,54赋值到1位置(0号下标),并且1(temp)没有54的最大的子节点43大进行,所以43(5号下标)赋值到原54的位置(2号下标)
,最后将1(temp)赋值到原43的位置(5号下标)
2)
①先将最大的数及堆顶array[0]号下标的数和最后未有序的数(8号下标)进行交换
②重新构造大顶堆—————————-
一直重复以上步骤,直到全部有序。