最近在看算法导论,学习了堆排序。今天特以记笔记的方式,来讲下堆排序的过程。
首先我们知道,堆分为最大堆跟最小堆两种。在最大堆中,最大堆性质是指除了根以为的所有节点 i 都要满足:
A[PARENT(i)] >=A[i]
也就是说,某个结点的值至多与其父结点一样大。因此,堆中的最大元素存放在根结点中;并且在任一子树中,该子树所包含的所有结点的值都不大于该孩子跟结点的值。最小堆正好相反。
先说下,整个排序的步骤:
1.先找到父结点,左结点,右结点三个之间最大的,然后跟父结点交换。保证父结点是最大的。
2.然后就是建堆的过程。
3.然后就是排序了。
下面就按部就班的一步一步来:
1、
先将父结点变成最大的。
比如像这样的二叉树的某一部分。最终的结果是2 和5掉了一个位置。那怎样用代码来解决呢。如下:
//将父节点跟子节点比较,使得父节点是最大的,然后,将交换的节点,继续此操作
private void ExchangeLagest(int array[],int heapSize,int index){
int l=2*index+1;//左节点
int r=2*index+2;//右节点
int lagest=index;
if(l<heapSize && array[l]>array[index]){ //判断左节点是否大于父节点,如果大于,就交换
lagest=l;
}
if(r<heapSize && array[r]>array[lagest]){//判断右节点是否大于父节点,如果大于,就交换
lagest=r;
}
if(lagest!=index){
int temp=array[lagest];
array[lagest]=array[index];
array[index]=temp;
ExchangeLagest(array, heapSize, lagest);//这边递归下,防止交换之后的节点影响下面的节点
}
}
下面就是第二步了,建堆的过程。
2、
我们知道堆可以看成是一个完全二叉树,那么在数组A[n/2+1....n]中的结点都是树的叶结点,也就是说每一个结点都可以看成只包含一个元素的堆。那么就相当于是这个堆已经是建好的,我们需要对 A[n/2....1] 从下往上的建堆。
那么好,通过下面代码就可以实现建堆的过程了
private void heap(int []array){
/*
* 这里从数组长度的一半开始,因为,在大根堆中,一半之后,都是叶子节点,没有子节点
* 所以在建堆得过程中,不需要考虑
*/
for(int i=array.length/2;i>=0;i--){
ExchangeLagest(array, array.length, i);
}
System.out.print("建过堆的数组:");
for(int i=0;i<array.length;i++){
System.out.print(array[i]);
}
}
这里就是,对length/2 到0 进行一个遍历,把每一个结点都进行步骤一的操作。
最后一步就是排序了。
3、
这里的排序就是,我们知道根结点已经是最大的了,让它跟最后一个结点交换数据,可以让该元素放到正确的位置。这时候,我们从堆中去掉结点n(这一操作可以通过减少heapSize的值来实现),剩余的结点中,原来根的孩子结点仍然是最大堆,而新的根结点可能会违背最大堆的性质。为了维护这一性质,我们要for循环重复建堆的过程。代码如下:
public void sort(){
int array[]={4,8,1,9,5,0,3,6,2};
System.out.print("原数组:");
for(int i=0;i<array.length;i++){
System.out.print(array[i]);
}
System.out.println();
heap(array);
for(int i=array.length-1;i>=1;i--){
int temp=array[0];
array[0]=array[i];
array[i]=temp;
ExchangeLagest(array, i, 0);
}
System.out.println();
System.out.print("排序过后数组:");
for(int i=0;i<array.length;i++){
System.out.print(array[i]);
}
}
以上就是完成的堆排序的过程了。
运行结果如下:
再给大家贴一个草图,画的不是很好,见谅: