1,問題描述
輸入n個整數,找出其中最小的K個數。例如輸入4,5,1,6,2,7,3,8這8個數字,則最小的4個數字是1,2,3,4,。
2,解題思路
(1)思路一:直接利用各種排序算法(快排、冒泡排序、選擇排序、堆排序)對數組進行從小到大的排序。然後輸出前K個。
這種方法容易想到,但不是最優的算法。
(2)思路二:將原數組的前K個元素構建成一個最大堆,然後從第K+1個元素開始,依次和堆頂元素比較,如果小於堆頂元素,則刪除堆頂元素,將該元素放入堆中,直到遍歷完整個數組,堆中元素就是最小的K個數。
注:思路二有兩種實現方式:
(1)利用java的PriorityQueue實現
(2)直接手撕堆排序底層代碼
3,源碼
(1)利用java的PriorityQueue實現
import java.util.ArrayList;
import java.util.Comparator;
import java.util.PriorityQueue;
public class Solution {
public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
ArrayList<Integer> list = new ArrayList<Integer>();
if(k > input.length || k==0) return list;
//PriorityQueue默認實現的最小堆,要想實現最大堆,需要通過比較器實現
PriorityQueue<Integer> heap = new PriorityQueue<Integer>(new Comparator<Integer>() {
@Override
public int compare(Integer arg0, Integer arg1) {
return arg1-arg0;
}
});
for(int i=0;i<input.length;i++) {
if(heap.size()<k) {
heap.offer(input[i]);
}else {
if(input[i] < heap.peek()) {
heap.poll();
heap.offer(input[i]);
}
}
}
for(int x : heap) {
list.add(x);
}
return list;
}
}
(2)手撕堆排序底層代碼
import java.util.ArrayList;
public class Solution {
public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
ArrayList<Integer> heap = new ArrayList<Integer>();
if(k>input.length||k==0) return heap;
heap.add(null);//堆元素下標從1開始,習慣問題而已,
//此時除根節點外的任意節點的索引位置關係爲:
//父節點位置:i/2; 左孩子:i*2; 右孩子:i*2+1;
//最後一個分支節點位置爲:n/2;
for(int i=0;i<k;i++) {
heap.add(input[i]);
//初始化方法1:插入法,插入新元素的時候就執行上浮操作。
heapUp(heap,heap.size()-1);
}
//初始化方法二:下沉法,只下沉前heap.size()/2個元素(非葉子節點)就行,但是必須最後一個非葉子節點開始
// for(int i = heap.size()/2; i>0; i--) {
// heapDown1(heap,i);
// //heapDown2(heap,i);
// }
for(int i=k;i<input.length;i++) {
if(input[i] < heap.get(1)) {
heap.set(1, input[i]);
heapDown2(heap,1);
}
}
heap.remove(0);//移除頭結點,因爲頭結點設置爲null了
return heap;
}
//非遞歸下沉
public void heapDown2(ArrayList<Integer> heap, int index) {
int child=-1;
int n = heap.size()-1;
while(2*index <= n) {
//如果只有左孩子節點
if(2*index==n) {
child = 2*index;
}//如果兩個孩子都有
else if(2*index+1 < n) {
if(heap.get(2*index) < heap.get(2*index+1)) {
child = 2*index+1;
}else {
child = 2*index;
}
}
if(heap.get(index) < heap.get(child)) {
swap(heap,index,child);
index = child;
}else break;
}
}
//遞歸下沉
public void heapDown1(ArrayList<Integer> heap, int index) {
int n = heap.size()-1;
int child = -1;//記錄孩子節點
//如果該節點沒有左右孩子,就不再下沉
if(index*2 > n) {
return;
}//只有左孩子
else if(index*2==n){
child = index*2;
}//左右孩子都有
else if(index*2+1 < n) {
if(heap.get(index*2) < heap.get(index*2+1)) {
child = index*2+1;
}else {
child = index*2;
}
}
if(heap.get(index) < heap.get(child)) {
swap(heap,index,child);
heapDown1(heap,child);
}
}
public void heapUp(ArrayList<Integer> heap, int index) {
//index=1的時,表示根節點
if(index>1) {
int parent = index/2;
int parentValue = heap.get(parent);
int indexValue = heap.get(index);
if(parentValue < indexValue) {
swap(heap,parent,index);
heapUp(heap,parent);
}
}
}
private void swap(ArrayList<Integer> heap, int i, int j) {
int temp = heap.get(i);
heap.set(i, heap.get(j));
heap.set(j,temp);
}
}