求取数组中倒数 第k大的数

求数组中倒数第k大的数有多种求法,如直接使用堆排序按从小到答案,在遍历数组求取第k个数即可时间复杂度为nlog(n),还有一种较为快速的求法是维持一个最小堆,算法有nlog(n)直接将为nlog(k),当k远远小于n时,该算法将会有很大的优势,本文将介绍该种算法的具体实现,

 在具体实现时,需要考虑几个因素,初始堆大小,有我们传入的k的大小决定,默认值我们赋值为 Integer.MAX_VALUE,需要考虑的是,只有该数组被 填满后,我们才能决定当新加入的数小于堆顶元素直接抛弃即可,所以会有一个flimit标志,当limit标志为0时,表明数组中的值全是外来加入,而非原先默认,故算法的具体实现如下:

 

/** 求一个数组中倒数第k大的数
 * Created by zoujianglin
 * 2018/9/6 0006.
 */
public class MinHeap {
    private int[] heap;
    //用于表示数组是否被填满,flag为0时表示已被填满
    private int limit;


    //k表示数组大小。数组第0个保存不用于存储数据
    public MinHeap(int k) {
        if (k <= 0) {
            throw new IllegalArgumentException("k必须大于0");
        }

        heap = new int[k + 1];
        for (int i = 1; i < k + 1; i++) {
            heap[i] = Integer.MAX_VALUE;
        }

        limit = k;

    }

    public static void main(String[] args) {
        int[] array = new int[]{1, 7, 3, 6, 12, 9};
        MinHeap minHeap = new MinHeap(3);
        for (int i = 0; i < array.length; i++) {
            minHeap.addToMinHeap(array[i]);
        }
        System.out.println(minHeap.getHeapFirst());


    }

    //左子树结点2n,右子树结点2n+1
    //加入成功会返回ture,失败则返回false
    public boolean addToMinHeap(int target) {
        //表示已经填满,与顶端元素比较即可
        if (limit <= 0) {
            if (target < heap[1]) { //比顶端还小?不应该加入
                return false;
            } else { //比顶端大,则抛弃顶端,
                heap[1] = target;
                adjustHeapfixdown(1);//向下重新调整堆
                return true;
            }
        } else { //填满的情况 ,相当于插入,寻找适合自已的位置
            insertHeap(target);
            return true;
        }


    }

    /**
     * @param index 调整的索引,也就是从第几个开始调整
     */
    private void adjustHeapfixdown(int index) {
        int position = index << 1; //左移找到子子树
        while (position <= heap.length - 1) { //未超过

            if (position + 1 < heap.length && heap[position + 1] < heap[position]) {//右子树比左子树小
                position = position + 1;
            }
            //父节点,小于字节点,跳过
            if (heap[position] > heap[index]) {
                break;
            }
            swap(position, index);
            index = position;
            position = position << 1;
            //这里最终的位置


        }


    }


    private void swap(int position, int index) {
        int temp = heap[position];
        heap[position] = heap[index];
        heap[index] = temp;
    }

    /**
     * 插入的值,表明插入哪个值,插入前先验证target
     * 只有在未填满的情况下可以插入,使用flag 变量控制
     * 相当于构建二叉树
     *
     * @param target
     */
    private boolean insertHeap(int target) {
        if (limit <= 0) {
            return false;
        }


        //进入这里说明未填满,直接将里面的Integer.maxValue覆盖掉
        //可以使用一个map保存哪些位置未被赋值,以空间换时间
        for (int i = 1; i < heap.length; i++) {
            if (heap[i] == Integer.MAX_VALUE) {
                limit--; //表明插入成功
                heap[i] = target;
                break;
            }


        }
        //这里进行堆调整
        for (int i = (heap.length - 1) / 2; i >= 1; i--) {

            adjustHeapfixdown(i);
        }


        return true;
    }


    public int getHeapFirst() {
        return heap[1];
    }

}


 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值