求数组中倒数第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]; } }