36-咸鱼学Java-堆排序

简介

堆排序(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号下标)进行交换
②重新构造大顶堆—————————-

一直重复以上步骤,直到全部有序。

猜你喜欢

转载自blog.csdn.net/qq_38345606/article/details/80363884
今日推荐