关闭

堆排序的Java实现

标签: java堆排序
434人阅读 评论(0) 收藏 举报
分类:

堆排序的Java实现

由于这里是堆排序,因此还是说明下堆排序的原理:

一般堆是用数组来进行模拟的,arr[k]的子节点为arr[k * 2+1]和 arr[k * 2+2] .

堆排序就是解决以下两个问题:

1、如何将一个待排序数组(含有n个元素)建成堆?

2、输出堆顶元素之后,怎么样调整剩余的n-1个元素使其变为一个新堆然后进行这一步。

以最小堆为例

至于第一个问题:就是将二叉堆的非叶子节点(对应的数组的arr[(len/2)-1]~arr[0])都依次向后沉。”向后沉”的意思如果父节点的值大于两个子节点中的最小值就进行交换然后继续进行这样的判断。

至于第二个问题:堆化数组后,第一次将A[0]与A[n - 1]交换,再对A[0…n-2]重新恢复堆(这里的恢复堆就是将A[0]向后沉即可)。第二次将A[0]与A[n – 2]交换,再对A[0…n - 3]重新恢复堆,重复这样的操作直到A[0]与A[1]交换。由于每次都是将最小的数据并入到后面的有序区间,故操作完成后整个数组就有序了。

注意使用最小堆排序后是递减数组,要得到递增数组,可以使用最大堆。

由于每次重新恢复堆的时间复杂度为O(logN),共N - 1次重新恢复堆操作,再加上前面建立堆时N / 2次向下调整,每次调整时间复杂度也为O(logN)。二次操作时间相加还是O(N * logN)。故堆排序的时间复杂度为O(N * logN)。

按照上面两步的思路实现的代码如下:

    public static void heapSort(int[] arr, int len) {
        //第一步先调整arr为最小堆
        if(arr==null||len<1){
            return ;
        }
        for(int i=(len/2)-1;i>=0;i--){//对于每个非叶子节点开始向下调整
            siftDown(arr,i,len);
        }
    //      System.out.println("数组调整之后为:");
    //      print(arr,len);
        //第二步,开始排序
        int begin = 0;
        int end = len-1;
        while(begin<end){
            //先交换
            swap(arr,begin,end);
            //然后调数组arr从下标为0~end-1位置为一个最小堆。
            siftDown(arr,0,end);//这里的end为剩余的长度

            //调整之后进行下一轮
            end--;  
        }
    }

其中,二叉堆中”下沉” 某个元素的实现代码如下:

    //函数功能:将arr[index]元素向后沉。
    private static void siftDown(int[] arr, int index, int len) {
        if(arr==null||len<1||index<0||index>len){
            return;
        }

        int half = len/2;
        int val = arr[index];
        while(index<half){
            int leftChild = (index<<1) + 1;
            int rightChild = leftChild + 1;
            int minIndex = leftChild;
            if(rightChild<len&&arr[rightChild]<arr[leftChild]){
                minIndex = rightChild;
            }
            //将arr[minIndex]与 val进行比较
            if(arr[minIndex]>=val){
                break;
            }
            //交换
            arr[index] = arr[minIndex];
            index = minIndex;
        }
        arr[index] = val;
    }

上面还涉及到一个交换数组中两个位置的元素的方法如下:

    //函数功能:完成数组位置begin/end的元素的交换
    private static void swap(int[] arr, int begin, int end) {
        if(arr==null||begin<0||begin>=arr.length||end<0||end>=arr.length){
            return;
        }
        int temp = arr[begin];
        arr[begin] = arr[end];
        arr[end] = temp;
    }

测试代码如下:

    public static void main(String[] args) {
        Random r = new Random(47);
        for(int j=0;j<20;j++){
            int len = r.nextInt(20);
            int [] arr = new int[len];

            for(int i=0;i<len;i++){
                arr[i] = r.nextInt(2*len);
            }
    //          int len = 10;
    //          int[] arr = {9,12,17,30,50,20,60,65,4,49};

            heapSort(arr,len);
            System.out.println("数组排序后的结果为:");
            print(arr,len);
        }

    }


    //函数功能:打印输出
    private static void print(int[] arr,int len) {
        if(arr==null||len<1){
            return;
        }

        for(int i=0;i<len;i++){
            System.out.print(arr[i]+" ");
        }
    }

以上就是关于堆排序的一个实现。还是比较简单哈。

使用最大堆的实现堆排序

上面是采用的是最小堆完成的堆排序,是逆序。下面是采用最大堆的形式完成的堆排序。

与最小堆相比,改动的地方就是“下沉”函数siftDown。在最小堆中,是父节点大于两个子节点的最大值就下层。而在最大堆中,是父节点小于两个子节点的最小值就下层。就这一点区别。

siftDown函数代码如下:

    //函数功能:将arr[index]元素向后沉。
    private static void siftDown(int[] arr, int index, int len) {
        if(arr==null||len<1||index<0||index>len){
            return;
        }

        int half = len/2;
        int val = arr[index];
        while(index<half){
            int leftChild = (index<<1) + 1;
            int rightChild = leftChild + 1;
            //现在是以最大堆为例,则是小的往下沉
            int maxIndex = leftChild;
            if(rightChild<len&&arr[rightChild]>arr[leftChild]){
                maxIndex = rightChild;
            }
            //将arr[maxIndex]与 val进行比较
            if(arr[maxIndex]<=val){
                break;
            }
            //交换
            arr[index] = arr[maxIndex];
            index = maxIndex;
        }
        arr[index] = val;
    }

小结

自己以前一直没有研究过堆排序,原因在于感觉挺复杂的,这两天看了PriorityQueue和PriorityBlockingQueue这两个类的源码之后,发现,原来二叉堆的“下沉”和“上浮”还是挺有意思的,于是自己也就一下子就理解了。因此就有了这篇关于堆排序的实现。

关于堆和堆排序的介绍还可以参考这篇博文:http://blog.csdn.net/morewindows/article/details/6709644

1
0
查看评论

【排序算法】堆排序原理及Java实现

1、基本思想堆是一种特殊的树形数据结构,其每个节点都有一个值,通常提到的堆都是指一颗完全二叉树,根结点的值小于(或大于)两个子节点的值,同时,根节点的两个子树也分别是一个堆。 堆排序就是利用堆(假设利用大顶堆)进行排序的方法。它的基本思想是,将待排序的序列构造成一个大顶堆。此时,整个序列的最大...
  • jianyuerensheng
  • jianyuerensheng
  • 2016-04-27 18:34
  • 8188

HeapSort堆排序Java实现图文代码详解

堆排序(Heapsort)堆积树设计的一种排序算法,可以利用数组的特点快速定位指定索引的元素。 排序图如下:(gif来自维基百科) 堆排序的过程就是首先构建大根堆,然后对顶元素(及最大元素)与最后个元素替换位置,heapsize减一,重新调整堆变成大根堆。重复上面操作直到heapsize等于一...
  • wg1033755123
  • wg1033755123
  • 2015-04-12 14:04
  • 760

堆排序(Heapsort)之Java实现

堆排序算法介绍 堆是一种重要的数据结构,为一棵完全二叉树, 底层如果用数组存储数据的话,假设某个元素为序号为i(Java数组从0开始,i为0到n-1), 如果它有左子树,那么左子树的位置是2i+1,如果有右子树,右子树的位置是2i+2,如果有父节点,父节点的位置是(n-1)/2取整。分为最大堆和最小...
  • kimylrong
  • kimylrong
  • 2013-12-05 22:25
  • 53243

【java面试】算法篇之堆排序

一、堆的概念 堆是一棵顺序存储的完全二叉树。完全二叉树中所有非终端节点的值均不大于(或不小于)其左、右孩子节点的值。 其中每个节点的值小于等于其左、右孩子的值,这样的堆称为小根堆; 其中每个节点的值大于等于其左、右孩子的值,这样的堆称为大根堆; 二、要点 1.将数组构造成初始堆(若想升序则建立...
  • qq_21492635
  • qq_21492635
  • 2017-06-12 14:23
  • 496

基本算法_堆排序_Java实现

堆排序就是将序列调整为指定的堆,然后调整首尾元素的位置,取出末尾元素,反复进行,直到将所有元素取出,形成一个有序的序列。
  • ljmingcom304
  • ljmingcom304
  • 2015-12-22 14:14
  • 1978

算法(第四版)学习笔记之java实现堆排序

继上一篇实现基于堆的优先队列后,这次将利用上一次完成的基于堆的能够重复删除最大元素操作的优先队列来实现一种经典而优雅的排序算法,称之为堆排序。 堆排序可分为两个阶段: 1.构建堆:在堆的构建过程中,我们将原始数组重新组织安排进一个堆中; 2.下沉排序:从堆中按递减顺序取出所有元素并得到排序结果...
  • l243225530
  • l243225530
  • 2015-07-26 16:02
  • 1038

《排序算法》——堆排序(大顶堆,小顶堆,Java)

堆的定义如下:   n个元素的序列{k0,k1,...,ki,…,k(n-1)}当且仅当满足下关系时,称之为堆。   " ki=k2i,ki>=k2i+1.(i=1,2,…,[n/2])"   若将和此次序列对应的一维数组(即以一维数组作此序列的存储结构)看成是一个完全二叉...
  • Gamer_gyt
  • Gamer_gyt
  • 2015-07-30 09:12
  • 2558

java堆排序递归代码,无原理版,比较好理解

public class HeapOperate2 { /* * 建立堆时只需要保证根结点小于两个子结点或者大于两个子结点,对两个子结点大小没有要求 */ public static void main(String[] args) { int[] a = { 9, 8, 7, 6...
  • zhuqiuhui
  • zhuqiuhui
  • 2016-04-21 23:29
  • 1204

java堆排序非递归代码,无原理版,比较好理解

public class HeapOperate { /* * 建立堆时只需要保证根结点小于两个子结点或者大于两个子结点,对两个子结点大小没有要求 */ public static void main(String[] args) { //数组0下标元素作为暂存单元 int d...
  • zhuqiuhui
  • zhuqiuhui
  • 2016-04-21 23:01
  • 810

堆排序分析及优化

堆排序,是利用堆结构进行排序,一般是利用最大堆,即根节点比左右两个子树的节点都大,具体算法步骤如下。 一、创建堆         首先将数组中的元素调整成堆,对应下面程序中的createHeap(List list)方法。创建堆就是从树中最后一个内节点(下...
  • x_i_y_u_e
  • x_i_y_u_e
  • 2015-04-09 15:21
  • 1433
    个人资料
    • 访问:699032次
    • 积分:12687
    • 等级:
    • 排名:第1311名
    • 原创:606篇
    • 转载:14篇
    • 译文:0篇
    • 评论:115条
    联系方式
    有问题欢迎探讨咨询哈
    qq号就不留了哈
    欢迎留言
    博客专栏
    最新评论