堆排序(JAVA)

来源:互联网 发布:在线录像软件 编辑:程序博客网 时间:2024/06/06 02:34

看了几篇博客,以自己的理解默写了一遍自己理解的堆排序,然后本地跑了一遍,可以正常排序,因为怕以后看不懂,注释写的很啰嗦

/** * 堆排序 * 堆一般指一个完全二叉树,根据其特性,可以分为大根堆,小根堆 * 大根堆:任意父节点的值大于或等于2个孩子节点的值 * 小根堆:任意父节点的值小于或等于2个孩子节点的值 * 就是利用堆的这样的特性用来排序,所以叫做堆排序 * 比较常用的就是大根堆了,因为根结点是堆的最大节点,所以可以类似于冒泡,选择排序那样,依次得到最大节点,次最大节点,完成一个序列的排序 * 每次都从根结点把堆的最大值交换到堆低,然后调整堆为新的大根堆,不过这时候堆的长度得减1,依次下去就可以完成排序 * 小根堆也一样,可以不断选择最小值,然后不断调整小根堆,获得次最小,然后也是一个排好序的序列 * 一颗完全二叉树,其层次遍历的结果可以顺序存在一个数组里 * 其父节点i的位置和左右孩子的位置关系是:左孩子:2*1+1;右孩子:2*1+2;节点的父节点位置:(i-1)/2 *  */public class HeapSort {public static void main(String[] args) {// TODO Auto-generated method stubint[] array = {87,45,78,32,17,65,53,9,122,133};heapSort(array);for(int i : array){System.out.print(i+" ");}}/** * 堆排序 * 使用大根堆的特性,每次获取堆顶的最大值,放到堆底,然后堆的长度减1,调整堆使其再次恢复大根堆特性,依次循环,直到只剩下一个节点为止 * @param data * @return */public static int[] heapSort(int[] data){if(data == null || data.length <= 1) return data;//构建大根堆buildMaxHeap(data);for(int i = data.length -1; i > 0; i--){//自减为0后,表示堆还剩一个节点没有排序//交换堆顶、底的值int temp = data[i];data[i] = data[0];data[0] = temp;//可能破坏了大根堆的特性,指定根结点进行调整]adjustMaxHeap(data, 0, i);//此时已经确定了最大的节点到堆底,所以堆的长度得减1}return data;}/** * 构建大根堆 * 思路: * 从右向左,从下到上不断调根结点为大根堆,起始点为最后一个父节点:(data.length-1-1)/2,终点为 * 树的根结点 0 * @param data * @return */public static int[] buildMaxHeap(int[] data){if(data == null || data.length <= 1) return data;for(int i = (data.length-1-1)/2; i >= 0; i--){adjustMaxHeap(data, i, data.length);//构建大根堆时,长度一直不变}return data;}/** * 调整大根堆,使其保持大根堆的特性,自上而下地向下调整,直到堆底 * @param data 需要调整的堆 * @param headIndex 需要调整的堆的根节点的位置 * @param length 需要调整的堆的长度 * @return */public static void adjustMaxHeap(int[] data, int headIndex, int length){//判断要调整的根结点的合法性,最后一个根结点的位置是(length-1-1)/2if(headIndex <= (length-1-1)/2){//位置合法,开始调整大根堆,判断其是否大于或等于左右孩子的最大值,是则不需要调整,直接跳出循环,否则与最大值交换,然后继续往下调整//一直调整所需要比较和交换值的子节点到堆底结束,即可以执行到data.length -1位置的节点//因为调整大根堆使,可能会不停的那较大的孩子节点的值覆盖父节点,所以需要保存当前父节点的值int temp = data[headIndex];for(int i = 2*headIndex+1; i < length; i = 2*i+1){//每次循环开始,即需要确认一个根结点headIndex是否需要调整,使其保持大根堆的性质//取左右孩子最大值的位置//i < data.length -1 这个条件的限制是为了确定i是左孩子的情况下,那么一定存在右孩子,如果i不满足条件,那么就不会有右孩子了if(i < length -1 && data[i] < data[i+1]){//i是左孩子,并且值比右孩子小,那么最大值的位置就是右孩子i++;}//此时已经得到左右孩子的最大位置,判断是否需要调整堆if(temp >= data[i]){//根结点的值已经大于或等于左右节点,不需要调整堆break;}else{//需要调整data[headIndex] = data[i];//将最大的值赋值到根结点//原headIndex的位置的值已经确定,这时候需要向下继续调整,因为我们还不知道 temp因该放在哪,即无论放在哪里都可能破坏//大根堆的结构,所以需要依次向下调整,直到不需要调整为止,才能保证大根堆的特性//现在temp可能会被放在刚刚赋值过去的i上,所以以i为根结点,继续调整大根堆headIndex = i;//以headIndex为根结点,并且假设将temp的值也迁移到这个新的headIndex上(可以这么想,因为每次都是temp与左右孩子较大值取比较的)//继续寻找左孩子的位置,即i = 2*i+1}}//此时循环结束,可能是headIndex的值本来就不需要调整,//或者进行调整后,因为把最大孩子节点的值赋值到根结点后,更新较大孩子节点的位置为headIndex,此时的headIndex的值不需要调整//或者headIndex的位置已经超出最后一个父亲节点的位置//那么headIndex就可以存放调节父节点的值temp了data[headIndex] = temp; //指定父节点调整大根堆完成}}}


原创粉丝点击