java使用堆结构进行排序及索引堆

文章介绍了两种方法实现堆排序,一种是使用额外空间构建堆并排序,另一种是针对最大堆进行逐步交换操作。主要涉及shiftdown操作,以及如何使用索引堆优化交换效率。
摘要由CSDN通过智能技术生成

在排序前需要shift down将无序数组转成堆结构,即父节点数据始终不小于或不大于子节点。用以下代码实现:

 //从第一个不是叶子节点的元素开始shift down,将data转为堆结构
        for (int i=count/2;i>=1;i--){
            shiftDown(i);
        }

注:从一个最大堆中取出一个元素(只能取出最大优先级的元素,也就是根节点元素),然后将最后一位元素移至堆顶,为保持堆结构将其下移至合适位置的操作称为 shift down

方法一:

      开辟额外的空间进行构造堆和对堆进行排序

方法二:

       对于一个最大堆,首先将开始位置数据和数组末尾数值进行交换,那么数组末尾就是最大元素,然后再对堆顶元素进行 shift down 操作,重新生成最大堆,然后将新生成的最大数和整个数组倒数第二位置进行交换,此时倒数第二位置就是倒数第二大数据,这个过程以此类推。

两个方法的完整代码如下:

public class HeapSort<T extends Comparable> {
    private T[] data;//堆结构数据
    private int count;//堆中的元素个数
    public HeapSort(T[] arr){
        data = (T[]) new Comparable[arr.length+1];
        count = arr.length;
        //将arr赋值给data
        for( int i = 0 ; i < arr.length; i ++ ){
            data[i+1] = arr[i];
        }
        //从第一个不是叶子节点的元素开始shift down,将data转为堆结构
        for (int i=count/2;i>=1;i--){
            shiftDown(i);
        }
    }
    //从一个最大堆中取出一个元素(只能取出最大优先级的元素,也就是根节点元素),
    // 然后将最后一位元素移至堆顶,为保持堆结构将其下移至合适位置的操作称为 shift down
    public void shiftDown(int index){
        if(index>=1 && count>0){
//            swap(data,count,index);
//            count--;
            int k=index;
            while ( k*2<=count){
                //右子节点不为空且大于左边子节点,则和k位置元素比较交换
                if (2*k+1<=count && data[2*k].compareTo(data[2*k+1])<0){
                    if (data[2*k+1].compareTo(data[k])>0){
                        swap(data,2*k+1,k);
                        k=2*k+1;
                    }else {
                        break;
                    }
                }else {
                    if (data[2*k].compareTo(data[k])>0){
                        swap(data,2*k,k);
                        k=2*k;
                    }else {
                        break;
                    }
                }
            }
        }
        //print(data);
    }
    public  void swap(T[] arr,int i,int j){
        T tem=arr[i];
        arr[i]=arr[j];
        arr[j]=tem;
    }

    //取出最大值并将最后一个元素移至堆顶的操作
    public T extractMax(){
        T max=data[1];
        data[1]=data[count];
        data[count]=null;
        count--;
        return max;
    }
    public void sort(){
        int n=count;//记录堆中所有元素个数
        System.out.println("n = " + n);
        print(data);
        while (count>=1){
            swap(data, count,1);
            count--;
            shiftDown(1);
        }
        count=n;
        print(data);
    }
    // 测试 MaxHeap
    public static void main(String[] args) {
        //对无序数组arr进行堆排序

        Integer[] oldArr=new Integer[]{null,15,17,19,13,22,16,28,30,41,62};
        //对arr中元素为null的处理
        Integer[] arr= Arrays.stream(oldArr).filter(i -> i!=null).toArray(Integer[]::new);
        //通过构造函数获得arr的堆结构数组data
        HeapSort<Integer> heapSort = new HeapSort<Integer>(arr);
        //方法一、这是利用额外的空间进行构造堆和对堆进行排序
   /*     print(arr);
        for (int i=0;i<arr.length;i++){
            //将堆的最大值取出赋给arr
            arr[i]= heapSort.extractMax();
            //取出最大值后进行shift down
            heapSort.shiftDown(1);
            System.out.println("arr[i] = " + arr[i]);
        }
        print(arr);*/

        //方法二、对于一个最大堆,首先将开始位置数据和数组末尾数值进行交换,那么数组末尾就是最大元素,
        // 然后再对堆顶元素进行 shift down 操作,重新生成最大堆,
        // 然后将新生成的最大数和整个数组倒数第二位置进行交换,此时倒数第二位置就是倒数第二大数据,这个过程以此类推。
        heapSort.sort();
    }
    public void print(T[] arr) {
        for (T n : arr) {
            System.out.print(n + " ");
        }
        System.out.println(" ");
    }
}

使用索引堆:

一、概念及其介绍

索引堆是对堆这个数据结构的优化。

索引堆使用了一个新的 int 类型的数组,用于存放索引信息。

相较于堆,优点如下:

  • 优化了交换元素的消耗。
  • 加入的数据位置固定,方便寻找。

二、适用说明

如果堆中存储的元素较大,那么进行交换就要消耗大量的时间,这个时候可以用索引堆的数据结构进行替代,堆中存储的是数组的索引,我们相应操作的是索引。

使用索引堆完成两个方法的完整代码:

public class HeapSort<T extends Comparable> {
    private T[] data;//堆结构数据
    private int[] indexes;//索引堆
    private int count;//堆中的元素个数
    public HeapSort(T[] arr){
        data = (T[]) new Comparable[arr.length+1];
        indexes=new int[arr.length+1];
        count = arr.length;
        //将arr赋值给data,并给索引堆赋予data的数据索引
        for( int i = 0 ; i < arr.length; i ++ ){
            data[i+1] = arr[i];
            indexes[i+1]=i+1;
        }
        //从第一个不是叶子节点的元素开始shift down,将data转为堆结构,但操作的是indexes堆,data实际上未变
        for (int i=count/2;i>=1;i--){
            shiftDown(i);
        }
    }
    //从一个最大堆中取出一个元素(只能取出最大优先级的元素,也就是根节点元素),
    // 然后将最后一位元素移至堆顶,为保持堆结构将其下移至合适位置的操作称为 shift down
    public void shiftDown(int index){
        if(index>=1 && count>0){
            int k=index;
            while ( k*2<=count){
                //右子节点不为空且大于左边子节点,则和k位置元素比较交换
                if (2*k+1<=count && data[indexes[2*k]].compareTo(data[indexes[2*k+1]])<0){
                    if (data[indexes[2*k+1]].compareTo(indexes[k])>0){
                        swap(indexes,2*k+1,k);
                        k=2*k+1;
                    }else {
                        break;
                    }
                }else {
                    if (data[indexes[2*k]].compareTo(data[indexes[k]])>0){
                        swap(indexes,2*k,k);
                        k=2*k;
                    }else {
                        break;
                    }
                }
            }
        }
        //print(data);
    }
    public  void swap(T[] arr,int i,int j){
        T tem=arr[i];
        arr[i]=arr[j];
        arr[j]=tem;
    }
    public  void swap(int[] arr,int i,int j){
        int tem=arr[i];
        arr[i]=arr[j];
        arr[j]=tem;
    }

    //取出最大值并将最后一个元素移至堆顶的操作
    public T extractMax(){
        int max=indexes[1];
        indexes[1]=indexes[count];
        indexes[count] = -1;
        count--;
        return data[max];
    }
    public void sort(){
        int n=count;//记录堆中所有元素个数
        print(data,indexes);
        while (count>=1){
            swap(indexes, count,1);
            count--;
            shiftDown(1);
        }
        count=n;
        print(data,indexes);
    }
    // 测试 MaxHeap
    public static void main(String[] args) {
        //对无序数组arr进行堆排序

        Integer[] oldArr=new Integer[]{null,15,19,13,62,22,16,28,30,41,17};
        //对arr中元素为null的处理
        Integer[] arr= Arrays.stream(oldArr).filter(i -> i!=null).toArray(Integer[]::new);
        //通过构造函数获得arr的堆结构数组data
        HeapSort<Integer> heapSort = new HeapSort<Integer>(arr);
        //方法一、这是利用额外的空间进行构造堆和对堆进行排序
     /*   heapSort.print(arr);
        for (int i=0;i<arr.length;i++){
            //将堆的最大值取出赋给arr
            arr[i]=heapSort.extractMax();
            //取出最大值后进行shift down
            heapSort.shiftDown(1);
        }
        heapSort.print(arr);*/

        //方法二、对于一个最大堆,首先将开始位置数据和数组末尾数值进行交换,那么数组末尾就是最大元素,
        // 然后再对堆顶元素进行 shift down 操作,重新生成最大堆,
        // 然后将新生成的最大数和整个数组倒数第二位置进行交换,此时倒数第二位置就是倒数第二大数据,这个过程以此类推。
        heapSort.sort();
    }
    public void print(T[] arr,int[] indexes) {
        for (int n : indexes) {
            System.out.print(arr[n] + " ");
        }
        System.out.println(" ");
    }

    public void print(T[] arr) {
        for (T n : arr) {
            System.out.print(n + " ");
        }
        System.out.println(" ");
    }

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值