劍指offer上的一道題:輸入n個整數,找出最小的k個數。例如輸入4、5、1、6、2、7、3、8共8個數,最小的4個數爲:1、2、3、4。
分析:
解法一:
可以把它看車數組排序問題,先對數組排序,再取數組前k個數。各個排序算法中,快排是性價比比較高的了,時間事件複雜度爲O(n*logn)。還有沒有其他解法呢?
解法二:
快排思想派上用場了。快排算法中,我們通常要找一個參考元素,針對這個元素把數組分爲倆個子數組。元素左邊的子數組小於該元素,元素右邊的子數組小於該元素。對了!只要找到一個元素使得他左邊的子數組個數爲k就可以了。
這種解法最壞的時間複雜度也才O(n*logn),是不是比第一種解法好多了!
解法三:
怎麼能忘了堆排呢,構建一個k個元素的小頂堆,不停的往裏面插入元素。最後堆裏的元素就是我們要求的K個最小元素。這種解法的好處是沒有移動數組中的元素。但卻開闢了額外的空間。
基於解法三的Java解法:
構建K個堆的過程太過複雜了,Java裏提供了很多集合,我們可以用起來撒!
下面的代碼就是我利用TreeSet構建的一個小頂堆,爲什麼要用TreeSet?嘿嘿,不深究了,不同的同學可以看看紅黑樹。TreeSet默認的排序是一個由大到小的二叉樹,怎麼辦呢?重寫Comparable接口。
1)自定義一個類MyInteger,將int封裝進去;
2)自定義MyInteger實現Comparable接口,大小關係顛倒;
3)將數組插入TreeSet,讀取前K個數組元素即可;
import java.util.Comparator;
import java.util.Iterator;
import java.util.TreeSet;
public class KMinNumber
{
static class MyInteger implements Comparable<MyInteger>
{
Integer i;
public MyInteger(int i)
{
this.i=i;
}
@Override
public int compareTo(MyInteger o)
{
// TODO Auto-generated method stub
if (i>o.getI())
{
return 1;
}
else if (i==o.getI())
{
return 0;
}
else
return -1;
}
@Override
public String toString()
{
// TODO Auto-generated method stub
return i.toString();
}
public Integer getI()
{
return i;
}
}
public static void main(String[] args)
{
int[] a=new int[20];
for (int i = 0; i < a.length; i++)
{
a[i]=(int)(Math.random()*100);
System.out.print(a[i]+" ");
}
findKMin(a,5);
}
public static void findKMin(int[] a, int k)
{
TreeSet<MyInteger> treeSet=new TreeSet<>();
for (int i = 0; i < a.length; i++)
{
MyInteger integer=new MyInteger(a[i]);
treeSet.add(integer);
}
System.out.println();
int i=0;
Iterator<MyInteger> iterator=treeSet.iterator();
while(i<k&&iterator.hasNext())
{
i++;
System.out.print(" "+iterator.next().toString());
}
}
}