堆排序算法思想:我们把一个线性表想成是一个完全二叉树也就是一个堆,通过完全二叉树的各种性质便可进行排序操作
我们利用了大顶堆。什么是大顶堆。
大顶堆:(1)堆是一棵完全二叉树(2)大顶堆的根节点的值是最大值。
也就是说所有根节点和它的子树相比是最大的
我们从最下面的非根节点找齐,和它的左右子树比,经过交换等操作使得根节点存储得值始终是最大的
这样一层一层直到最高的顶点完成排序,那么顶点便是所有数中最大的数。得到最大的数后我们把它和最后的一个叶子节点交换
这样最大的数的序号就是最后一个数,我们再调整这个堆为大顶堆,但是这时我们调整的是除了最后一个数的堆,因为最后一个数已经是最大的了,不需要排序了。调整后再交换当前最大数(也就是堆顶),使得每次交换后未排序的顺序表中的最大值会放到未排序顺序表的最后,下次这个数将不参与排序。这样一直循环到最后一个数便结束。
代码里注释写的很全:
package heap;
public class Heap {
public static void main(String[] args){
int[] heap =new int[]{0,4, 2, 3, 2, 52, 2, 1, 6, 42,53};//这里第一个数要填0,因为完全二叉树对于序号要求很高。这样才能利用完全二叉树的序号进行操作
int i=0;
int swap;
for (i=(heap.length-1)/2;i>0;i--){//查找所有非叶子节点,这里必须-1,因为长度是11,但是其实我们只操作10个数,要从最下面的非叶子节点算起,避免排序不完全
System.out.println("1");
heapAdjust(heap,i,heap.length-1);}
for (i=heap.length-1;i>1;i--){//循环所有大顶堆的顶点,直到循环完所有,顶堆的顺序是从小到大
swap(heap,1,i);//交换大顶堆的第一个数和最后一个数(第一个数是最大的),这是最后一个数就是最大的了
heapAdjust(heap,1,i-1);//调整交换后的大顶堆。此时要除了最后一个最大的数
}
for (int f : heap) {
System.out.println(f);
}
}
//每次方法的执行相当于一个非叶子节点完成排序
public static void heapAdjust(int[] heap,int s,int m){//构建大顶堆
//s为传入的根节点(必须是非叶子节点),m为堆的大小(这个变量是循环控制变量)
/*
* 大顶堆:(1)堆是一棵完全二叉树(2)大根堆的根节点的值是最大值。
* 那么我们的根节点是最大值。
* 我们要用到的性质:对于有n个结点的完全二叉树按层序编号(从左至右),对任一节点n它的左孩子为2n,右孩子为2n+1
* 思路:通过传入需要操作的的根节点进行操作。比较根节点的和其左右子树的大小,进行交换使得根节点是最大值,但是有一点
* 我们交换后如果交换的子树的根节点在其子树并不是最大仍需要处理。
* */
int temp;//存储根节点的临时值,以便最后交换
int j;//存储最大节点用
//通过for循环遍历根节点。求得根节点和它所有子树中最大的树
//1.从当前根节点的左子树开始求
//2.获得左右子树中的最大值,让j指向最大值的下标
//3.如果根节点小于左右子树的最大值,那么进行交换,把j的值赋值给s,用于后来交换
//4.如果跟根节点大于左右子树的最大值表示不需要交换直接退出循环就好。
//5.循环结束把temp赋值给最后一个操作的值,因为它只把值给了别人,自己还没改变值
//6.再次循环判断条件是当根节点大于数组长度时边停止循环,不能只循环一次,因为交换完的子节点有可能没它的子节点大了,所以要再次判断这个点
//7.那么每次循环完的j一直是最大点的下标,对它进行最大最小值的判断就需要*2
temp=heap[s];
for (j=s*2;j<=m;j*=2){//j=s*2获得左子树的下标
System.out.println(j+" "+m);
if (j<m&&heap[j]<heap[j+1])//j<m判断不是最后一个节点,如果是最后的节点就不要这步比较
++j;
if (temp>=heap[j]) {//判断根节点时候最大,如果最大则退出循环
break;
}
heap[s]=heap[j];//如果需要交换则进行赋值交换
s=j;//循环结束后赋值用
}
heap[s]=temp;
}
public static void swap(int[] heap,int i,int j){//交换函数
int k;
k=heap[i];
heap[i]=heap[j];
heap[j]=k;
}
}