堆排序算法的java实现

     堆积排序(Heapsort)是指利用堆积树(堆)这种资料结构所设计的一种排序算法,可以利用数组的特点快速定位指定索引的元素。堆排序是不稳定的排序方法,辅助空间为O(1), 最坏时间复杂度为O(nlog2n) ,堆排序的堆序的平均性能较接近于最坏性能。

中心思想是在使用数组存储的完全二叉树内从下往上每次构造大顶堆或者小顶堆,然后将找出来的堆顶数字放到数组结尾,剩下数组继续构造堆结构。

主要是参考了网上比较常见的两种堆排序的java实现,自己加了一些注释

实现1

采用递归,每次父节点与最大子节点交换后递归构造被交换后的子树

    public static void heapSort(int[] array) {
        if (array == null || array.length <= 1) {
            return;
        }

        buildMaxHeap(array);

        System.out.println("buildMaxHeap " + Arrays.toString(array));

        for (int i = array.length - 1; i >= 1; i--) {
            exchangeElements(array, 0, i);

            maxHeap(array, i, 0);

            System.out.println("maxHeap " + Arrays.toString(array) + " i is "
                    + i);
        }

    }

    private static void buildMaxHeap(int[] array) {
        if (array == null || array.length <= 1) {
            return;
        }

        int half = array.length / 2 - 1;
        // 根据二叉树性质,深度为k的二叉树至多有2的k次方-1个结点(k≥1)
        // 所以如果最末尾节点为右节点,array.length为奇数,那么上一层父节点的编号应该为(array.length-1)/2=array.length/2
        // 所以如果最末尾节点为左节点,array.length为偶数,那么上一层父节点的编号也为array.length/2
        // 由于数组下标从0开始,所以应该要在堆对应的编号基础上-1

        // 从下往上把比较中最大的值往顶上冒,冒过后要把被换下来的值对应的子树再做一遍堆调整。
        for (int i = half; i >= 0; i--) {
            maxHeap(array, array.length, i);
        }
    }

    private static void maxHeap(int[] array, int heapSize, int index) {
        // 堆编号x ,数组编号index ,a=index+1;
        // 所以左节点数组编号=2a-1=index * 2 + 1
        // 右节点数组编号=2a+1-1=index * 2 + 2

        int left = index * 2 + 1;
        int right = index * 2 + 2;

        int largest = index;
        if (left < heapSize && array[left] > array[index]) {
            largest = left;
        }

        if (right < heapSize && array[right] > array[largest]) {
            largest = right;
        }

        if (index != largest) {
            exchangeElements(array, index, largest);// 将子节点更大的值换到父节点

            System.out.println("maxHeap " + Arrays.toString(array)
                    + " index is " + index + " left is " + left + " right is "
                    + right + " largest is " + largest + " heapSize is "
                    + heapSize);

            maxHeap(array, heapSize, largest);// 原有父节点的值放到了子节点后可能不满足堆的性质,需要调整修改后largest节点对应的子树
        }
    }

    private static void exchangeElements(int[] array, int index1, int index2) {
        int temp = array[index1];
        array[index1] = array[index2];
        array[index2] = temp;
    }

实现2

while循环,同样父子节点交换后记录被换过的子节点位置,使用while (2 * k + 1 <= lastIndex)循环判断对应的子树是否符合堆性质并调整

    public static void heapSort2(int[] array) {
        for (int i = 0; i < array.length; i++) {
            maxHeap2(array, array.length - 1 - i);
            exchangeElements(array, 0, array.length - 1 - i);
            System.out.println(Arrays.toString(array));
        }

    }

    private static void exchangeElements(int[] array, int index1, int index2) {
        int temp = array[index1];
        array[index1] = array[index2];
        array[index2] = temp;
    }

    private static void maxHeap2(int[] data, int lastIndex) {

        //lastIndex= array.length - 1
        //所以(lastIndex+1)/2-1等于上层最后一个有子节点的节点在数组中的索引
        //(lastIndex+1)/2-1=(lastIndex-1)/2
        for (int i = (lastIndex - 1) / 2; i >= 0; i--) {
            // 保存当前正在判断的节点
            int k = i;

            // 若当前节点的左节点存在
            while (2 * k + 1 <= lastIndex) {//

                // biggerIndex总是记录较大节点的值,先赋值为当前判断节点的左子节点
                int biggerIndex = 2 * k + 1;
                if (biggerIndex < lastIndex) {
                    // 若右子节点存在,比较左右子节点大小,右节点不存在biggerIndex为左节点
                    if (data[biggerIndex] < data[biggerIndex + 1]) {
                        // 若右子节点值比左子节点值大,则biggerIndex记录的是右子节点的值
                        biggerIndex++;
                    }
                }
                if (data[k] < data[biggerIndex]) {
                    // 若当前节点值比子节点最大值小,则交换2者得值,交换后将biggerIndex值赋值给k
                    exchangeElements(data, k, biggerIndex);
                    k = biggerIndex; //k记录了原来的父节点被换到了什么位置,原来的父节点下来后不一定比子节点更大
                    //while循环继续去判断它对应的子树符不符合堆的性质并调整
                    System.out.println("k is "+k+" "+Arrays.toString(data));

                } else {
                    //父节点已经比子节点大了,不需要调整
                    break;
                }

                //System.out.println();
            }
        }

    }

参考资料

http://blog.csdn.net/apei830/article/details/6584645

http://blog.csdn.net/kimylrong/article/details/17150475

时间: 2024-10-29 21:55:18

堆排序算法的java实现的相关文章

必须知道的八大种排序算法【java实现】(三) 归并排序算法、堆排序算法详解

一.归并排序算法 基本思想: 归并(Merge)排序法是将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的.然后再把有序子序列合并为整体有序序列. 归并排序示例: 合并方法: 设r[i-n]由两个有序子表r[i-m]和r[m+1-n]组成,两个子表长度分别为n-i +1.n-m. j=m+1:k=i:i=i; //置两个子表的起始下标及辅助数组的起始下标 若i>m 或j>n,转⑷ //其中一个子表已合并完,比较选取结束 //选取r[i]和r[j]

7种基本排序算法的Java实现

7种基本排序算法的Java实现 转自我的Github 以下为7种基本排序算法的Java实现,以及复杂度和稳定性的相关信息. 以下为代码片段,完整的代码见Sort.java 插入排序 1 /** 2 * 直接插入排序 3 * 不稳定 4 * 时间复杂度:O(n^2) 5 * 最差时间复杂度:O(n^2) 6 * 空间复杂度:O(1) 7 * 使用场景:大部分元素有序 8 * @param elements 9 * @param comparator 10 * @param <T> 11 */ 1

[转]各种排序算法及其java程序实现

原文:http://blog.csdn.net/t12x3456/article/details/7430700 各种排序算法:冒择路(入)兮(稀)快归堆,桶式排序,基数排序 冒泡排序,选择排序,插入排序,稀尔排序,快速排序,归并排序,堆排序,桶式排序,基数排序 一.冒泡排序(BubbleSort)1. 基本思想: 两两比较待排序数据元素的大小,发现两个数据元素的次序相反时即进行交换,直到没有反序的数据元素为止.2. 排序过程: 设想被排序的数组R[1..N]垂直竖立,将每个数据元素看作有重量的

几大排序算法的Java实现

很多的面试题都问到了排序算法,中间的算法和思想比较重要,这边我选择了5种常用排序算法并用Java进行了实现.自己写一个模板已防以后面试用到.大家可以看过算法之后,自己去实现一下. 1.冒泡排序:大数向下沉,小数向上浮. package TestProject; /** * 冒泡排序 * @author xuhui * */ public class SortAll { public static void main(String[] args){ int[] a = {0,8,1,2,8,6,1

堆排序算法实现

关于堆排序算法的思想,网上有很多介绍,这里不再解释,这里提供了两个Java类,读者可以把类潜入到自己的程序中,直接调用,免去了重新编写堆排序的过程. 分为两个堆排序接口,一个是数组从下标1开始存储的堆排序类Duisort1,另一个是从下标0开始存储的堆排序类Duisort2,具体的Java代码如下: 1 import java.util.*; //导入包 2 //这个类的堆排序元素是从下标1开始的 3 class Duisort1 { //从小到大建立大头堆,从大到小建立小头堆 4 5 publ

算法基础——经典八大排序算法的Java及Python实现

概述 八大排序算法不用多说了,程序员算法基础必须要掌握的,现在总结一下加深记忆.下图是这八大排序算法的分类.名称.时间空间复杂度,以及稳定性. 代码 以下是经典八大排序算法的Java及Python代码,都是基于经典算法书籍<算法导论>里的伪代码实现的,我在关键语句部分附上了注释. 按照上图中的顺序分别介绍八大排序算法的实现(升序),前面是Java,后面是Python.Java的排序函数写在了一个类里,Python的排序函数则直接写出来了. 直接插入排序 public class InsertS

程序员必须掌握的8大排序算法(Java版)

程序员必须掌握的8大排序算法(Java版) 提交 我的评论 加载中 已评论 程序员必须掌握的8大排序算法(Java版) 2015-07-28 极客学院 极客学院 极客学院 微信号 jikexueyuan00 功能介绍 极客学院官方帐号,最新课程.活动发布.欢迎大家反馈问题哟^_^ 本文由网络资料整理而来,如有问题,欢迎指正! 分类: 1)插入排序(直接插入排序.希尔排序) 2)交换排序(冒泡排序.快速排序) 3)选择排序(直接选择排序.堆排序) 4)归并排序 5)分配排序(基数排序) 所需辅助空

一文搞定十大经典排序算法(Java实现)

本文总结十大经典排序算法及变形,并提供Java实现. 参考文章: 十大经典排序算法总结(Java语言实现) 快速排序算法—左右指针法,挖坑法,前后指针法,递归和非递归 快速排序及优化(三路划分等) 一.排序算法概述 1.定义 将杂乱无章的数据元素,通过一定的方法按关键字顺序排列的过程叫做排序. 2.分类 十种常见排序算法可以分为两大类: 非线性时间比较类排序:通过比较来决定元素间的相对次序,由于其时间复杂度不能突破O(nlogn),因此称为非线性时间比较类排序. 线性时间非比较类排序:不通过比较

十大经典排序算法(Python,Java实现)

参照:https://www.cnblogs.com/wuxinyan/p/8615127.html https://www.cnblogs.com/onepixel/articles/7674659.html 一.排序算法分类: 比较类排序:通过比较来决定元素间的相对次序,由于其时间复杂度不能突破O(nlogn),因此也称为非线性时间比较类排序. 非比较类排序:不通过比较来决定元素间的相对次序,它可以突破基于比较排序的时间下界,以线性时间运行,因此也称为线性时间非比较类排 二.算法复杂度 注(