堆排序的思想是 先把需要排序的数组构造成一个大根堆或者小根堆 然后用选择排序的思想 每一次都从堆里面选取最小的元素放到已经有序的序列中去 交换完成后对 被取走根的堆重建 其中在建立堆和调整堆的时候都需要用到一个方法来调整 这里我命名为一个adjustHeal(arr ,index,endIndex)
其中index代表的是需要调整的节点的位置 endindex是指本次需要调整的结束的位置(结束位置有效)代码如下
public static void adjustHeal(int[] arr,int index,int endIndex)
{
int adjustIndex=index;
int max=adjustIndex*2+1;
int temp;
while (max<=endIndex)
{
if (max+1<=endIndex&&arr[max]<arr[max+1])//左孩子小于右孩子
max++;
if (arr[adjustIndex]>arr[max])break;
else
{
temp=arr[adjustIndex];
arr[adjustIndex]=arr[max];
arr[max]=temp;
}
/**
* 更新 需要调整的节点的位置 以及孩子的位置 准备进入下一轮调整
*/
adjustIndex=max;
max=adjustIndex*2+1;
}
}
首先是定义一个adjustIndex 表示当前已经进行到的 需要调整的节点位置
max 就是左孩子或者是右孩子 具体是那个 通过下面的判断来确定 如果是大根堆 那就取最大 反之取小的那个
此时如果当前的需要调整的节点已经满足最大堆的特点也就是
if (arr[adjustIndex]>arr[max])break;
了 那就停止
否则的话交换 需要调整的节点和较大孩子节点的值 构造大根堆
然后给adjustIndex和max重新赋值 从而进入下一轮的调整
这个调整会一直到当前已经满足大根堆的特点或者 到了叶子节点才结束
堆的调整方法写完了 就可以开始写排序函数了 ,排序函数其实没有什么
- 先建堆 这里建立堆的时候要注意一个问题就是 要从最后一个非叶子节点开始建 从下往上 因为写的adjustHeal方法是 对于一个节点一直往下调整 知道遇到它的孩子节点满足了堆的性质就结束了 然而并不会检查 孩子的孩子是否满足性质 所以我们要从底部开始建立堆 这样就可以保证在调整堆 那个while循环终止的时候 儿子节点们都是满足堆的性质的 (最后一个非叶子节点 通过arr.length/2-1得出
- 然后就是一共要循环 arr.length-1次 选择这么多次最小或者最大的 从而就达到了排序的效果了 跟普通的选择排序一样 每次把堆顶部的元素跟尾部的第一个未排序的元素交换 这里的排序有序区是放在后面的 例如i=1的时候 整个数组是个大根堆 那排序区目前还没有元素 由于我们是要从小到大排序 所以应该把最大的元素依次放在最后面 较后面 次后面 如此下去 所以用
temp=arr[arr.length-i];
和下面的两句来交换 - 下面是一个数组样例的排序过程
原数组: [7, 2, 35, 0, 10, 14, 47]
建堆完成后的数组: [47, 10, 35, 0, 2, 14, 7]
下面是整个过程:
[47, 10, 35, 0, 2, 14, 7]
[35, 10, 14, 0, 2, 7, 47]
[14, 10, 7, 0, 2, 35, 47]
[10, 2, 7, 0, 14, 35, 47]
[7, 2, 0, 10, 14, 35, 47]
[2, 0, 7, 10, 14, 35, 47]
排序后的数组: [0, 2, 7, 10, 14, 35, 47]
代码如下
public static void healSort(int [] arr)
{
for (int i = arr.length/2-1; i >=0 ; i--)
adjustHeal(arr,i,arr.length-1);
int temp;
for (int i = 1; i < arr.length; i++)
{
temp=arr[arr.length-i];
arr[arr.length-i]=arr[0];
arr[0]=temp;
adjustHeal(arr,0,arr.length-i-1);
}
}