堆排序新版

import java.util.Arrays;

public class HeapSort {

    public static void main(String[] args) {
        // 要求将数组进行升序排序
//      int arr[] = {4, 6, 8, 5, 9};
//      heapSort(arr);
//      System.out.println(Arrays.toString(arr));

        // 800w 数据测试
        int[] arr = new int[5];
       arr= new int[]{4, 6, 8, 5, 9};
        long time1 = System.currentTimeMillis();
        heapSort(arr);
        long time2 = System.currentTimeMillis();
        System.out.println("堆排序实现大顶堆排序800W数据:" + (time2 - time1) + "毫秒");
        System.out.println(Arrays.toString(arr));
    }

    /**
     * 构建堆排序(最终调用)
     * @param arr 传入的数组
     */
    public static void heapSort(int[] arr) {
        int temp = 0;

        // 分步测试
//      adjustHeap(arr, 1, arr.length);
//      System.out.println("第一次" + Arrays.toString(arr)); // 4, 9, 8, 5, 6
//      adjustHeap(arr, 0, arr.length);
//      System.out.println("第二次" + Arrays.toString(arr)); // 9, 6, 8, 5, 4

        // 完整步骤
        // 将无序序列构建成的一个堆,根据升序或者降序,选择大顶堆或者小顶堆   为社么是arr.length/2-1 这是因为从下到上依次
        for (int i = arr.length / 2 - 1; i >= 0; i--) {
            adjustHeap(arr, i, arr.length);
        }

        // 将堆顶元素与末尾元素交换,将最大元素 "沉"到数组末端
        // 重新调整结构,使其满足堆定义,然后继续交换堆顶元素与当前末尾元素,(就这样反复执行 【调整 -> 交换】,直到整个序列有序)
        // 时间复杂度 为线性 O(nlogn)
        for (int j = arr.length - 1; j > 0; j--) {
            temp = arr[j];
            arr[j] = arr[0];
            arr[0] = temp;
            adjustHeap(arr, 0, j);
        }
    }

    /**
     * 将一个数组(二叉树),调整成一个大顶堆
     * 功能:完成将 以 i 对应的非叶子节点的树调整成大顶堆
     * eg:第一次:int arr[] = {4, 6, 8, 5, 9}; => adjustHeap(i=1) => 得到 {4, 9, 8, 5, 6}
     * eg:第一次:int arr[] = {4, 9, 8, 5, 6}; => adjustHeap(i=0) => 得到 {9, 6, 8, 5, 4}
     * @param arr       待调整的数组
     * @param i         表非叶子节点在数组中的索引
     * @param length    表对多少个元素进行调整(length会逐渐减少,因为被调整好的增加了)
     */
    public static void adjustHeap(int[] arr, int i, int length) {
        // 先取出当前元素的值,报错在临时变量中
        int temp = arr[i];

        // 开始调整
        // 故:k = 2*i+1; 表示 k 是 i节点的左子节点
        for (int k = 2 * i + 1; k < length; k = 2 * k + 1) {// k=2*k+1 ,k 左子节点的左子节点
            if (k + 1 < length && arr[k] < arr[k + 1]) {// 说明左子节点的值,小于右子节点的值
                k++;// k 指向右子节点
            }
            if (arr[k] > temp) {// 若子节点大于父节点的值
                arr[i] = arr[k]; // 把较大的值赋值给当前节点
                i = k;// 指向 k,继续比较(移动)
            } else {
                break;
            }
        }
        // 当for 循环结束后,已经将以 i 为父节点的树的最大值,放在最顶点(局部)
        arr[i] = temp;// 将 temp放到调整后的位置
    }
}

X9-1、java数据结构---堆排序【2021-1-2】 - 简书 (jianshu.com)

别人的代码

这是测试代码

测试的数组是这样的  初始状态是无序堆

 

这里我们要进行升序排序 那么就需要将无序堆排序成大顶堆

1.第一次排序成大顶堆

因为主函数是只调用了heapSort()方法 所以我们在这里打断点

 

 此时的值

点击下一步后进入到了这里

经过了int temp=0后的值

 

这个if代码是干嘛的  其实是进行大顶堆排序的

我们怎么把一个无序堆排序为大顶堆 

 

 首先先从最下面开始  所以就从这一棵子树开始排序   所以循环里面的i=arr.length/2-1  这里就是 1 注意arr.length/2是整除  所以就算结果是5/2=2.5 但是会取2  所以结果是1  

 所以第一次的排序是从非叶子结点1开始排序

然后一直点击下一步  就进入了if语句里面的代码

也就是这里面

 此时的值

 根据这个可以知道 我们上述的推理是正确的 第一个需要排序的就是结点6的子树

 

这一步又是什么意思

因为要进行堆的跳转 肯定要交换结点 所以设置了一个temp  temp指向的是一个子树的根节点

跳转到下一步(就是经过了temp这一步后 ) 

 此时的值

再进行下一步  这里面的if就是进行排序了 就是判断子树的字节点和根节点的大小比值  然后根据情况进行结点交换

 

此时的值

 

 

根据代码可知 if里面比较的是

  第一比较的是 有没有右字节点

 第二步比的是 左字节点是不是小于右字节点 

为什么要比较  因为此时的i指向的是k 也就是左节点(此时的i是1 指向的结点值为6)

 如果确实左节点小于右结点 那么就让k++ 也就是让arr[k]指向结点值较大的值

所以if语句的作用就是找出左右结点哪一个的值更大

 

经历了上面的if之后

到了这里

 根据此时的值可知

所以if语句里面的值确实增加了arr[k]指向了值更大的9 

 然后再向下运行 就会执行完这个if else语句  这个if语句执行了些什么呢

1.if语句里面是进行值的比较  比较的对象是 我们在之前的if语句里面找到的最大的字节点  和子树的根节点进行比较  

如果满足 那么就让根结点的值变成最大值的字节点   然后下标也跟着变化 (其实就是让根节点变成和最大值字节点一摸一样  本质就是替换结点)

就是变成了这样  只不过和图里面不一样的是 4结点还没从9变成6

 

 到达这里  又开始了for循环

 此时的值

 点击下一步的时候 

就直接到了这里  说明for循环直接跳出来了 因为k的值大于了length    此时的k值是9  length是5

经过了这一步后  原来的结点9就变成了结点6了 

完完全全变成这样了

 然后再向下执行(也就是heapSort的for循环的第一次循环结束 开始进行下一个循环 也就是开始调整下一棵树了)

 此时的值

点击下一步后 就变成了这样

 

i=0 说明应该调整这一棵子树了

 

 然后就是相同的过程了 这里就不细讲了

反正调整了之后就变成这样了

全部子树 

 但是还有一个问题  因为不断的调整导致了树的混乱

这里的结点6应该在顶点  所以需要再调整一下

 

变成这样

 那这是如果变成这样的呢   这一步是在哪里进行的

因为结点1的子树调整完了

然后又会再运行一次for循环 此时的i就变成了0了  

也就是该调整0号子树了    然后就进入adjustHead函数

 

 当经历了k=2*0+1的循环后   

所有的值就变成了这样

为什么i变成了1 因为 经历了这一步

 然后再for循环k就变成了3  ( 2*1+1)

也就是再次调整i=1的子树  

也就是说这次的for循环就是调整这样

 

 

上面的这次调整是在这里  就是i=0的根结点所在的子树  调整完了之后

 

因为还没跳出for进行array[i]=temp

所以此时的数组是这样的

 

 

 

 

最后会执行这段代码  下面这个代码在heapSort()里面 是最后一行

 

 

这三步是干嘛的  将第一个元素和最后一个元素交换位置  保证最大的元素在数组最后  

 

 交换好了之后   会再次按照上述的操作调整大顶堆  但是不同的是 会去掉最后一个元素进行调整

也就是不看结点9   这既是这段代码的含义  

   i是从哪一个结点开始调整  

 每调整完一次 j就会减小1  也就是屏蔽最后一个结点

 

至此所有的就讲完了

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值