堆排序--(大顶堆/二叉堆/top K个最大值:排行榜)

堆排序相关的数据结构知识:

  1.   满二叉树/完全二叉树的区别;
  2.   二叉堆与满足条件: 完全二叉树+任意父节点都大于/小于他的子节点;
  3.   大顶堆和小顶堆;
  4.   二叉树的先序,中序,后序遍历(根的先后顺序)

     5.用数组构建二叉堆时, 数组就像是"物理结构的存储位置", 二叉堆则是逻辑结构.

具体在组织上, 知道父节点的位置下标是i, 左右子节点的下标是2i+1和2i+2; 知道了任意子节点下标n, 则父节点下标(n-1)/2----int类型除法

二叉树的三种遍历方法:

public class TreeAndArray {

    /**
     * 二叉堆,默认数组已经堆化,;
     * 1.若i为父节点下标,二子节点:2i+1,2i+2;
     * 2.若n为孩子节点,父节点: (n-1)/2 (int类型除法)
     * @param arr
     * @param index
     */

    /*先序遍历*/
    public static void preOrder(int[] arr, int index){ //其中,数组表示数据的“物理存储位置”,index是目前节点下标
        if(index>=arr.length) //退出条件,index的子节点越界,说明index就是叶子结点
            return;
        System.out.print(arr[index] +" "); //出栈时的输出位置正好体现了遍历的名称
        preOrder(arr,2*index+1);
        preOrder(arr,2*index+2);
    }

    /*中序遍历*/
    public static void inOrder(int[] arr, int index){ 
        if(index>=arr.length)
            return;
        preOrder(arr,2*index+1);
        System.out.println(arr[index]);
        System.out.print(arr[index] +" ");
    }

    /*后序遍历*/
    public static void laterOrder(int[] arr, int index){ 
        if(index>=arr.length)
            return;
        preOrder(arr,2*index+1);
        preOrder(arr,2*index+2);
        System.out.print(arr[index] +" ");
    }

    public static void main(String[] args) {
        int[] arg=new int[8];
        for (int i = 0; i < arg.length; i++) {
            arg[i]=(int)(Math.random()*20);
        }

        preOrder(arg,0);
    }
}

堆排序代码与步骤----大顶堆

堆排序步骤:
 * 1.纵向有序: 数组堆化,变成二叉堆
 * 2.横向有序: 排序,每次都调换根节点和未排序范围内最后一个叶子结点(数组的首和尾),大小顶堆转换,数组变得有序
  恰好分为三个递进的方法:maxHeapFixdown, maxHeapMaker, maxHeaSort

/**
 * 堆排序步骤:
 * 1.纵向有序: 数组堆化,变成二叉堆
 * 2.横向有序: 排序,每次都调换根节点和未排序范围内最后一个叶子结点(数组的首和尾),大小顶堆转换,数组变得有序
 */
public class MaxHeapSort {

    //step0:  maxHeapFixdown,某节点的大/小顶堆调整方法(这里是大顶堆),基础
    public static void maxHeapFixdown(int[] arr, int index, int range){ //range控制树的数量大小
        //找到子树根节点俩子节点
        int left = 2*index+1;
        int right = 2*index+2;

        //1.判断子节点是否越界,结束条件; 2.找到最大子节点;
        int max;
        if (left>=range)
            return;
        if (right>=range)
            max=left;
        else{
            if (arr[left]>arr[right]){
                max=left;
            }else
                max=right;
        }
        //父节点比较是否交换
        if (arr[max]<=arr[index])
            return;
        //若是交换,则递归向下调整交换子节点子树
        int temp=arr[index];
        arr[index]=arr[max];
        arr[max]=temp;
        maxHeapFixdown(arr,max,range);
    }

    /*step1: maxHeapMaker,堆化数组(这里是大顶堆),使得二叉堆成立(纵向有序,横向待定)*/
    public static void maxHeapMaker(int[] arr){

        //从倒数第二层,一层层地向上调整顶堆
        for (int x=arr.length/2-1; x>=0;x--){
            maxHeapFixdown(arr, x,arr.length);
        }
    }

    /*step2: maxHeapSort,反转二叉堆(大顶堆<->小顶堆),使得"物理数组"有序*/
    public static void maxHeapSort(int[] arr){

        //堆化数组
        maxHeapMaker(arr);
        //对换二叉堆根节点与最后未排序的节点(根节点是最大值/最小值,相当于将最大/小值顶到数组末尾)
        for (int i=arr.length-1; i>0; i--){
            util.util.swap(arr,i,0);
            maxHeapFixdown(arr,0,i-1);
        }
        //缩小树范围,并调整新根节点的二叉堆(这样不断调换,不断缩小二叉堆,不断向下调整新二叉堆,最终大小顶堆调换,)
    }

    public static void main(String[] args) {
        int[] arg=new int[8];
        for (int i = 0; i < arg.length; i++) {
            arg[i]=(int)(Math.random()*20);
        }

        maxHeapSort(arg);
        System.out.println(Arrays.toString(arg));
    }
}

二叉堆及堆排序的实际应用:top K个数

问题:求一个给定乱序数组中的前k个数据 / 或者是键盘输入n个数,输入-1结束,输出topk个数(不用排序,要的话Arrays.sort() )

应用:排行榜前K个玩家的查找

解决思路:在这k数里,若是下一个数值大于其中的最小值,那么就替换掉它,重新选出新K个数里最小的那个。(这里没要求排序,要的话topk数组Arrays.sort())

 * 1.堆排序与二叉堆: 创建一个topK的辅助数组,每次都用小顶堆选出最小值,
  只要有新数据大于这个最小值,替换,调整使新的最小值浮上来
 * 2. partition+二分: 类似快速排序那样分区(左大右小), 找到mid==k-1后, 输出前K位

这里恰好可以联想到“小顶堆”的向下调整maxHeapFixdown方法:每次比较处于根节点处的最小值,大于他了,就替换掉,然后向下调整使其最小值浮上来。

具体步骤:

  1. 创建topK数组,将前K个数放进去
  2. 堆化数组
  3. 遍历,然后和根节点最小值做比较
  4. 小于,则替换,maxHeapFixdown调整
  5. 重复3,4步骤
  6. 结束,输出topK数组
/** 求乱序数组中的前top K个最大数 / 或者是键盘输入n个数,输入-1结束,输出topk个数
 * 解题思路:
 *  1.堆排序与二叉堆: 创建一个topK的辅助数组,每次都用小顶堆选出最小值,
 *   只要有新数据大于这个最小值,替换,调整使新的最小值浮上来
 * 2. partition+二分: 分区,找到mid=topk后,若是需要排序,则输出排好序的前K位
 */
public class Heep_topK个数_B站 {
    static int[] topK(int[] arr){
        //top数组
        int[] top= Arrays.copyOfRange(arr,0,10);
        //堆化top数组
        minHeapMaker(top);
        //遍历比较最小值,小就替换掉,并且重新调整小顶堆
        for (int i = 10; i < arr.length; i++) {
            if (arr[i]>top[0])
                top[0]=arr[i];
            minHeapFixdown(top,0);
        }
        return top;
    }

    public static void minHeapMaker(int[] arr){
        //从倒数第二层,一层层地向上调整顶堆
        for (int x=arr.length/2-1; x>=0;x--){
            minHeapFixdown(arr, x);
        }
    }

    /*知道父节点i求子节点:2i+1,2i+2 */
    static void minHeapFixdown(int[] arr,int father){
        int left=father*2+1;
        int right=father*+2;
        int minOfKid;
        //左结点过节
        if (left>arr.length-1)
            return;
        //右节点越界
        minOfKid=left; //找到小的子节点,这是种节约辅助空间的写法,不用temp了
        if (right>arr.length-1)
            minOfKid=left;
        else if (arr[left]>arr[right])
            minOfKid=right;

        //比较,若比最小子节点还小,交换,向下调整替换了的子树
        if (arr[father]>arr[minOfKid]){
            int temp=arr[minOfKid];
            arr[minOfKid]=arr[father];
            arr[father]=temp;
            minHeapFixdown(arr,minOfKid);
        }
        else return;
    }

    public static void main(String[] args) {
        int[] arg ={-1513,2,3,7,5,6,-12,4,3,9,110,-2,-1,-2,-7};
        int[] temp=topK(arg);
        System.out.println(Arrays.toString(temp));

        int[] arg02=new int[20];
        for (int i = 0; i < arg.length; i++) {
            arg02[i]=(int)(Math.random()*50);
        }
        System.out.println(Arrays.toString(topK(arg02)));
    }

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值