这个题目的话,单纯看题目的名字就知道利用堆可以去解决。但是先讲一下另一种思路,就是将所有元素记录到自己的位置上,这样元素就会默认的从小到大进行排列,然后就可以找到了。代码如下:
public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
ArrayList<Integer> result = new ArrayList<>();
if(input.length < k || input.length == 0)
return result;
int max = input[0], min = input[0];
for(int num : input) {
if(num > max)
max = num;
if(num < min)
min = num;
}
int[] flags = new int[max - min+1]; //找到最大最小值,申请空间存放每个元素相对位置
for(int num : input) {
flags[num-min]++;
}
for(int i = 0; i < flags.length; ++i) {
if(flags[i] > 0) {
if (result.size() == k) //遍历前K个
return result;
result.add(min+i);
}
}
return result;
}
当然这种解法空间复杂度比较高,一些特殊的数组例如最大最小值相差很大的可能造成很多空间的浪费,而且时间复杂度上也并没有多好,好几个O(n)。只是想提供一种这样的思路,即数组下标可以作为元素的索引。
当然,还是老老实实用堆做一遍比较好
public class Solution {
public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
ArrayList<Integer> result = new ArrayList<>();
if(input.length < k || input.length == 0 || k==0)
return result;
int[] heap = new int[k];
for(int i = 0; i < k; ++i)
heap[i] = input[i];
for(int i = 0; i < k/2; ++i) //对每个有叶子的进行调整
siftDown(heap, i, k/2);
for(int i = k; i < input.length; ++i) { //只需要调整堆顶就可以了
if (input[i] < heap[0]) {
heap[0] = input[i];
siftDown(heap, 0, k/2);
}
}
for (int hea : heap)
result.add(hea);
return result;
}
//对于一个堆调整,index为当前节点下标,length为截至长度
private void siftDown(int[] heap, int index, int length) {
while(index < length) {
int child = index*2 + 1;
if(index*2+2 < heap.length && heap[index*2+2] > heap[child]) //找子树中较大的,将它上调,因为是个大顶堆
child++;
if(heap[index] < heap[child]) {
swap(heap, index, child);
index = child;
} else {
return; //如果已经符合大顶堆了,则不用调整了,直接返回
}
}
}
private void swap(int[] heap, int index1, int index2) {
int temp = heap[index1];
heap[index1] = heap[index2];
heap[index2] = temp;
}
}