题目:给定一个无需的整型数组arr,找到其中最小的k个数
要求:时间复杂度为O(nlogk)
解题思路:
1.先建立一个含有k个数的大根堆,这个堆代表"目前选出的"k个最小的数,在堆里的k个元素中堆顶的元素是最小的k个数里最大的那个。
2.接下来遍历整个数,遍历的过程中看当前数是否比堆顶元素小。
3.如果当前遍历到的数比堆顶小,就把堆顶的元素替换成当前的数,然后从堆顶的位置调整整个堆,让替换操作后堆的最大元素继续处在堆顶的位置;
4.如果当前遍历到的数不比堆顶小,则不进行任何操作,继续遍历下一个数。
5.在遍历完成后,堆中的k个数就是所有数组中最小的k个数
代码如下:
public class TopK {
public int[] getMinKNumsByHeap(int[] array, int k){
if(k < 1 || k > array.length){
return array;
}
int[] kHeap = new int[k];
//选出k个数,建立大根堆,后续对这个大根堆进行维护
for(int i = 0; i != k; i++){
heapInsert(kHeap, array[i], i);
}
for(int i = k; i != array.length; i++){
if(array[i] < kHeap[0]){
kHeap[0] = array[i];
heapify(kHeap, 0 , k);
}
}
return kHeap;
}
//建堆
public void heapInsert(int[] array, int value, int index){
array[index] = value;
while(index != 0){
int parent = (index - 1) / 2;
if(array[parent] < array[index]){
//若比父位置大就交换
swap(array, parent, index);
//并来到父位置,再比较,直到不比父位置大,停住。
index = parent;
}else{
break;
}
}
}
//调整堆 index位置的数变小了,往下沉 heapSize表示已经形成的堆的大小
public void heapify(int[] array, int index, int heapSize){
int left = index * 2 + 1;
int right = index * 2 + 2;
int largest = index;//largest记录左右两个孩子哪个比较大
while(left < heapSize){
if(array[left] > array[index]){
largest = left;//左孩子大则largest记录左孩子的位置
}
if(right < heapSize && array[right] > array[largest]){
largest = right;
}
if(largest != index){
swap(array, largest, index);
}else{
//若largest == index则说明我和左右孩子之间的最大值是自己,不用往下沉
break;
}
index = largest;
left = index * 2 + 1;
right = index * 2 + 2;
}
}
public void swap(int[] array, int i, int j){
int temp = array[i];
array[i] = array[j];
array[j] = temp;
}
}