您的位置:首页 > 理论基础 > 数据结构算法

数据结构面试题1.2.9-查找最小的K个元素-使用最大堆

2015-09-23 15:41 429 查看
Java最大堆相关知识:/article/9191429.html

package questions;

import java.util.Arrays;
import java.util.Random;

/**
* @title 查找最小的K个元素-使用最大堆
* @question 输入n个整数,输出其中最小的K个元素,例如,输入1,2,3,4,5,6,7,8那么最小的4个元素就是1,2,3,4
* @analysis
*           堆其实是一颗完全二叉树,堆对于两类问题有着很好的解决方案:a.排序问题:由于堆是一颗完全二叉树,所以采用堆,堆n元数组进行排序,时间复杂度不会超过O
*           (nlgn),而且只需要几个额外的空间。b.优先级队列。通过插入新元素和调整堆结构来维护堆的性质,每个操作所需要的时间都是O(lgn)<br>
*           堆常见实现是采用一个大小为n的数组存储元素,并且0号单元舍弃不用。对堆中的元素按照层次从上到下,从左导游的顺序依次编号。
*           那么对于编号为i的元素:<br>
*           a:如果左孩子存在,那么左孩子的编号为2i<br>
*           b:如果右孩子存在,那么右孩子的编号为2*i+1<br>
*           c:如果有父结点,那么父结点的编号为i/2<br>
*           d:结点为叶结点的条件是左孩子且右孩子都为空,为空结点的条件是i<1或者i>n<br>
*           堆的设计对于处理Top K问题十分方便。首先设置一个大小为K的堆(如果求最大top K,那么用最小堆,如果求最小top
*           K,那么用最大堆),然后扫描数组。并将数组的每个元素与堆的根比较,符合条件的就插入堆中,同时调整堆使之符合堆的特性,扫描完成后,
*           堆中保留的元素就是最终的结果
*           。说到调整堆,不得不提的是调整的算法,分为两类:向下调整(shiftdown)和向上调整(shiftup)。<br>
*           有了堆的基本操作,top K问题就有了一个基础(当然也完全可以完全不用堆解决top K问题)。以最小top
*           K问题为例(此时需要建立大小为K的最大堆),top
*           K的求解过程是:扫描原数组,将数组的前K个元素扔到堆中,调整使之保持堆的特性。对于k之后的元素
*           ,如果比堆顶的元素小,那么替换堆顶元素并调整堆,扫描数组完成后,堆中保存的元素就是最终的结果。
*
*
*
* @link 堆的理解http://www.java3z.com/cwbwebhome/article/article1/1362.html?id=4745
* @author Sam
*
*/
public class Ex1o2o9 {
public static void main(String[] args) {
int[] a = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
18, 19 };
int k = 7;
MinKElement mke = new MinKElement();
mke.disArrange(a);
System.out.println("after disarranging,the array a[]:");
System.out.println(Arrays.toString(a));
mke.findKMin(a, k);
}

}

/**
* 最小的K个元素,使用最大堆,当然使用快速排序也是OK的
*/
class MinKElement {
/**
* rearrange the array, just for test.随机调换数组
*
* @param a
*/
public void disArrange(int[] a) {
for (int i = 0, len = a.length; i < len; i++) {
Random random = new Random();
int j = random.nextInt(len);
swap(a, i, j);
}
}

/**
* 数组a中下标为i,j数据交换
*
* @param a
* @param i
* @param j
*/
private static void swap(int[] a, int i, int j) {
int temp = a[i];
a[i] = a[j];
a[j] = temp;
}

public void findKMin(int[] a, int k) {
// you can do this:int[] heap = new int[k].but that may be space-cost
int[] heap = a;
// create MaxHeap of K elements form the last RootIndex to 0.
// 先建立K的最大堆,K个值,那么起始点为K/2-1
int rootIndex = k / 2 - 1;
while (rootIndex >= 0) {
reheap(heap, rootIndex, k - 1);
rootIndex--;
}
// 建立好K的最大堆之后,循环,将length-k之外的值不断的和最大值进行对比,小于最大值,就纳入堆中,调整堆的最大堆
for (int i = k, len = heap.length; i < len; i++) {
if (heap[i] < heap[0]) {
heap[0] = heap[i];
reheap(heap, 0, k - 1);
}
}
System.out.println(toString(heap, k));
}

// reheap:from root to lastIndex.
public void reheap(int[] heap, int rootIndex, int lastIndex) {
int orphan = heap[rootIndex];
boolean done = false;
int leftIndex = rootIndex * 2 + 1;
while (!done && leftIndex <= lastIndex) {
int largerIndex = leftIndex;
if (leftIndex + 1 <= lastIndex) {
int rightIndex = leftIndex + 1;
if (heap[rightIndex] > heap[leftIndex]) {
largerIndex = rightIndex;
}
}
// Attention! should not use-->heap[root]<heap[largerIndex]<--
// I spend time to find the problem....
if (orphan < heap[largerIndex]) {
heap[rootIndex] = heap[largerIndex];
rootIndex = largerIndex;
leftIndex = rootIndex * 2 + 1;
} else {
done = true;
}
}
heap[rootIndex] = orphan;
}

public static String toString(int[] a, int length) {
if (a == null)
return "null";
int iMax = length - 1;
if (iMax == -1)
return "[]";

StringBuilder b = new StringBuilder();
b.append('[');
for (int i = 0;; i++) {
b.append(a[i]);
if (i == iMax)
return b.append(']').toString();
b.append(", ");
}
}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: