堆排序

堆特点

  1. 一个完全二叉树,以数组形式给出,0位置即为根节点
  2. 假设父节点在数组中的位置为i,则其两个子节点(若存在)的位置分别为2i+1与2i+2
  3. 一个节点在数组中的位置为k,则其父节点位置为(k-1)/2;

堆排序

  1. 大顶堆,数组中的最大值位于0位置
  2. 小顶堆,数组中的最小值位于0位置
  3. 其余位置不要求有序

流程

  1. 调整堆结构,使得要排序区间的最大值位于0位置
  2. 交换,将0位置的值与尾部交换

代码

以自然顺序为例,通过大顶堆排序,虽然该代码能正确排序,但是还需要改进,改进地方是调整堆结构哪里

import java.util.Arrays;

public class MyHeapSort {
    public static void main(String[] args) {
        int[] arr = {1,4,3,8,1,6,8,4,0,6,8,4,26,13,48,0,48,6};
        heapSort(arr);
        System.out.println(Arrays.toString(arr));
    }

    /**
     * 堆排
     * @param arr
     */
    public static void heapSort(int[] arr){
    	// 先将其调整为大顶堆
        adjust(arr,arr.length - 1);
        // 堆排,交换,调整
        for (int i = arr.length - 1; i > 0; i--) {
            swap(arr,0,i);
            adjust(arr,i-1);
        }
    }

    /**
     * 调整堆结构
     * @param arr 要调整的数组
     * @param len 要调整的区间,从0开始的
     */
    public static void adjust(int[] arr,int len){
    	// 从最后的非叶子节点调整
        for (int i = (len-1)/2; i >= 0; i--) {
        	// 找到其左子节点位置
            int k =  2*i+1;
            // 如果该节点存在右子节点或者在调整的区间内
            // 找到两个子节点的最大值的位置
            if(k+1 <= len && arr[k+1] > arr[k]){
                k++;
            }
            // 如果子节点中存在比该节点的值比较大的就交换
            if(arr[k] > arr[i]){
                swap(arr,i,k);
            }
        }
    }

    public static void swap(int[] arr,int i,int j){
        int tmp = arr[i];
        arr[i] = arr[j];
        arr[j] = tmp;
    }
}

结果
在这里插入图片描述
改进后的代码,以韩顺平老师的代码为例吧

import java.util.Arrays;

public class HeapSort {
    public static void main(String[] args) {
        int n = 800000;
        int[] arr = new int[n];
        int[] arr1 = new int[n];
        for (int i = 0; i < n; i++) {
            arr[i] = (int)(Math.random()*n);
            arr1[i] = arr[i];
        }
        long start = System.currentTimeMillis();
        new HeapSort().heapSort(arr);
        long mid = System.currentTimeMillis();
        System.out.println("myHeapSort cost: " + (mid-start)+"ms");
        Arrays.sort(arr1);
        long end = System.currentTimeMillis();
        System.out.println("ArraysSort cost: " + (end - mid)+"ms");
        System.out.println(Arrays.equals(arr,arr1));
    }

    void heapSort(int arr[]){
        //将无序序列构建成一个堆,根据升序降序需求选择大顶堆或小顶堆
        for (int i = arr.length / 2 - 1; i >= 0 ; i--) {
            adjustHeap(arr,i,arr.length);
        }
        /*
          2.将堆顶元素于末尾元素交换,将最大元素沉到数组末端
          3.重新调整结构,使其满足堆定义,然后继续交换堆顶元素与当前末尾元素,反复执行调整+交换步骤,直到整个序列有序
         */
        for (int j = arr.length - 1; j > 0; j--) {
            int tmp = arr[j];
            arr[j] = arr[0];
            arr[0] = tmp;
            adjustHeap(arr,0,j);
        }
    }

    /**
     * 将一个数组(二叉树),调整成一个大顶堆
     * 功能:完成将i对应的非叶子节点的树调整成大顶堆
     * 举例:int[] arr = {4,6,8,5,9}; => i=1 => adjustHeap => 得到{4,9,8,5,6}
     * 如果再次调用adjustHeap传入的是 i = 0 => 得到{4,9,8,5,6} => {9,6,8,5,4}
     * @param arr 待调整的数组
     * @param i 表示非叶子节点在数组中的索引
     * @param length 表示对多少个元素继续调整,length是在逐渐减少的
     */
    void adjustHeap(int[] arr,int i,int length){
        int tmp = arr[i];//取出当前元素的值保存在临时变量
        //开始调整
        //1.k = i * 2 + 1是i节点的左子节点
        for (int k = i * 2 + 1; k < length; k = k * 2 +1) {
            if (k+1 < length && arr[k] < arr[k+1]){//说明左子节点的值小于右子节点的值
                k++;//k指向右子节点
            }
            if (arr[k] > tmp){//如果子节点大于父节点
                arr[i] = arr[k];//把较大的值赋给当前节点
                i = k;//i指向k,继续循环比较
            }else {
                break;
            }
        }
        //当for循环结束后,已经将以i为父节点的树的最大值,放到了最顶(局部)
        arr[i] = tmp;//将temp值放到调整后的位置
    }
}

执行结果
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值