堆的相关以及topK问题的JAVA实现


title: 堆的相关以及topK问题的JAVA实现
date: 2020-03-09 21:03:42
tags:


堆的相关

什么是堆:

  • 完全二叉树
  • 每个节点的值大于等于或者小于等于其左右子节点的值,即大顶堆和小顶堆。

完全二叉树使用数组来存储,不需要使用左右指针,仅通过数组下标就可以访问左右子节点,从0开始计算,下标为i的节点的左节点为2*i+1,右节点为2*i+2

相关操作

以大顶堆为例

将某个数组元素堆化,从这个节点出发,直到节点大于其左右子节点的值:

    /**
     * 堆化
     *
     * @param arr 要堆化的数组
     * @param n   最后堆元素下标
     * @param i   要堆化的元素下标
     */
    private static void heapify(int[] arr, int n, int i) {
        while (true) {
            // 最大值位置
            int maxPos = i;
            // 与左子节点(i * 2 + 1)比较,获取最大值位置
            if (i * 2 + 1 <= n && arr[i] < arr[i * 2 + 1]) {
                maxPos = i * 2 + 1;
            }
            // 最大值与右子节点(i * 2 + 2)比较,获取最大值位置
            if (i * 2 + 2 <= n && arr[maxPos] < arr[i * 2 + 2]) {
                maxPos = i * 2 + 2;
            }
            // 最大值是当前位置结束循环
            if (maxPos == i) {
                break;
            }
            // 与子节点交换位置
            swap(arr, i, maxPos);
            // 以交换后子节点位置接着往下查找
            i = maxPos;
        }
    }

建堆:

 /**
     * 建堆
     *
     * @param arr
     */
    private void buildHeap(int[] arr) {
        // (arr.length - 1) / 2 为最后一个叶子节点的父节点
        // 也就是最后一个非叶子节点,依次堆化直到根节点
        for (int i = (arr.length - 1) / 2; i >= 0; i--) {
            heapify(arr, arr.length - 1, i);
        }
    }

堆排序:

    /**
     * 排序
     * <p>
     * 堆元素是从数组下标0开始
     *
     * @param arr
     */
    public void sort(int[] arr) {
        if (arr.length <= 1) {
            return;
        }

        // 1、建堆
        buildHeap(arr);

        // 2、排序
        int k = arr.length - 1;
        while (k > 0) {
            // 将堆顶元素(最大)与最后一个元素交换位置
            swap(arr, 0, k);
            // 将剩下元素重新堆化,堆顶元素变成最大元素
            heapify(arr, --k, 0);
        }
    }

删除堆顶元素,将最后一个叶子节点,置于堆顶,然后将其堆化:

    /**
     * @param arr 
     * @param n 最后堆元素下标
     */
    public void delTop(int[] arr , int n){
        if(n < 0) return;
        arr[0] = arr[n];
        heapify(arr , --n , 0);
    }

牛客上有道题,求最小的K个数,我们就可以使用堆来实现。

题目描述

输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是>1,2,3,4,。

可以先把数组的前K个数拿出来,构建一个大顶堆,然后从第K+1个数开始循环,如果这个数小于堆顶元素,即把这个数置于堆顶,然后堆化。

一次堆化的时间复杂度为O(logK),遍历数组O(n),假设每个元素都要入堆,则时间复杂度为O(nlongK)

    /**
     * @param k 求top K元素,
     * @param a
     * @return
     */
    public int[] topK(int k , int[] a){
        int count = 0;
        int[] re = new int[k];
        for (int i = 0; i < k; i++) {
            re[i] = a[i];
        }
        //堆化K个元素
        for (int i = (k - 1) / 2; i >= 0; i--) {
            heapify(re, k-1, i);
        }
        
        for (int j = k; j < a.length; j++) {
            if(re[0] > a[j]){
                re[0] = a[j];
                heapify(re , re.length - 1 , 0);
            }
        }
        return re;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值