zoukankan      html  css  js  c++  java
  • 堆排序

    package sort;
    
    import java.util.Arrays;
    
    public class HeapSort {
        public static void main(String[] args) {
            int[] array = new int[]{2, 1, 4, 3, 6, 5, 8, 7};
            // 接下来就是排序的主体逻辑
            sort(array);
            System.out.println(Arrays.toString(array));
        }
    
        public static void sort(int[] array) {
            // 按照完全二叉树的特点,从最后一个非叶子节点开始,对于整棵树进行大根堆的调整
            // 也就是说,是按照自下而上,每一层都是自右向左来进行调整的
            // 注意,这里元素的索引是从0开始的
            // 另一件需要注意的事情,这里的建堆,是用堆调整的方式来做的
            // 堆调整的逻辑在建堆和后续排序过程中复用的
            for (int i = array.length / 2 - 1; i >= 0; i--) {
                adjustHeap(array, i, array.length);
            }
    
            // 上述逻辑,建堆结束
            // 下面,开始排序逻辑
            for (int j = array.length - 1; j > 0; j--) {
                // 元素交换
                // 说是交换,其实质就是把大顶堆的根元素,放到数组的最后;换句话说,就是每一次的堆调整之后,都会有一个元素到达自己的最终位置
                swap(array, 0, j);
                // 元素交换之后,毫无疑问,最后一个元素无需再考虑排序问题了。
                // 接下来我们需要排序的,就是已经去掉了部分元素的堆了,这也是为什么此方法放在循环里的原因
                // 而这里,实质上是自上而下,自左向右进行调整的
                adjustHeap(array, 0, j);
            }
        }
    
        /**
         * 这里,是整个堆排序最关键的地方,正是因为把这个方法抽取出来,才更好理解了堆排序的精髓,会尽可能仔细讲解
         *
         * @param array
         * @param i
         * @param length
         */
        public static void adjustHeap(int[] array, int i, int length) {
            // 先把当前元素取出来,因为当前元素可能要一直移动
            int temp = array[i];
            // 可以参照sort中的调用逻辑,在堆建成,且完成第一次交换之后,实质上i=0;也就是说,是从根所在的最小子树开始调整的
            // 接下来的讲解,都是按照i的初始值为0来讲述的
            // 这一段很好理解,如果i=0;则k=1;k+1=2
            // 实质上,就是根节点和其左右子节点记性比较,让k指向这个不超过三个节点的子树中最大的值
            // 这里,必须要说下为什么k值是跳跃性的。
            // 首先,举个例子,如果a[0] > a[1]&&a[0]>a[2],说明0,1,2这棵树不需要调整,那么,下一步该到哪个节点了呢?肯定是a[1]所在的子树了,
            // 也就是说,是以本节点的左子节点为根的那棵小的子树
            // 而如果a[0}<a[2]呢,那就调整a[0]和a[2]的位置,然后继续调整以a[2]为根节点的那棵子树,而且肯定是从左子树开始调整的
            // 所以,这里面的用意就在于,自上而下,自左向右一点点调整整棵树的部分,直到每一颗小子树都满足大根堆的规律为止
            for (int k = 2 * i + 1; k < length; k = 2 * k + 1) {
                System.out.println("k==========="+k);
                // 让k先指向子节点中最大的节点
                if (k + 1 < length && array[k] < array[k + 1]) {
                    k++;
                }
    
                // 如果发现子节点更大,则进行值的交换
                if (array[k] > temp) {
                    swap(array, i, k);
                    // 下面就是非常关键的一步了
                    // 如果子节点更换了,那么,以子节点为根的子树会不会受到影响呢?
                    // 所以,循环对子节点所在的树继续进行判断
                    i = k;
                    // 如果不用交换,那么,就直接终止循环了
                } else {
                    break;
                }
            }
        }
    
        /**
         * 交换元素
         *
         * @param arr
         * @param a   元素的下标
         * @param b   元素的下标
         */
        public static void swap(int[] arr, int a, int b) {
            int temp = arr[a];
            arr[a] = arr[b];
            arr[b] = temp;
        }
    }
    

      

    参考 

    图解排序算法(三)之堆排序   https://www.cnblogs.com/chengxiao/p/6129630.html

  • 相关阅读:
    AngularJS-webapp($q)
    mybatis
    maven联通网络下中央仓库不能访问的解决办法
    IDEA闪退问题
    scala学习笔记(8)文件和正则表达式
    scala学习笔记(8)
    scala学习笔记(7)
    kafka复习(2)
    第一天
    while循环
  • 原文地址:https://www.cnblogs.com/chengpeng15/p/10748955.html
Copyright © 2011-2022 走看看